cutelyst
4.5.1
A C++ Web Framework built on top of Qt, using the simple approach of Catalyst (Perl) framework.
|
Detect and select locale based on different input parameters. More...
#include <Cutelyst/Plugins/Utils/LangSelect>
Protected Member Functions | |
bool | setup (Application *app) override |
Protected Member Functions inherited from QObject | |
virtual void | childEvent (QChildEvent *event) |
virtual void | connectNotify (const QMetaMethod &signal) |
virtual void | customEvent (QEvent *event) |
virtual void | disconnectNotify (const QMetaMethod &signal) |
bool | isSignalConnected (const QMetaMethod &signal) const const |
int | receivers (const char *signal) const const |
QObject * | sender () const const |
int | senderSignalIndex () const const |
virtual void | timerEvent (QTimerEvent *event) |
Additional Inherited Members | |
Public Member Functions inherited from Cutelyst::Plugin | |
Plugin (Application *parent) | |
Public Member Functions inherited from QObject | |
QObject (QObject *parent) | |
QBindable< QString > | bindableObjectName () |
bool | blockSignals (bool block) |
const QObjectList & | children () const const |
QMetaObject::Connection | connect (const QObject *sender, const char *signal, const char *method, Qt::ConnectionType type) const const |
void | deleteLater () |
void | destroyed (QObject *obj) |
bool | disconnect (const char *signal, const QObject *receiver, const char *method) const const |
bool | disconnect (const QObject *receiver, const char *method) const const |
void | dumpObjectInfo () const const |
void | dumpObjectTree () const const |
QList< QByteArray > | dynamicPropertyNames () const const |
virtual bool | event (QEvent *e) |
virtual bool | eventFilter (QObject *watched, QEvent *event) |
T | findChild (const QString &name, Qt::FindChildOptions options) const const |
QList< T > | findChildren (const QRegularExpression &re, Qt::FindChildOptions options) const const |
QList< T > | findChildren (const QString &name, Qt::FindChildOptions options) const const |
QList< T > | findChildren (Qt::FindChildOptions options) const const |
bool | inherits (const char *className) const const |
void | installEventFilter (QObject *filterObj) |
bool | isQuickItemType () const const |
bool | isWidgetType () const const |
bool | isWindowType () const const |
void | killTimer (int id) |
virtual const QMetaObject * | metaObject () const const |
void | moveToThread (QThread *targetThread) |
QString | objectName () const const |
void | objectNameChanged (const QString &objectName) |
QObject * | parent () const const |
QVariant | property (const char *name) const const |
Q_CLASSINFO (Name, Value) | |
Q_EMIT Q_EMIT | |
Q_ENUM (...) | |
Q_ENUM_NS (...) | |
Q_ENUMS (...) | |
Q_FLAG (...) | |
Q_FLAG_NS (...) | |
Q_FLAGS (...) | |
Q_GADGET Q_GADGET | |
Q_GADGET_EXPORT (EXPORT_MACRO) | |
Q_INTERFACES (...) | |
Q_INVOKABLE Q_INVOKABLE | |
Q_MOC_INCLUDE Q_MOC_INCLUDE | |
Q_NAMESPACE Q_NAMESPACE | |
Q_NAMESPACE_EXPORT (EXPORT_MACRO) | |
Q_OBJECT Q_OBJECT | |
Q_PROPERTY (...) | |
Q_REVISION Q_REVISION | |
Q_SET_OBJECT_NAME (Object) | |
Q_SIGNAL Q_SIGNAL | |
Q_SIGNALS Q_SIGNALS | |
Q_SLOT Q_SLOT | |
Q_SLOTS Q_SLOTS | |
T | qobject_cast (const QObject *object) |
T | qobject_cast (QObject *object) |
QT_NO_NARROWING_CONVERSIONS_IN_CONNECT QT_NO_NARROWING_CONVERSIONS_IN_CONNECT | |
void | removeEventFilter (QObject *obj) |
void | setObjectName (const QString &name) |
void | setObjectName (QAnyStringView name) |
void | setParent (QObject *parent) |
bool | setProperty (const char *name, const QVariant &value) |
bool | setProperty (const char *name, QVariant &&value) |
bool | signalsBlocked () const const |
int | startTimer (int interval, Qt::TimerType timerType) |
int | startTimer (std::chrono::milliseconds interval, Qt::TimerType timerType) |
QThread * | thread () const const |
Static Public Member Functions inherited from QObject | |
QMetaObject::Connection | connect (const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type) |
QMetaObject::Connection | connect (const QObject *sender, const QMetaMethod &signal, const QObject *receiver, const QMetaMethod &method, Qt::ConnectionType type) |
QMetaObject::Connection | connect (const QObject *sender, PointerToMemberFunction signal, const QObject *context, Functor functor, Qt::ConnectionType type) |
QMetaObject::Connection | connect (const QObject *sender, PointerToMemberFunction signal, const QObject *receiver, PointerToMemberFunction method, Qt::ConnectionType type) |
QMetaObject::Connection | connect (const QObject *sender, PointerToMemberFunction signal, Functor functor) |
bool | disconnect (const QMetaObject::Connection &connection) |
bool | disconnect (const QObject *sender, const char *signal, const QObject *receiver, const char *method) |
bool | disconnect (const QObject *sender, const QMetaMethod &signal, const QObject *receiver, const QMetaMethod &method) |
bool | disconnect (const QObject *sender, PointerToMemberFunction signal, const QObject *receiver, PointerToMemberFunction method) |
QString | tr (const char *sourceText, const char *disambiguation, int n) |
Public Attributes inherited from QObject | |
typedef | QObjectList |
Properties inherited from QObject | |
objectName | |
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. If the language is not supported, it will use a fallback locale. As another fallback it will try to get the locale from the Accept-Language header.
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.
There are some options you can set in your application configuration file in the Cutelyst_LangSelect_Plugin
section. The configuration file options are available since Cutelyst 4.0.0.
Type: string
Default: 1 month
The expiration time of the cookie. The value will be parsed by Utils::durationFromString(), so you can use one of the supported human readable time spans.
Type: string
Default: empty
The domain to be used when setting the cookie. When empty, the browser will set the current domain.
Type: bool
Default: false
Whether to use a secure cookie. If this is set to true
, the cookie will be marked as secure, which means browsers may ensure that the cookie is only sent with an HTTPS connection.
Type: string
Default: lax
Acceptable values: default,none,lax,strict
Defines the SameSite attribute of the cookie. See MDN to learn more about SameSite cookies. See also QNetworkCookie::SameSite.
The plugin is linked to Cutelyst::Core, Cutelyst::Session and the QtNetwork module. To use it in your application, link your application to Cutelyst::Utils::LangSelect.
Definition at line 348 of file langselect.h.
|
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 52 of file langselect.cpp.
References Cutelyst::Application::beforePrepareAction(), QLocale::C, Qt::CaseInsensitive, QString::compare(), Cutelyst::Engine::config(), QObject::connect(), Cutelyst::Utils::durationFromString(), Cutelyst::Application::engine(), and Cutelyst::Application::postForked().