Cutelyst  3.7.0
langselect.cpp
1 /*
2  * SPDX-FileCopyrightText: (C) 2018-2022 Matthias Fehring <mf@huessenbergnetz.de>
3  * SPDX-License-Identifier: BSD-3-Clause
4  */
5 
6 #include "langselect_p.h"
7 
8 #include <Cutelyst/Application>
9 #include <Cutelyst/Context>
10 #include <Cutelyst/Plugins/Session/Session>
11 #include <Cutelyst/Response>
12 
13 #include <QDir>
14 #include <QFileInfo>
15 #include <QLoggingCategory>
16 #include <QUrl>
17 #include <QNetworkCookie>
18 #include <QUrlQuery>
19 
20 #include <map>
21 #include <utility>
22 
23 Q_LOGGING_CATEGORY(C_LANGSELECT, "cutelyst.plugin.langselect", QtWarningMsg)
24 
25 using namespace Cutelyst;
26 
27 static thread_local LangSelect *lsp = nullptr;
28 
29 #define SELECTION_TRIED QStringLiteral("_c_langselect_tried")
30 
32  , d_ptr(new LangSelectPrivate)
33 {
34  Q_D(LangSelect);
35  d->source = source;
36  d->autoDetect = true;
37 }
38 
40  , d_ptr(new LangSelectPrivate)
41 {
42  Q_D(LangSelect);
43  d->source = AcceptHeader;
44  d->autoDetect = false;
45 }
46 
47 
49 {
50  delete d_ptr;
51 }
52 
54 {
55  Q_D(LangSelect);
56  if (d->fallbackLocale.language() == QLocale::C) {
57  qCCritical(C_LANGSELECT, "We need a valid fallback locale.");
58  return false;
59  }
60  if (d->autoDetect) {
61  if (d->source < Fallback) {
62  if (d->source == URLQuery && d->queryKey.isEmpty()) {
63  qCCritical(C_LANGSELECT, "Can not use url query as source with empty key name.");
64  return false;
65  } else if (d->source == Session && d->sessionKey.isEmpty()) {
66  qCCritical(C_LANGSELECT, "Can not use session as source with empty key name.");
67  return false;
68  } else if (d->source == Cookie && d->cookieName.isEmpty()) {
69  qCCritical(C_LANGSELECT, "Can not use cookie as source with empty cookie name.");
70  return false;
71  }
72  } else {
73  qCCritical(C_LANGSELECT, "Invalid source.");
74  return false;
75  }
76  connect(app, &Application::beforePrepareAction, this, [d](Context *c, bool *skipMethod) {
77  d->beforePrepareAction(c, skipMethod);
78  });
79  }
80  if (!d->locales.contains(d->fallbackLocale)) {
81  d->locales.append(d->fallbackLocale);
82  }
83  connect(app, &Application::postForked, this, &LangSelectPrivate::_q_postFork);
84 
85  qCDebug(C_LANGSELECT) << "Initialized LangSelect plugin with the following settings:";
86  qCDebug(C_LANGSELECT) << "Supported locales:" << d->locales;
87  qCDebug(C_LANGSELECT) << "Fallback locale:" << d->fallbackLocale;
88  qCDebug(C_LANGSELECT) << "Auto detection source:" << d->source;
89  qCDebug(C_LANGSELECT) << "Detect from header:" << d->detectFromHeader;
90 
91  return true;
92 }
93 
95 {
96  Q_D(LangSelect);
97  d->locales.clear();
98  d->locales.reserve(locales.size());
99  for (const QLocale &l : locales) {
100  if (Q_LIKELY(l.language() != QLocale::C)) {
101  d->locales.push_back(l);
102  } else {
103  qCWarning(C_LANGSELECT) << "Can not add invalid locale" << l << "to the list of suppored locales.";
104  }
105  }
106 }
107 
109 {
110  Q_D(LangSelect);
111  d->locales.clear();
112  d->locales.reserve(locales.size());
113  for (const QString &l : locales) {
114  QLocale locale(l);
115  if (Q_LIKELY(locale.language() != QLocale::C)) {
116  d->locales.push_back(locale);
117  } else {
118  qCWarning(C_LANGSELECT, "Can not add invalid locale \"%s\" to the list of supported locales.", qUtf8Printable(l));
119  }
120  }
121 }
122 
124 {
125  if (Q_LIKELY(locale.language() != QLocale::C)) {
126  Q_D(LangSelect);
127  d->locales.push_back(locale);
128  } else {
129  qCWarning(C_LANGSELECT) << "Can not add invalid locale" << locale << "to the list of supported locales.";
130  }
131 }
132 
134 {
135  QLocale l(locale);
136  if (Q_LIKELY(l.language() != QLocale::C)) {
137  Q_D(LangSelect);
138  d->locales.push_back(l);
139  } else {
140  qCWarning(C_LANGSELECT, "Can not add invalid locale \"%s\" to the list of supported locales.", qUtf8Printable(locale));
141  }
142 }
143 
144 void LangSelect::setLocalesFromDir(const QString &path, const QString &name, const QString &prefix, const QString &suffix)
145 {
146  Q_D(LangSelect);
147  d->locales.clear();
148  if (Q_LIKELY(!path.isEmpty() && !name.isEmpty())) {
149  const QDir dir(path);
150  if (Q_LIKELY(dir.exists())) {
151  const auto _pref = prefix.isEmpty() ? QStringLiteral(".") : prefix;
152  const auto _suff = suffix.isEmpty() ? QStringLiteral(".qm") : suffix;
153  const QString filter = name + _pref + u'*' + _suff;
154  const auto files = dir.entryInfoList({name}, QDir::Files);
155  if (Q_LIKELY(!files.empty())) {
156  d->locales.reserve(files.size());
157  bool shrinkToFit = false;
158  for (const QFileInfo &fi : files) {
159  const auto fn = fi.fileName();
160  const auto prefIdx = fn.indexOf(_pref);
161  const auto locPart = fn.mid(prefIdx + _pref.length(), fn.length() - prefIdx - _suff.length() - _pref.length());
162  QLocale l(locPart);
163  if (Q_LIKELY(l.language() != QLocale::C)) {
164  d->locales.push_back(l);
165  qCDebug(C_LANGSELECT, "Added locale \"%s\" to the list of supported locales.", qUtf8Printable(locPart));
166  } else {
167  shrinkToFit = true;
168  qCWarning(C_LANGSELECT, "Can not add invalid locale \"%s\" to the list of supported locales.", qUtf8Printable(locPart));
169  }
170  }
171  if (shrinkToFit) {
172  d->locales.squeeze();
173  }
174  } else {
175  qCWarning(C_LANGSELECT, "Can not find translation files for \"%s\" in \"%s\".", qUtf8Printable(filter), qUtf8Printable(path));
176  }
177  } else {
178  qCWarning(C_LANGSELECT, "Can not set locales from not existing directory \"%s\".", qUtf8Printable(path));
179  }
180  } else {
181  qCWarning(C_LANGSELECT, "Can not set locales from dir with emtpy path or name.");
182  }
183 }
184 
185 void LangSelect::setLocalesFromDirs(const QString &path, const QString &name)
186 {
187  Q_D(LangSelect);
188  d->locales.clear();
189  if (Q_LIKELY(!path.isEmpty() && !name.isEmpty())) {
190  const QDir dir(path);
191  if (Q_LIKELY(dir.exists())) {
192  const auto dirs = dir.entryList(QDir::AllDirs);
193  if (Q_LIKELY(!dirs.empty())) {
194  d->locales.reserve(dirs.size());
195  bool shrinkToFit = false;
196  for (const QString &subDir : dirs) {
197  const QString relFn = subDir + u'/' + name;
198  if (dir.exists(relFn)) {
199  QLocale l(subDir);
200  if (Q_LIKELY(l.language() != QLocale::C)) {
201  d->locales.push_back(l);
202  qCDebug(C_LANGSELECT, "Added locale \"%s\" to the list of supported locales.", qUtf8Printable(subDir));
203  } else {
204  shrinkToFit = true;
205  qCWarning(C_LANGSELECT, "Can not add invalid locale \"%s\" to the list of supported locales.", qUtf8Printable(subDir));
206  }
207  } else {
208  shrinkToFit = true;
209  }
210  }
211  if (shrinkToFit) {
212  d->locales.squeeze();
213  }
214  }
215  } else {
216  qCWarning(C_LANGSELECT, "Can not set locales from not existing directory \"%s\".", qUtf8Printable(path));
217  }
218  } else {
219  qCWarning(C_LANGSELECT, "Can not set locales from dirs with empty path or names.");
220  }
221 }
222 
224 {
225  Q_D(const LangSelect);
226  return d->locales;
227 }
228 
230 {
231  Q_D(LangSelect);
232  d->queryKey = key;
233 }
234 
236 {
237  Q_D(LangSelect);
238  d->sessionKey = key;
239 }
240 
242 {
243  Q_D(LangSelect);
244  d->cookieName = name;
245 }
246 
248 {
249  Q_D(LangSelect);
250  d->subDomainMap.clear();
251  d->locales.clear();
252  d->locales.reserve(map.size());
253  auto i = map.constBegin();
254  while (i != map.constEnd()) {
255  if (i.value().language() != QLocale::C) {
256  d->subDomainMap.insert(i.key(), i.value());
257  d->locales.append(i.value());
258  } else {
259  qCWarning(C_LANGSELECT) << "Can not add invalid locale" << i.value() << "for subdomain" << i.key() << "to the subdomain map.";
260  }
261  ++i;
262  }
263  d->locales.squeeze();
264 }
265 
267 {
268  Q_D(LangSelect);
269  d->domainMap.clear();
270  d->locales.clear();
271  d->locales.reserve(map.size());
272  auto i = map.constBegin();
273  while (i != map.constEnd()) {
274  if (Q_LIKELY(i.value().language() != QLocale::C)) {
275  d->domainMap.insert(i.key(), i.value());
276  d->locales.append(i.value());
277  } else {
278  qCWarning(C_LANGSELECT) << "Can not add invalid locale" << i.value() << "for domain" << i.key() << "to the domain map.";
279  }
280  ++i;
281  }
282  d->locales.squeeze();
283 }
284 
286 {
287  Q_D(LangSelect);
288  d->fallbackLocale = fallback;
289 }
290 
292 {
293  Q_D(LangSelect);
294  d->detectFromHeader = enabled;
295 }
296 
298 {
299  Q_D(LangSelect);
300  if (Q_LIKELY(!key.isEmpty())) {
301  d->langStashKey = key;
302  } else {
303  qCWarning(C_LANGSELECT) << "Can not set an empty key name for the language code stash key. Using current key name" << d->langStashKey;
304  }
305 }
306 
308 {
309  Q_D(LangSelect);
310  if (Q_LIKELY(!key.isEmpty())) {
311  d->dirStashKey = key;
312  } else {
313  qCWarning(C_LANGSELECT) << "Can not set an empty key name for the language direction stash key. Using current key name" << d->dirStashKey;
314  }
315 }
316 
318 {
319  if (!lsp) {
320  qCCritical(C_LANGSELECT) << "LangSelect plugin not registered";
321  return QVector<QLocale>();
322  }
323 
324  return lsp->supportedLocales();
325 }
326 
328 {
329  if (!lsp) {
330  qCCritical(C_LANGSELECT) << "LangSelect plugin not registered";
331  return true;
332  }
333 
334  const auto d = lsp->d_ptr;
335  const auto _key = !key.isEmpty() ? key : d->queryKey;
336  if (!d->getFromQuery(c, _key)) {
337  if (!d->getFromHeader(c)) {
338  d->setFallback(c);
339  }
340  d->setToQuery(c, _key);
341  c->detach();
342  return false;
343  }
344  d->setContentLanguage(c);
345 
346  return true;
347 }
348 
350 {
351  bool foundInSession = false;
352 
353  if (!lsp) {
354  qCCritical(C_LANGSELECT) << "LangSelect plugin not registered";
355  return foundInSession;
356  }
357 
358  const auto d = lsp->d_ptr;
359  const auto _key = !key.isEmpty() ? key : d->sessionKey;
360  foundInSession = d->getFromSession(c, _key);
361  if (!foundInSession) {
362  if (!d->getFromHeader(c)) {
363  d->setFallback(c);
364  }
365  d->setToSession(c, _key);
366  }
367  d->setContentLanguage(c);
368 
369  return foundInSession;
370 }
371 
373 {
374  bool foundInCookie = false;
375 
376  if (!lsp) {
377  qCCritical(C_LANGSELECT) << "LangSelect plugin not registered";
378  return foundInCookie;
379  }
380 
381  const auto d = lsp->d_ptr;
382  const auto _name = !name.isEmpty() ? name : d->cookieName;
383  foundInCookie = d->getFromCookie(c, _name);
384  if (!foundInCookie) {
385  if (!d->getFromHeader(c)) {
386  d->setFallback(c);
387  }
388  d->setToCookie(c, _name);
389  }
390  d->setContentLanguage(c);
391 
392  return foundInCookie;
393 }
394 
396 {
397  bool foundInSubDomain = false;
398 
399  if (!lsp) {
400  qCCritical(C_LANGSELECT) << "LangSelect plugin not registered";
401  return foundInSubDomain;
402  }
403 
404  const auto d = lsp->d_ptr;
405  const auto _map = !subDomainMap.empty() ? subDomainMap : d->subDomainMap;
406  foundInSubDomain = d->getFromSubdomain(c, _map);
407  if (!foundInSubDomain) {
408  if (!d->getFromHeader(c)) {
409  d->setFallback(c);
410  }
411  }
412 
413  d->setContentLanguage(c);
414 
415  return foundInSubDomain;
416 }
417 
419 {
420  bool foundInDomain = false;
421 
422  if (!lsp) {
423  qCCritical(C_LANGSELECT) << "LangSelect plugin not registered";
424  return foundInDomain;
425  }
426 
427  const auto d = lsp->d_ptr;
428  const auto _map = !domainMap.empty() ? domainMap : d->domainMap;
429  foundInDomain = d->getFromDomain(c, _map);
430  if (!foundInDomain) {
431  if (!d->getFromHeader(c)) {
432  d->setFallback(c);
433  }
434  }
435 
436  d->setContentLanguage(c);
437 
438  return foundInDomain;
439 }
440 
441 bool LangSelect::fromPath(Context *c, const QString &locale)
442 {
443  if (!lsp) {
444  qCCritical(C_LANGSELECT) << "LangSelect plugin not registered";
445  return true;
446  }
447 
448  const auto d = lsp->d_ptr;
449  const QLocale l(locale);
450  if (l.language() != QLocale::C && d->locales.contains(l)) {
451  qCDebug(C_LANGSELECT) << "Found valid locale" << l << "in path";
452  c->setLocale(l);
453  d->setContentLanguage(c);
454  return true;
455  } else {
456  if (!d->getFromHeader(c)) {
457  d->setFallback(c);
458  }
459  auto uri = c->req()->uri();
460  auto pathParts = uri.path().split(u'/');
461  const auto localeIdx = pathParts.indexOf(locale);
462  pathParts[localeIdx] = c->locale().bcp47Name().toLower();
463  uri.setPath(pathParts.join(u'/'));
464  qCDebug(C_LANGSELECT) << "Storing selected locale by redirecting to" << uri;
465  c->res()->redirect(uri, 307);
466  c->detach();
467  return false;
468  }
469 }
470 
471 bool LangSelectPrivate::detectLocale(Context *c, LangSelect::Source _source, bool *skipMethod) const
472 {
473  bool redirect = false;
474 
476 
477  if (_source == LangSelect::Session) {
478  if (getFromSession(c, sessionKey)) {
479  foundIn = _source;
480  }
481  } else if (_source == LangSelect::Cookie) {
482  if (getFromCookie(c, cookieName)) {
483  foundIn = _source;
484  }
485  } else if (_source == LangSelect::URLQuery) {
486  if (getFromQuery(c, queryKey)) {
487  foundIn = _source;
488  }
489  } else if (_source == LangSelect::SubDomain) {
490  if (getFromSubdomain(c, subDomainMap)) {
491  foundIn = _source;
492  }
493  } else if (_source == LangSelect::Domain) {
494  if (getFromDomain(c, domainMap)) {
495  foundIn = _source;
496  }
497  }
498 
499  // could not find supported locale in specified source
500  // falling back to Accept-Language header
501  if (foundIn == LangSelect::Fallback && getFromHeader(c)) {
502  foundIn = LangSelect::AcceptHeader;
503  }
504 
505 
506  if (foundIn == LangSelect::Fallback) {
507  setFallback(c);
508  }
509 
510  if (foundIn != _source) {
511  if (_source == LangSelect::Session) {
512  setToSession(c, sessionKey);
513  } else if (_source == LangSelect::Cookie) {
514  setToCookie(c, cookieName);
515  } else if (_source == LangSelect::URLQuery) {
516  setToQuery(c, queryKey);
517  redirect = true;
518  if (skipMethod) {
519  *skipMethod = true;
520  }
521  }
522  }
523 
524  if (!redirect) {
525  setContentLanguage(c);
526  }
527 
528  return redirect;
529 }
530 
531 bool LangSelectPrivate::getFromQuery(Context *c, const QString &key) const
532 {
533  const QLocale l(c->req()->queryParam(key));
534  if (l.language() != QLocale::C && locales.contains(l)) {
535  qCDebug(C_LANGSELECT) << "Found valid locale" << l << "in url query key" << key;
536  c->setLocale(l);
537  return true;
538  } else {
539  qCDebug(C_LANGSELECT) << "Can not find supported locale in url query key" << key;
540  return false;
541  }
542 }
543 
544 bool LangSelectPrivate::getFromCookie(Context *c, const QString &cookie) const
545 {
546  const QLocale l(c->req()->cookie(cookie));
547  if (l.language() != QLocale::C && locales.contains(l)) {
548  qCDebug(C_LANGSELECT) << "Found valid locale" << l << "in cookie name" << cookie;
549  c->setLocale(l);
550  return true;
551  } else {
552  qCDebug(C_LANGSELECT) << "Can no find supported locale in cookie value with name" << cookie;
553  return false;
554  }
555 }
556 
557 bool LangSelectPrivate::getFromSession(Context *c, const QString &key) const
558 {
559  const QLocale l = Cutelyst::Session::value(c, key).toLocale();
560  if (l.language() != QLocale::C && locales.contains(l)) {
561  qCDebug(C_LANGSELECT) << "Found valid locale" << l << "in session key" << key;
562  c->setLocale(l);
563  return true;
564  } else {
565  qCDebug(C_LANGSELECT) << "Can not find supported locale in session value with key" << key;
566  return false;
567  }
568 }
569 
570 bool LangSelectPrivate::getFromSubdomain(Context *c, const QMap<QString, QLocale> &map) const
571 {
572  const auto domain = c->req()->uri().host();
573  auto i = map.constBegin();
574  while (i != map.constEnd()) {
575  if (domain.startsWith(i.key())) {
576  qCDebug(C_LANGSELECT) << "Found valid locale" << i.value() << "in subdomain map for domain" << domain;
577  c->setLocale(i.value());
578  return true;
579  }
580  ++i;
581  }
582 
583  const auto domainParts = domain.split(u'.', Qt::SkipEmptyParts);
584  if (domainParts.size() > 2) {
585  const QLocale l(domainParts.at(0));
586  if (l.language() != QLocale::C && locales.contains(l)) {
587  qCDebug(C_LANGSELECT) << "Found supported locale" << l << "in subdomain of domain" << domain;
588  c->setLocale(l);
589  return true;
590  }
591  }
592  qCDebug(C_LANGSELECT) << "Can not find supported locale for subdomain" << domain;
593  return false;
594 }
595 
596 bool LangSelectPrivate::getFromDomain(Context *c, const QMap<QString, QLocale> &map) const
597 {
598  const auto domain = c->req()->uri().host();
599  auto i = map.constBegin();
600  while (i != map.constEnd()) {
601  if (domain.endsWith(i.key())) {
602  qCDebug(C_LANGSELECT) << "Found valid locale" << i.value() << "in domain map for domain" << domain;
603  c->setLocale(i.value());
604  return true;
605  }
606  ++i;
607  }
608 
609  const auto domainParts = domain.split(u'.', Qt::SkipEmptyParts);
610  if (domainParts.size() > 1) {
611  const QLocale l(domainParts.at(domainParts.size() - 1));
612  if (l.language() != QLocale::C && locales.contains(l)) {
613  qCDebug(C_LANGSELECT) << "Found supported locale" << l << "in domain" << domain;
614  c->setLocale(l);
615  return true;
616  }
617  }
618  qCDebug(C_LANGSELECT) << "Can not find supported locale for domain" << domain;
619  return false;
620 }
621 
622 bool LangSelectPrivate::getFromHeader(Context *c, const QString &name) const
623 {
624  if (detectFromHeader) {
625  const auto accpetedLangs = c->req()->header(name).split(u',', Qt::SkipEmptyParts);
626  if (Q_LIKELY(!accpetedLangs.empty())) {
627  std::map<float,QLocale> langMap;
628  for (const QString &al : accpetedLangs) {
629  const auto idx = al.indexOf(u';');
630  float priority = 1.0f;
631  QString langPart;
632  bool ok = true;
633  if (idx > -1) {
634  langPart = al.left(idx);
635  const auto ref = QStringView(al).mid(idx + 1);
636  priority = ref.mid(ref.indexOf(u'=') + 1).toFloat(&ok);
637  } else {
638  langPart = al;
639  }
640  QLocale locale(langPart);
641  if (ok && locale.language() != QLocale::C) {
642  const auto search = langMap.find(priority);
643  if (search == langMap.cend()) {
644  langMap.insert({priority, locale});
645  }
646  }
647  }
648  if (!langMap.empty()) {
649  auto i = langMap.crbegin();
650  while (i != langMap.crend()) {
651  if (locales.contains(i->second)) {
652  c->setLocale(i->second);
653  qCDebug(C_LANGSELECT) << "Selected locale" << c->locale() << "from" << name << "header";
654  return true;
655  }
656  ++i;
657  }
658  // if there is no exact match, lets try to find a locale
659  // where at least the language matches
660  i = langMap.crbegin();
661  const auto constLocales = locales;
662  while (i != langMap.crend()) {
663  for (const QLocale &l : constLocales) {
664  if (l.language() == i->second.language()) {
665  c->setLocale(l);
666  qCDebug(C_LANGSELECT) << "Selected locale" << c->locale() << "from" << name << "header";
667  return true;
668  }
669  }
670  ++i;
671  }
672  }
673  }
674  }
675 
676  return false;
677 }
678 
679 void LangSelectPrivate::setToQuery(Context *c, const QString &key) const
680 {
681  auto uri = c->req()->uri();
682  QUrlQuery query(uri);
683  if (query.hasQueryItem(key)) {
684  query.removeQueryItem(key);
685  }
686  query.addQueryItem(key, c->locale().bcp47Name().toLower());
687  uri.setQuery(query);
688  qCDebug(C_LANGSELECT) << "Storing selected locale in URL query by redirecting to" << uri;
689  c->res()->redirect(uri, 307);
690 }
691 
692 void LangSelectPrivate::setToCookie(Context *c, const QString &name) const
693 {
694  qCDebug(C_LANGSELECT) << "Storing selected locale in cookie with name" << name;
695  c->res()->setCookie(QNetworkCookie(name.toLatin1(), c->locale().bcp47Name().toLatin1()));
696 }
697 
698 void LangSelectPrivate::setToSession(Context *c, const QString &key) const
699 {
700  qCDebug(C_LANGSELECT) << "Storing selected locale in session key" << key;
701  Session::setValue(c, key, c->locale());
702 }
703 
704 void LangSelectPrivate::setFallback(Context *c) const
705 {
706  qCDebug(C_LANGSELECT) << "Can not find fitting locale, using fallback locale" << fallbackLocale;
707  c->setLocale(fallbackLocale);
708 }
709 
710 void LangSelectPrivate::setContentLanguage(Context *c) const
711 {
712  if (addContentLanguageHeader) {
713  c->res()->setHeader(QStringLiteral("Content-Language"), c->locale().bcp47Name());
714  }
715  c->stash({
716  {langStashKey, c->locale().bcp47Name()},
717  {dirStashKey, (c->locale().textDirection() == Qt::LeftToRight ? QStringLiteral("ltr") : QStringLiteral("rtl"))}
718  });
719 }
720 
721 void LangSelectPrivate::beforePrepareAction(Context *c, bool *skipMethod) const
722 {
723  if (*skipMethod) {
724  return;
725  }
726 
727  if (!c->stash(SELECTION_TRIED).isNull()) {
728  return;
729  }
730 
731  detectLocale(c, source, skipMethod);
732 
733  c->setStash(SELECTION_TRIED, true);
734 }
735 
736 void LangSelectPrivate::_q_postFork(Application *app)
737 {
738  lsp = app->plugin<LangSelect *>();
739 }
740 
741 #include "moc_langselect.cpp"
The Cutelyst Application.
Definition: application.h:43
void beforePrepareAction(Cutelyst::Context *c, bool *skipMethod)
T plugin()
Returns the registered plugin that casts to the template type T.
Definition: application.h:107
void postForked(Cutelyst::Application *app)
The Cutelyst Context.
Definition: context.h:39
void stash(const QVariantHash &unite)
Definition: context.cpp:546
void detach(Action *action=nullptr)
Definition: context.cpp:339
QLocale locale() const noexcept
Definition: context.cpp:453
Response * res() const noexcept
Definition: context.cpp:103
void setStash(const QString &key, const QVariant &value)
Definition: context.cpp:218
void setLocale(const QLocale &locale)
Definition: context.cpp:459
Language selection plugin.
Definition: langselect.h:300
void setLocalesFromDir(const QString &path, const QString &name, const QString &prefix=QStringLiteral("."), const QString &suffix=QStringLiteral(".qm"))
Definition: langselect.cpp:144
void setDetectFromHeader(bool enabled)
Definition: langselect.cpp:291
void setLanguageDirStashKey(const QString &key=QStringLiteral("c_langselect_dir"))
Definition: langselect.cpp:307
static bool fromPath(Context *c, const QString &locale)
Definition: langselect.cpp:441
static QVector< QLocale > getSupportedLocales()
Definition: langselect.cpp:317
void setFallbackLocale(const QLocale &fallback)
Definition: langselect.cpp:285
void setQueryKey(const QString &key)
Definition: langselect.cpp:229
void setSubDomainMap(const QMap< QString, QLocale > &map)
Definition: langselect.cpp:247
static bool fromCookie(Context *c, const QString &name=QString())
Definition: langselect.cpp:372
static bool fromDomain(Context *c, const QMap< QString, QLocale > &domainMap=QMap< QString, QLocale >())
Definition: langselect.cpp:418
void setDomainMap(const QMap< QString, QLocale > &map)
Definition: langselect.cpp:266
void setLanguageCodeStashKey(const QString &key=QStringLiteral("c_langselect_lang"))
Definition: langselect.cpp:297
static bool fromUrlQuery(Context *c, const QString &key=QString())
Definition: langselect.cpp:327
void setCookieName(const QString &name)
Definition: langselect.cpp:241
static bool fromSession(Context *c, const QString &key=QString())
Definition: langselect.cpp:349
void setLocalesFromDirs(const QString &path, const QString &name)
Definition: langselect.cpp:185
static bool fromSubDomain(Context *c, const QMap< QString, QLocale > &subDomainMap=QMap< QString, QLocale >())
Definition: langselect.cpp:395
QVector< QLocale > supportedLocales() const
Definition: langselect.cpp:223
virtual ~LangSelect() override
Definition: langselect.cpp:48
void setSessionKey(const QString &key)
Definition: langselect.cpp:235
void addSupportedLocale(const QLocale &locale)
Definition: langselect.cpp:123
virtual bool setup(Application *app) override
Definition: langselect.cpp:53
void setSupportedLocales(const QVector< QLocale > &locales)
Definition: langselect.cpp:94
LangSelect(Application *parent, Source source)
Definition: langselect.cpp:31
QString header(const QString &key) const
Definition: request.h:554
QString queryParam(const QString &key, const QString &defaultValue={}) const
Definition: request.h:542
QString cookie(const QString &name) const
Definition: request.cpp:272
void redirect(const QUrl &url, quint16 status=Found)
Definition: response.cpp:235
void setHeader(const QString &field, const QString &value)
void setCookie(const QNetworkCookie &cookie)
Definition: response.cpp:215
static QVariant value(Context *c, const QString &key, const QVariant &defaultValue=QVariant())
Definition: session.cpp:150
static void setValue(Context *c, const QString &key, const QVariant &value)
Definition: session.cpp:165
The Cutelyst namespace holds all public Cutelyst API.
Definition: Mainpage.dox:8
QFileInfoList entryInfoList(QDir::Filters filters, QDir::SortFlags sort) const const
QStringList entryList(QDir::Filters filters, QDir::SortFlags sort) const const
bool exists() const const
void reserve(int alloc)
int size() const const
QString bcp47Name() const const
QLocale::Language language() const const
const T & value() const const
QMap::const_iterator constBegin() const const
QMap::const_iterator constEnd() const const
bool empty() const const
int size() const const
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QStringList split(const QString &sep, QString::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
QString::const_reverse_iterator crbegin() const const
bool isEmpty() const const
QString left(int n) const const
QByteArray toLatin1() const const
QString toLower() const const
int indexOf(QStringView str, int from) const const
QStringView mid(qsizetype start) const const
LeftToRight
SkipEmptyParts
QString host(QUrl::ComponentFormattingOptions options) const const
QString path(QUrl::ComponentFormattingOptions options) const const
QLocale toLocale() const const
int size() const const