Cutelyst
2.13.0
|
Language selection plugin. More...
#include <Cutelyst/Plugins/Utils/LangSelect>
Public Types | |
enum | Source : quint8 { URLQuery, Session, Cookie, SubDomain, Domain, AcceptHeader, Fallback } |
Public Member Functions | |
LangSelect (Application *parent) | |
LangSelect (Application *parent, Source source) | |
virtual | ~LangSelect () override |
void | addSupportedLocale (const QLocale &locale) |
void | addSupportedLocale (const QString &locale) |
void | setCookieName (const QString &name) |
void | setDetectFromHeader (bool enabled) |
void | setDomainMap (const QMap< QString, QLocale > &map) |
void | setFallbackLocale (const QLocale &fallback) |
void | setLanguageCodeStashKey (const QString &key=QStringLiteral("c_langselect_lang")) |
void | setLanguageDirStashKey (const QString &key=QStringLiteral("c_langselect_dir")) |
void | setLocalesFromDir (const QString &path, const QString &name, const QString &prefix=QStringLiteral("."), const QString &suffix=QStringLiteral(".qm")) |
void | setLocalesFromDirs (const QString &path, const QString &name) |
void | setQueryKey (const QString &key) |
void | setSessionKey (const QString &key) |
void | setSubDomainMap (const QMap< QString, QLocale > &map) |
void | setSupportedLocales (const QStringList &locales) |
void | setSupportedLocales (const QVector< QLocale > &locales) |
QVector< QLocale > | supportedLocales () const |
![]() | |
Plugin (Application *parent) | |
Static Public Member Functions | |
static bool | fromCookie (Context *c, const QString &name=QString()) |
static bool | fromDomain (Context *c, const QMap< QString, QLocale > &domainMap=QMap< QString, QLocale >()) |
static bool | fromPath (Context *c, const QString &locale) |
static bool | fromSession (Context *c, const QString &key=QString()) |
static bool | fromSubDomain (Context *c, const QMap< QString, QLocale > &subDomainMap=QMap< QString, QLocale >()) |
static bool | fromUrlQuery (Context *c, const QString &key=QString()) |
static QVector< QLocale > | getSupportedLocales () |
Protected Member Functions | |
virtual bool | setup (Application *app) override |
The LangSelect plugin can be used to automatically detect and set a user's language by querying various sources like session keys, cookies, path and subdomain components or the Accept-Language header sent by the user agent (like the web browser). It will compare the detected locale against a list of locales supported by the application to choose the most appropriate locale fitting the user's preferences.
Unless the plugin has been constructed with the manual mode constructor, it will be connected to the Application::beforePrepareAction() signal to set the locale. If auto detection is disabled, you can manually set the locale by calling LangSelect::fromCookie(), LangSelect::fromDomain(), LangSelect::fromPath(), LangSelect::fromSession(), LangSelect::fromUrlQuery() or LangSelect::fromSubDomain() at appropriate places.
Note that you must register plugins like StaticSimple before the LangSelect plugin, especially if you want to store the selected locale in the domain or the path.
On a multilingual site you will mostly have some kind of selector that allows users to choose the display language. Especially on publicly available content you might want to put the locale information into the domain or URL path to optimize your content for search engines.
The plugin will also set two values to the stash that will contain the BCP47 name of the selected locale and the text direction. You can set the stash keys used for this information with setLanguageCodeStashKey() and setLanguageDirStashKey().
One of the main purposes of this plugin is not only to select a locale, but also to select a locale that is supported by your application. Therefore the plugin provides different methods to set the list of supported locales. If you already use Application::loadTranslationsFromDir() or Application::loadTranslationsFromDirs() to load the translation files for your application, you can simply use the returned list of that methods and give them to setSupportedLocales(). If you use a different way of loading translations, have a look at the other functions to set the supported locales: addSupportedLocale(), setSupportedLocales(), setLocalesFromDir() and setLocalesFromDirs().
The plugin can either work automatically or manually. The auto detection mode hooks into the Application::beforePrepareAction() signal to set the locale. The mode of operation is defined when constructing and registering the plugin. If auto detection is disabled, you can use one of the static functions that get and set the locale. Note that you still have to set the list of supported locales and might want to set some defaults for the sources like the session key, etc.
The LangSelect plugin supports different sources to get locale information from. Some sources only set the detected locale internally, other sources that rely on the request URI will perform a redirect to set the detected source. Common to all sources is, that they will fall back to the Accept-Language header and the locale set by setFallbackLocale() if no supported locale can be detected in the source. You can omit the Accept-Language header and use the fallback language directly by setting setDetectFromHeader() to false
. If you set the source to LangSelect::AcceptHeader, the locale will always be extracted from the Accept-Langauge header filed from the request and will never be stored.
For the following examples we will assume that your application supports English, German and Portuguese and English is the fallback locale.
If you use LangSelect::URLQuery to register the plugin in auto mode or if you use LangSelect::fromUrlQuery() to get and set the locale from the url query manually at appropriate places in you application, the plugin will try to detect the locale from the query key specified for the plugin.
If a user now requests a resource by the URL https://www.example.com/my/path?lang=pt
the plugin will automatically select Portuguese as locale and will set it to Context::setLocale(). If the use would call the URL https://www.example.com/my/path?lang=ru
the plugin would redirect him to a locale matching the Accept-Language header of the browser. If that would contain some form of German or Portuguese, the redirection would be performed to the language that would have the highest priority in the header. If the header would not contain a supported language, the user would be redirected to https://www.example.com/my/path?lang=en
.
If you use LangSelect::Cookie to register the plugin in auto mode or if you use LangSelect::fromCookie() to get and set the locale from a cookie manually at appropriate places in your application, the plugin will try to detect the locale from the cookie specified for the plugin.
If a user now requests something on our site for the first time, there will be no cookie containing the language - neither set by auto detection from header nor by some input field. So the plugin will try to detect the language from the Accept-Language header and will store it to the cookie named "lang". On the next request it will not have to examine the header again but can take the locale from the cookie. As the session id is also stored as a cookie this approach is similar to the session approach but does not need a store for the session on the server side. If you use sessions anyway, you should store the locale in the session.
If you use LangSelect::Session to register the plugin in auto mode or if you use LangSelect::fromSession() to get and set the locale from the session manually at appropriate places in your application, the plugin will try to detect the locale from the session key specified for the plugin. This approach is great if you use sessions anyway because the complete QLocale object can be stored in the session, making it quite fast to load compared with the other methods that have to construct the QLocale again from a string on every request.
If a user now requests a resource in our application without having the locale stored in the session value identified by the "lang" key, the fallback locale English will be selected and will be stored to the session. For sure you would then need some selection interface that would make it possible for the user to change the display language. If you only would use the session to store the locale, it might be easier to store the locale in a cookie as that would need no session store.
If you use a chained dispatcher to detect the locale, you can use this plugin in manual mode and use LangSelect::fromPath() at the chained action that takes the locale as path argument. This will then set the locale if it is supported or will redirect to a path containing a supported locale.
If the user would now call the URL http://www.example.com/pt/my/resource
the locale would be set to Portuguese and the normal flow of the application would continue. If the user would call the URL http://www.example.com/ru/my/resource
and has no supported locale in the Accept-Language header, the function would create a redirect to http://www.example.com/en/my/resource
and would detach from the normal execution flow.
If you use LangSelect::SubDomain to register the plugin in auto mode or if you use LangSelect::fromSubDomain() to get the locale from the subdomain manually at appropriate places in your application, the plugin will try to detect the locale from the subdomain part specified for the plugin. This approach needs for sure DNS entries for every supported locale. Let us assume we have the following registered subdomains: www.example.com, de.example.com, en.example.com and pt.example.com. The subdomain will simply be determined by checking if the domain starts with one of the entries from the map set by setSubDomainMap(). If the domain does not start with any of the map keys, the plugin will use the first domain part by splitting the domain name at the dots and will try to construct a valid QLocale object from it. If there can be no supported locale found, the plugin will use a locale from the Accept-Language header or the fallback locale. If for the example you want to use the fallback language English for the www subdomain, simply set setDetectFromHeader() to false
.
If a user now calls the URL http://www.exmaple.com
the plugin will choose a locale from the Accept-Language header or the fallback locale. If the user would call http://pt.example.com
Portuguese would be set as locale.
If you use LangSelect::Domain to register the plugin in auto mode or if you use LangSelect::fromDomain() to get the locale from the domain manually at appropriate places in your application, the plugin will try to detect the locale from the domain part specified for the plugin. This approach needs for sure DNS entries for every supported locale. Let us assume we have the following registered domains: exmaple.br, example.com, example.co.uk and example.de. The domain will simply be determined by checking if the domain ends with one of the keys from the map set by setDomainMap(). If the domain does not end with any of the map keys, the plugin will try to create a valid QLocale object from the TLD of the request URI. If that is not valid or not part of the supported locales, the plugin will try to detect the locale from the Accept-Language header (if setDetectFromHeader() has not been set to false
) or will use the fallback language.
If a user now calls the URL http://www.examble.br
the locale will be set to Portuguese and the normal operation flow will continue. If there are also domains pointing to your application that are not part of the domain map
If you detect and set locales based on the session or a cookie you do not need to change anything on your links in your application. If you use the path or query to detect the locale, you can use Context::uriFor() or Context::uriForAction() to set the locale on internal URIs. For Grantlee themes there is also the Cutelyst specific tag c_uri_for
that can be used as {% c_uri_for "/path" "arg1" "arg2" QUERY c.request.queryParams "foo=bar" %}
. Taking the defaul name of the stash key with the BCP47 name of the selected locale and using the path approach to set the locale, you could use the tag as follows: {% c_uri_for "/path" c_langselect_lang "otherArg" QUERY c.request.queryParams "foo=bar" %}
. If you use the domain or subdomain to set the locale, simply use relative paths in your internal links.
Definition at line 312 of file langselect.h.
enum Cutelyst::LangSelect::Source : quint8 |
Sources that can be used for automatically detecting the locale.
Enumerator | |
---|---|
URLQuery | Tries to get and store the locale from an URL query parameter. Requires setQueryKey(). |
Session | Tries to get and store the locale from a session key. Requires setSessionKey(). |
Cookie | Tries to get and store the locale from a cookie. Requires setCookieName(). |
SubDomain | Tries to get and store the locale from a subdomain part. Requires setSubDomainMap(). |
Domain | Tries to get and store the locale from the domain. Requires setDomainMap(). |
AcceptHeader | Will the locale only detect from the Accept-Header and will not store it. |
Fallback | Only used internal. |
Definition at line 320 of file langselect.h.
LangSelect::LangSelect | ( | Application * | parent, |
Source | source | ||
) |
Constructs a new LangSelect object with the given parent and source in auto detection mode.
The plugin will be connected to the Application::beforePrepareAction signal and will automatically set the appropriate locale extracted from source.
This mode is good is good if you use the same approach to detect and set the locale in your complete application.
Definition at line 44 of file langselect.cpp.
LangSelect::LangSelect | ( | Application * | parent | ) |
Constructs a new LangSelect object with the given parent in manual mode.
The plugin will not be connected to the Application::beforePrepareAction signal so you have to use one of the static functions to set and store the locale.
Definition at line 52 of file langselect.cpp.
References AcceptHeader.
|
overridevirtual |
Deconstructs the LangSelect object.
Definition at line 61 of file langselect.cpp.
void LangSelect::addSupportedLocale | ( | const QLocale & | locale | ) |
Adds a single locale
to the list of supported locales.
Definition at line 136 of file langselect.cpp.
void LangSelect::addSupportedLocale | ( | const QString & | locale | ) |
Adds a single locale
to the list of supported locales. The locale string will only be added if it is valid.
Definition at line 146 of file langselect.cpp.
|
static |
Tries to get the locale from a cookie specified by name. If that cookie is not present or does not contain a supported locale, the plugin will either try to get a supported locale from the Accept-Language header (if setDetectFromHeader() is not set to false
) or it will use the locale set by setFallbackLocale().
If the cookie did not contain a valid locale, the locale detected by the header or the fallback locale will be set to the cookie with the given name.
See the cookie example to learn more about this approach.
c | The current Context. |
name | The name of the cookie to get and store the locale. If empty, the value set by setCookieName() will be used. |
true
if the cookie contained a supported locale, otherwise false
. Definition at line 385 of file langselect.cpp.
|
static |
Tries to get the locale from the domain specified in the domainMap. The domainMap has to contain mappings between domains and locales. See the domain example to get an idea on how this approach works. If the domain in the request URI can not be found in the domainMap, the plugin will try to create a valid QLocale object from the TLD part of the domain. If that is not valid, it will either try to get a supported locale from the Accept-Language header (if setDetectFromHeader() is not set to false
) or it will use the locale set by setFallbackLocale().
c | The current Context. |
domainMap | Mapping between domains and locales. If empty, the map set by setDomainMap() will be used. |
true
if the domain has been found in the domainMap or the TLD, otherwise false
if the locale has been set by the Accept-Header of the fallback locale. Definition at line 431 of file langselect.cpp.
|
static |
Takes the locale from a string extracted as argument from a chained action. See the path example to get an idea on how this approach works.
I the locale is not part of the supported locales, the plugin will create a 307 redirect to the same path but with a valid locale either extracted from the Accept-Language header (if setDetectFromHeader() is not set to false
) or it will use the locale set by setFallbackLocale(). If the redirect will be performed, the function will return false
and Context::detach() will be called, otherwise it will return true
.
c | The current Context. |
locale | Locale string supported by QLocale. |
true
if the locale has been found in the path, false
if the locale will be set by a redirect to a path containing a valid locale. Definition at line 454 of file langselect.cpp.
References Cutelyst::Context::detach(), Cutelyst::Context::locale(), Cutelyst::Response::redirect(), Cutelyst::Context::res(), and Cutelyst::Context::setLocale().
|
static |
Tries to get the locale from a session value specified by key. If that key is not present or does not contain a supported locale, the plugin will either try to get a supported locale from the Accept-Language header (if setDetectFromHeader() is not set to false
) or it will use the locale set by setFallbackLocale().
If the session did not contain a valid supported locale, the locale detected by the header or the fallback locale will be set to the session under the given key.
See the session example to learn more about this appraoch.
c | The current Context. |
key | The session key to get and store the locale. If empty the value set by setSessionKey() will be used. |
true
if the session contained a supported locale, otherwise false
. Definition at line 362 of file langselect.cpp.
|
static |
Tries to get the locale from the subdomain specified in the subDomainMap. The subDomainMap has to contain mappings between subdomains and locales. See the subdomain example to get an idea on how this approach works. If the domain in the request URI can nat be found in the subDomainMap, the plugin will try to create a valid QLocale object from the first subdomain part. If that is not valid it will either try to get a supported locale from the Accept-Language header (if setDetectFromHeader() is not set to false
) or it will use the locale set by setFallbackLocale().
c | The current Context. |
subDomainMap | Mapping between subdomains and locales. If empty, the map set by setSubDomainMap() will be used. |
true
if the subdomain has been found in the subDomainMap or the first sub domain, false
if the locale will be set by a the Accept-Header or the fallback locale. Definition at line 408 of file langselect.cpp.
|
static |
Tries to get the locale from an URL query parameter specified by key. If that key is not present or does not contain a supported locale, the plugin will either try to get a supported locale from the Accept-Language header (if setDetectFromHeader() is not set to false
) or it will use the locale set by setFallbackLocale().
If the URL query did not contain a valid supported locale, the locale detected by the header or the fallback locale will be set to the URL query by creating a 307 redirect on the response that contains the selected locale in the query key. If this redirect will be performed, the function returns false
, otherwise it will return true
.
See the URL query example to learn more about this approach.
c | The current Context. |
key | The URL query key to check. If empty the value set by setQueryKey() will be used. |
true
if the URL query contained a supported locale, false
if the locale will be set by a redirect. Definition at line 340 of file langselect.cpp.
References Cutelyst::Context::detach().
|
static |
Returns the list of supported locales.
Definition at line 330 of file langselect.cpp.
References supportedLocales().
void LangSelect::setCookieName | ( | const QString & | name | ) |
Sets the name of a cookie to store and retrieve the locale.
Definition at line 254 of file langselect.cpp.
void LangSelect::setDetectFromHeader | ( | bool | enabled | ) |
Set to false
if locale detection by Accept-Language header should be disabled. By default, the detection by the header is enabled if the other sources can not extract a valid supported locale.
Definition at line 304 of file langselect.cpp.
void LangSelect::setDomainMap | ( | const QMap< QString, QLocale > & | map | ) |
Sets the map for full domains as source for locale selection. The map should contain the locale domain part as key and the associated locale as value. See the domain example to learn more about this approach.
Definition at line 279 of file langselect.cpp.
void LangSelect::setFallbackLocale | ( | const QLocale & | fallback | ) |
Sets the fallback locale to be used if no appropriate locale can be found.
Definition at line 298 of file langselect.cpp.
void LangSelect::setLanguageCodeStashKey | ( | const QString & | key = QStringLiteral("c_langselect_lang") | ) |
Sets the name of the stash key that will contain the BCP47 name of the selected locale.
Definition at line 310 of file langselect.cpp.
void LangSelect::setLanguageDirStashKey | ( | const QString & | key = QStringLiteral("c_langselect_dir") | ) |
Sets the name of the stash key that will contain the text direction of the selected locale, either "ltr" or "rtl".
Definition at line 320 of file langselect.cpp.
void LangSelect::setLocalesFromDir | ( | const QString & | path, |
const QString & | name, | ||
const QString & | prefix = QStringLiteral(".") , |
||
const QString & | suffix = QStringLiteral(".qm") |
||
) |
Sets the list of supported locales by reading translation files from path. name specifies the base file name while prefix is a delimeter used to delimit the base name from the locale identification part. suffix is the file suffix, for Qt translations it will be mostly ".qm".
You can use this function if your translations are organized in a single directory like
/usr/share/myapp/translations/myapp_de
.qm /usr/share/myapp/translations/myapp_pt_BR
.qm
...For the above organization of translation files, the call to this function would be as follows:
If you already use Application::loadTranslations() to load your app translations you don't need to use this function. Simply take the list returned by Application::loadTranslations() and give it to setSupportedLocales().
Definition at line 157 of file langselect.cpp.
void LangSelect::setLocalesFromDirs | ( | const QString & | path, |
const QString & | name | ||
) |
Sets the list of supported locales by reading translation files with a specific name from a directory structure under path.
You can use this function if your translations are organized in multiple directories like
/usr/share/locale/de/LC_MESSAGES/myapp
.qm /usr/share/locale/pt_BR/LC_MESSAGES/myapp
.qm
...For the above organization of translation files, the call to this function would be as follows:
If you already use Application::loadTranslationFromDirs() to load your app translations you don't need to use this function. Simply take the list returned by Application::loadTranslationsFromDirs() and give it to setSupportedLocales().
Definition at line 198 of file langselect.cpp.
void LangSelect::setQueryKey | ( | const QString & | key | ) |
Sets a key used in the URL query to store and retrieve the locale.
Definition at line 242 of file langselect.cpp.
void LangSelect::setSessionKey | ( | const QString & | key | ) |
Set a session key used to store and retrieve the locale.
Definition at line 248 of file langselect.cpp.
void LangSelect::setSubDomainMap | ( | const QMap< QString, QLocale > & | map | ) |
Sets the map for subdomains as source for locale selection. The map should contain the locale subdomain part as key and the associated locale as value. See the subdomain example to learn more about this approach.
Definition at line 260 of file langselect.cpp.
void LangSelect::setSupportedLocales | ( | const QStringList & | locales | ) |
Sets the list of supported locales. Use this function if you exactly now which locales your application supports. Only valid locale strings are added to the list of supported locales.
Definition at line 121 of file langselect.cpp.
void LangSelect::setSupportedLocales | ( | const QVector< QLocale > & | locales | ) |
Sets the list of supported locales. Use this function if you exactly now which locales your application supports. It also fits perfectly to the list of loaded locales that is returned by Application::loadTranslations() or Application::loadTranslationsFromDirs().
Definition at line 107 of file langselect.cpp.
|
overrideprotectedvirtual |
Sets the plugin up and checks the plugin configuration. If the configuration contains errors, it will return false
, otherwise it will return true
. If the plugin has been constructed with auto detection constructor, it will connect the plugin to the Application::beforePrepareAction() signal.
Reimplemented from Cutelyst::Plugin.
Definition at line 66 of file langselect.cpp.
References Cutelyst::Application::beforePrepareAction(), Cookie, Fallback, Cutelyst::Application::postForked(), and URLQuery.
QVector< QLocale > LangSelect::supportedLocales | ( | ) | const |
Returns the list of supported locales. There is also the static method LangSelect::getSupportedLocales().
Definition at line 236 of file langselect.cpp.
Referenced by getSupportedLocales().