Cutelyst  3.5.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 + QLatin1Char('*') + _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 + QLatin1Char('/') + 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(QLatin1Char('/'));
461  const auto localeIdx = pathParts.indexOf(locale);
462  pathParts[localeIdx] = c->locale().bcp47Name().toLower();
463  uri.setPath(pathParts.join(QLatin1Char('/')));
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 #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
584  const auto domainParts = domain.split(QLatin1Char('.'), Qt::SkipEmptyParts);
585 #else
586  const auto domainParts = domain.split(QLatin1Char('.'), QString::SkipEmptyParts);
587 #endif
588  if (domainParts.size() > 2) {
589  const QLocale l(domainParts.at(0));
590  if (l.language() != QLocale::C && locales.contains(l)) {
591  qCDebug(C_LANGSELECT) << "Found supported locale" << l << "in subdomain of domain" << domain;
592  c->setLocale(l);
593  return true;
594  }
595  }
596  qCDebug(C_LANGSELECT) << "Can not find supported locale for subdomain" << domain;
597  return false;
598 }
599 
600 bool LangSelectPrivate::getFromDomain(Context *c, const QMap<QString, QLocale> &map) const
601 {
602  const auto domain = c->req()->uri().host();
603  auto i = map.constBegin();
604  while (i != map.constEnd()) {
605  if (domain.endsWith(i.key())) {
606  qCDebug(C_LANGSELECT) << "Found valid locale" << i.value() << "in domain map for domain" << domain;
607  c->setLocale(i.value());
608  return true;
609  }
610  ++i;
611  }
612 
613 #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
614  const auto domainParts = domain.split(QLatin1Char('.'), Qt::SkipEmptyParts);
615 #else
616  const auto domainParts = domain.split(QLatin1Char('.'), QString::SkipEmptyParts);
617 #endif
618  if (domainParts.size() > 1) {
619  const QLocale l(domainParts.at(domainParts.size() - 1));
620  if (l.language() != QLocale::C && locales.contains(l)) {
621  qCDebug(C_LANGSELECT) << "Found supported locale" << l << "in domain" << domain;
622  c->setLocale(l);
623  return true;
624  }
625  }
626  qCDebug(C_LANGSELECT) << "Can not find supported locale for domain" << domain;
627  return false;
628 }
629 
630 bool LangSelectPrivate::getFromHeader(Context *c, const QString &name) const
631 {
632  if (detectFromHeader) {
633 #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
634  const auto accpetedLangs = c->req()->header(name).split(QLatin1Char(','), Qt::SkipEmptyParts);
635 #else
636  const auto accpetedLangs = c->req()->header(name).split(QLatin1Char(','), QString::SkipEmptyParts);
637 #endif
638  if (Q_LIKELY(!accpetedLangs.empty())) {
639  std::map<float,QLocale> langMap;
640  for (const QString &al : accpetedLangs) {
641  const auto idx = al.indexOf(QLatin1Char(';'));
642  float priority = 1.0f;
643  QString langPart;
644  bool ok = true;
645  if (idx > -1) {
646  langPart = al.left(idx);
647  const QString ref = al.mid(idx + 1);
648  priority = ref.mid(ref.indexOf(QLatin1Char('=')) +1).toFloat(&ok);
649  } else {
650  langPart = al;
651  }
652  QLocale locale(langPart);
653  if (ok && locale.language() != QLocale::C) {
654  const auto search = langMap.find(priority);
655  if (search == langMap.cend()) {
656  langMap.insert({priority, locale});
657  }
658  }
659  }
660  if (!langMap.empty()) {
661  auto i = langMap.crbegin();
662  while (i != langMap.crend()) {
663  if (locales.contains(i->second)) {
664  c->setLocale(i->second);
665  qCDebug(C_LANGSELECT) << "Selected locale" << c->locale() << "from" << name << "header";
666  return true;
667  }
668  ++i;
669  }
670  // if there is no exact match, lets try to find a locale
671  // where at least the language matches
672  i = langMap.crbegin();
673  const auto constLocales = locales;
674  while (i != langMap.crend()) {
675  for (const QLocale &l : constLocales) {
676  if (l.language() == i->second.language()) {
677  c->setLocale(l);
678  qCDebug(C_LANGSELECT) << "Selected locale" << c->locale() << "from" << name << "header";
679  return true;
680  }
681  }
682  ++i;
683  }
684  }
685  }
686  }
687 
688  return false;
689 }
690 
691 void LangSelectPrivate::setToQuery(Context *c, const QString &key) const
692 {
693  auto uri = c->req()->uri();
694  QUrlQuery query(uri);
695  if (query.hasQueryItem(key)) {
696  query.removeQueryItem(key);
697  }
698  query.addQueryItem(key, c->locale().bcp47Name().toLower());
699  uri.setQuery(query);
700  qCDebug(C_LANGSELECT) << "Storing selected locale in URL query by redirecting to" << uri;
701  c->res()->redirect(uri, 307);
702 }
703 
704 void LangSelectPrivate::setToCookie(Context *c, const QString &name) const
705 {
706  qCDebug(C_LANGSELECT) << "Storing selected locale in cookie with name" << name;
707  c->res()->setCookie(QNetworkCookie(name.toLatin1(), c->locale().bcp47Name().toLatin1()));
708 }
709 
710 void LangSelectPrivate::setToSession(Context *c, const QString &key) const
711 {
712  qCDebug(C_LANGSELECT) << "Storing selected locale in session key" << key;
713  Session::setValue(c, key, c->locale());
714 }
715 
716 void LangSelectPrivate::setFallback(Context *c) const
717 {
718  qCDebug(C_LANGSELECT) << "Can not find fitting locale, using fallback locale" << fallbackLocale;
719  c->setLocale(fallbackLocale);
720 }
721 
722 void LangSelectPrivate::setContentLanguage(Context *c) const
723 {
724  if (addContentLanguageHeader) {
725  c->res()->setHeader(QStringLiteral("Content-Language"), c->locale().bcp47Name());
726  }
727  c->stash({
728  {langStashKey, c->locale().bcp47Name()},
729  {dirStashKey, (c->locale().textDirection() == Qt::LeftToRight ? QStringLiteral("ltr") : QStringLiteral("rtl"))}
730  });
731 }
732 
733 void LangSelectPrivate::beforePrepareAction(Context *c, bool *skipMethod) const
734 {
735  if (*skipMethod) {
736  return;
737  }
738 
739  if (!c->stash(SELECTION_TRIED).isNull()) {
740  return;
741  }
742 
743  detectLocale(c, source, skipMethod);
744 
745  c->setStash(SELECTION_TRIED, true);
746 }
747 
748 void LangSelectPrivate::_q_postFork(Application *app)
749 {
750  lsp = app->plugin<LangSelect *>();
751 }
752 
753 #include "moc_langselect.cpp"
void setCookie(const QNetworkCookie &cookie)
Definition: response.cpp:215
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const const
void postForked(Cutelyst::Application *app)
bool empty() const const
void setHeader(const QString &field, const QString &value)
static bool fromPath(Context *c, const QString &locale)
Definition: langselect.cpp:441
QStringList split(const QString &sep, QString::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
void reserve(int alloc)
Response * res() const noexcept
Definition: context.cpp:103
QMap::const_iterator constBegin() const const
QString host(QUrl::ComponentFormattingOptions options) const const
virtual ~LangSelect() override
Definition: langselect.cpp:48
void setStash(const QString &key, const QVariant &value)
Definition: context.cpp:212
void detach(Action *action=nullptr)
Definition: context.cpp:333
QString::const_reverse_iterator crbegin() const const
T plugin()
Returns the registered plugin that casts to the template type T.
Definition: application.h:102
QString queryParam(const QString &key, const QString &defaultValue={}) const
Definition: request.h:542
void setQueryKey(const QString &key)
Definition: langselect.cpp:229
QVector< QLocale > supportedLocales() const
Definition: langselect.cpp:223
int size() const const
void setSupportedLocales(const QVector< QLocale > &locales)
Definition: langselect.cpp:94
The Cutelyst Context.
Definition: context.h:38
static bool fromCookie(Context *c, const QString &name=QString())
Definition: langselect.cpp:372
void setLanguageCodeStashKey(const QString &key=QStringLiteral("c_langselect_lang"))
Definition: langselect.cpp:297
bool exists() const const
void redirect(const QUrl &url, quint16 status=Found)
Definition: response.cpp:235
static bool fromUrlQuery(Context *c, const QString &key=QString())
Definition: langselect.cpp:327
void stash(const QVariantHash &unite)
Definition: context.cpp:540
void beforePrepareAction(Cutelyst::Context *c, bool *skipMethod)
QFileInfoList entryInfoList(QDir::Filters filters, QDir::SortFlags sort) const const
bool isEmpty() const const
void setLocalesFromDirs(const QString &path, const QString &name)
Definition: langselect.cpp:185
QMap::const_iterator constEnd() const const
Qt::LayoutDirection textDirection() const const
QString header(const QString &key) const
Definition: request.h:554
QString path(QUrl::ComponentFormattingOptions options) const const
QLocale::Language language() const const
const T & value() const const
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:7
QLocale locale() const noexcept
Definition: context.cpp:447
void setLanguageDirStashKey(const QString &key=QStringLiteral("c_langselect_dir"))
Definition: langselect.cpp:307
QString toLower() const const
void addSupportedLocale(const QLocale &locale)
Definition: langselect.cpp:123
void setLocale(const QLocale &locale)
Definition: context.cpp:453
static bool fromDomain(Context *c, const QMap< QString, QLocale > &domainMap=QMap< QString, QLocale >())
Definition: langselect.cpp:418
Language selection plugin.
Definition: langselect.h:299
QByteArray toLatin1() const const
QString mid(int position, int n) const const
virtual bool setup(Application *app) override
Definition: langselect.cpp:53
static QVector< QLocale > getSupportedLocales()
Definition: langselect.cpp:317
void setCookieName(const QString &name)
Definition: langselect.cpp:241
QStringList entryList(QDir::Filters filters, QDir::SortFlags sort) const const
void setSubDomainMap(const QMap< QString, QLocale > &map)
Definition: langselect.cpp:247
void setDomainMap(const QMap< QString, QLocale > &map)
Definition: langselect.cpp:266
QString bcp47Name() const const
void setSessionKey(const QString &key)
Definition: langselect.cpp:235
LangSelect(Application *parent, Source source)
Definition: langselect.cpp:31
static QVariant value(Context *c, const QString &key, const QVariant &defaultValue=QVariant())
Definition: session.cpp:150
QString left(int n) const const
QLocale toLocale() const const
QString cookie(const QString &name) const
Definition: request.cpp:272
The Cutelyst Application.
Definition: application.h:42
static bool fromSession(Context *c, const QString &key=QString())
Definition: langselect.cpp:349
int indexOf(const QRegExp &rx, int from) const const
void setFallbackLocale(const QLocale &fallback)
Definition: langselect.cpp:285
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
int size() const const
static bool fromSubDomain(Context *c, const QMap< QString, QLocale > &subDomainMap=QMap< QString, QLocale >())
Definition: langselect.cpp:395
int size() const const
void setDetectFromHeader(bool enabled)
Definition: langselect.cpp:291
void setLocalesFromDir(const QString &path, const QString &name, const QString &prefix=QStringLiteral("."), const QString &suffix=QStringLiteral(".qm"))
Definition: langselect.cpp:144