ADF Faces allows you to create multi-lingual applications without much effort at all. Translations are present for all the standard texts and messages that ADF Faces uses, and all you need to do is provide resource bundles containing translations for all the application-specific texts, labels, prompts etc. in your application.
By default, ADF Faces determines the locale of the end user by inspecting the browser language settings. Recently, a partner asked me how this default behaviour could be changed to an implementation where the application itself can determine the locale to be used for a specific end user. A common scenario is to provide the end user with links or "flag" images in the page on which he can click to determine his language preference. Another common scenario is to use some kind of "user preference" functionality where preferred locales are persisted and automatically restored when the user logs in.
At first, I thought it would take a few minutes in the JDeveloper Online Help to find an example on how to do this, but that did not appear to be the case. I did learn some interesting facts, though, that I'll list here in case you're interested:
- You can get to the current Locale with the following EL expression: #{view.locale.language}.
- The scope of the "view" bean is "request", and it is an instance of javax.faces.component.UIViewRoot.
- In Java code, you can get a hold of this object using the following code: FacesContext.getCurrentInstance().getViewRoot()
- The "locale" property of this UIViewRoot bean is "writable", and is indeed the "single point of truth" for all locale-dependent code in the ADF Faces framework.
Wild goose chase
Lacking an example, my first incling was to look for this type of functionality in the adf-faces-config.xml file. As the ADF Faces Developers Guide points out, you can use EL expressions in that file (very cool!), so I hoped to be able to set the default locale there using an expression like #{userPrefs.locale}. Unfortunately, this did not appear to be the case.
Next, I turned my attention to the following section in the Faces Config file:
<application>
...
<locale-config>
<default-locale>en</default-locale>
</locale-config>
...
</application>
In the <locale-config> section you can specify the default locale and the supported locales for your application. Unfortunately, it is not allowed to put an EL expression in here as follows:
<default-locale>#{userPrefs.locale}</default-locale>
With that, I had exhausted the possibilities I could come up with to solve this problem in a declarative manner.
Google came to the resque, however, and pointed me to the "locale" property of the <f:view> component. Suddenly it all made sense to me: both the Java statement: FacesContext.getCurrentInstance().getViewRoot() and the EL expression #{view.locale.language} are just different ways to access the <f:view> component, and you can set the "locale" property declaratively in the JSF page!
The Solution
As an example, I implemented a small "LocaleManager" Java class, and used in a managed bean. Let's start with the managed bean definition in the Faces Config file:
<managed-bean>
<managed-bean-name>localeManager</managed-bean-name>
<managed-bean-class>demoapp.LocaleManager</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
I'll provide the code of the LocaleManager class here:
public class LocaleManager
{
String userLocale;
public void setUserLocale(String userLocale)
{
this.userLocale = userLocale;
}
public String getUserLocale()
{
return userLocale;
}
public void changeLocale(ActionEvent actionEvent)
{
String id = actionEvent.getComponent().getId();
setUserLocale(id.substring(id.lastIndexOf("_")+1));
}
}
The LocaleManager has a writable "userLocale" property that could be used in JSF pages both to show and change the "userLocale" user preference. But also added is a "changeLocale" action listener method that can be used, for instance, if you want clickable links, images or buttons in your application that the end user can use to set the locale to his preference. In this example, I (ab)use the "Id" property for parameter-passing; when this method is invoked from a UI component with id="xxxxx_en", "en" will be the new locale.
Next step is to tie the View component in the JSP pages to the LocaleManager, which is straightforward:
<f:view locale="#{localeManager.userLocale}"/>
Finally, lets put buttons in the page with which we can invoke the "changeLocale' method, for example:
<af:commandButton
text="French"
actionListener="#{localeManager.changeLocale}"
id="changeLocale_fr"/>
Conclusion
Although this approach is pretty straighforward and requires little coding, I still feel that the more "logical" place for configuring the locale behaviour would be in the <locale-config> section (what's in a name) in the Faces config file, rather than in a UI component in each and every JSF page of my application. I am actually hoping that someone reading this will point out that this is indeed possible. But at least, this is a workable solution, especially for JHeadstart users who only need to customize the dataPage.vm template to get this set up in all the generated pages.