Every request processed by every portlet is invoked within a context of current Locale. Current Locale can be retrieved by calling the getLocale() method of the javax.portlet.PortletRequest interface.
The exact algorithm for determining the current Locale is not specified by Portlet Specification, and is left to portlet containers to implement the way they deem most appropriate.
In GateIn 3.2, each portal instance has a default language which can be used to present content for new users. Another option is to use each user’s browser language preference, provided it matches one of the available localizations that GateIn 3.2 supports, and only fallback to portal default language if no match is found. Every user, while visiting a portal, has an option to change the language of the user interface by using a Language chooser. The choice can be remembered for the duration of the session, or it can be remembered for a longer period using a browser cookie, or - for registered and logged-in users - it can be saved into user’s profile.
So, we can see that there is more than one way to determine the Locale to be used for displaying a portal page to the user. For this reason the mechanism for determining the current Locale of the request is pluggable in GateIn 3.2, so the exact algorithm can be customized.
Customization is achieved by using LocalePolicy API, which is a simple API consisting of one interface, and one class:
- org.exoplatform.services.resources.LocalePolicy interface
- org.exoplatform.services.resources.LocaleContextInfo class
The LocalePolicy interface defines a single method that is invoked on the installed LocalePolicy service implementation:
Locale returned by the determineLocale() method is Locale that will be returned to portlets when they call javax.portlet.PortletRequest.getLocale() method.
The returned Locale has to be one of the locales supported by portal, otherwise it will fallback to portal-default Locale.
The supported locales are listed in gatein.ear/portal.war/WEB-INF/conf/common/locales-config.xml file as described in sect-Reference_Guide-Internationalization_Configuration-Locales_configuration.
The determineLocale() method takes a parameter of LocaleContextInfo type, which represents a compilation of preferred locales from different sources, including user’s profile, portal default, browser language settings, current session, browser cookie, and more. All these different sources of Locale configuration or preference are used as input to the LocalePolicy implementation that decides which Locale should be used.
By default, org.exoplatform.portal.application.localization.DefaultLocalePolicyService- an implementation of LocalePolicy- is installed to provide the default behaviour. This, however, can easily be extended and overridden. A completely new implementation can also be written from scratch.
DefaultLocalePolicyService treats logged-in users slightly differently than anonymous users. Logged-in users have a profile that can contain language preference, while anonymous users do not.
Here is an algorithm used for anonymous users.
Iterate over the LocaleContextInfo properties in the following order:
Get each property's value - if it's a collection, get the first value.
- If value is one of the supported locales return it as a result.
- If value is not in the supported locales set, try to remove country information, and check if a language matching locale is in the list of supported locales. If so, return it as a result.
Otherwise, continue with the next property.
If no supported locale is found, the return locale eventually defaults to portalLocale.
The algorithm for logged-in users is virtually the same except that the first Locale source checked is user's profile.
Iterate over LocaleContextInfo properties in the following order:
Do the next steps which are the same as for anonymous users.
The easiest way to customize the LocalePolicy is to extend DefaultLocalePolicyService. The study of its source code will be required. There is sample JavaDoc that provides thorough information. Most customizations will involve simply overriding one or more of its protected methods.
An example of a customization is an already provided NoBrowserLocalePolicyService. By overriding just one method, it skips any use of browser language preference.
The LocalePolicy framework is enabled for portlets by configuring the LocalizationLifecycle class in portal's webui configuration file: gatein.ear/portal.war/WEB-INF/webui-configuration.xml.
The default LocalePolicy implementation is installed as GateIn Kernel portal service via gatein.ear/portal.war/WEB-INF/conf/portal/web-configuration.xml. Here you can change it to a different value according to your needs.
The following fragment is responsible for installing the service:
Besides implementing LocalePolicy, the service class also needs to implement org.picocontainer.Startable interface in order to get installed.
In portals, all the resources that are not portlets themselves but are accessed through portlets - reading data through PortletRequest, and writing to PortletResponse- are referred to as 'bridged'. Any resources that are accessed directly, bypassing portal filters and servlets, are referred to as 'non-bridged'.
Non-bridged servlets, and .jsps have no access to PortalRequest. They do not use PortletRequest.getLocale() to determine current Locale. Instead, they use ServletRequest.getLocale() which is subject to precise semantics defined by Servlet specification - it reflects browser's language preference.
In other words, non-bridged resources do not have a notion of current Locale in the same sense that portlets do. The result is that when mixing portlets and non-bridged resources there may be a localization mismatch - an inconsistency in the language used by different resources composing your portal page.
This problem is addressed by LocalizationFilter. This is a filter that changes the behaviour of ServletRequest.getLocale() method so that it behaves the same way as PortletRequest.getLocale(). That way even localization of servlets, and .jsps accessed in a non-bridged manner can stay in synchronization with the portlet localization.
LocalizationFilter is installed through the gatein.ear/portal.war/WEB-INF/web.xml file of the portal.
One tiny limitation to this mechanism is that it is unable to determine the current portal, and consequently its default language. As a result, the portalLocale defaults to English, but can be configured to something else by using filter's PortalLocale init param. For example:
By default, LocalizationFilter is applied to *.jsp, which is considered the minimum required by GateIn 3.2 to properly keep its non-bridged resources in synchronization with the rest of the portal. Additionally, deployed portlets and portal applications may need broader mapping to cover their non-bridged resources.
Avoid using /, /public/, /private/*, and similar broad mappings as LocalizationFilter sometimes adversely interacts with the processing of portlet requests. Use multiple filter-mappings instead to specifically target non-bridged resources.
Keeping the mapping limited to only non-bridged resources will minimize any impact on performance as well.