ADF Faces: Setting Locale through Application Logic (rather than Browser language)

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.


 

Comments:

Hello Peter, I'm looking for final solution for changing language through flags (links) in ADF BC + ADF Faces. For pages I wrote code in my FacesPageLifecycle in overriden method preparRender: FacesContext.getCurrentInstance().getViewRoot().setLocale(new Locale(lang)). This works well. Problem is that business components (model layer) probably don't know about this. All validation messages and exception messages are still in english. Are these messages translated in your example?

Posted by Radovan Kobularcik on November 03, 2006 at 07:33 AM PST #

Hello, I still don't have THE solution, but your hints gave me so ideas and I managed to put some things together and obtain a correct behaviour in my application (allow the user to select a language and have all texts of the application immediately correct). Here is my method: - configure dynamically the list of supported locales in faces-context.xml <managed-property> <property-name>localeList</property-name> <property-class>java.util.ArrayList</property-class> <list-entries> <value-class>java.lang.String</value-class> <value>fr</value> <value>de</value> <value>it</value> <value>en</value> </list-entries> </managed-property> <managed-property> <property-name>defaultLocale</property-name> <property-class>java.lang.String</property-class> <value>fr</value> </managed-property> - create a backing bean class (LocaleManager) which receives the supported locales and dynamically builds a SelectItems list for user's choice - create a backing bean class (MessageManager) who reads messages from the bundle (static methods) - present the list as a selectOneRadio in jsp file ("home" menu only) - when the user chooses another language, "onchange" option activates a method of the LocaleManager. - at this point the LocaleManager calls a static method of MessageManager to change the bundle accoring to the new locale Of course, every jsp of the application must use the locale option as you wrote it: <h:view locale="#{localeManager.selectedLocale}"> Finally, business components who build dynamically selectOneMenus, selectOneRadio etc must have a "reset" function for those lists, which I managed by creating a ListManager which renews all of them, including the list of the supported Locales (!). I you are interested just e-mail and I can send you my code.

Posted by enrico rosina on January 02, 2007 at 04:50 AM PST #

I've used the following approach to so that i don't have to explicitly specify the locale attribute for every f:view: public class MyViewHandler extends ViewHandler { private ViewHandler origViewHandler; public MyViewHandler(ViewHandler origViewHandler) { this.origViewHandler = origViewHandler; } public Locale calculateLocale(FacesContext facesContext) { Locale locale = facesContext.getApplication().getDefaultLocale(); String userId = getCurrentUserId(); User user = getUser(userId); if(user.getUserLocale()!=null) locale = user.getUserLocale(); return locale; } public UIViewRoot createView(FacesContext facesContext, String string) { return origViewHandler.createView(facesContext, string); } // ... } you'll need to register your ViewHandler in faces-config.xml.

Posted by Marko Asplund on March 13, 2007 at 08:34 AM PDT #

Hi Peter, I have tried the approach that you have mentioned and it works perfectly on static menu tabs/bars. However when the Menu Tabs/bars are dynamic (using the example used in Oracle doc - MenuItem & MenuTreeModelAdapter) it does not work. Did I missed anything here to implement? Hi Marko, Can you complete your code? sorry I try to find a way to complete your implementation but I was unable to do so... Thanks... Abdul

Posted by Abdul on October 12, 2008 at 03:51 PM PDT #

Abdul, First, you would define all the missing ViewHandler interface methods in MyViewHandler. The implementations would just delegate to origViewHandler. Second, in calculateLocale() you would replace the following code with a custom mechanism for determining user locale: User user = getUser(userId); if(user.getUserLocale()!=null) locale = user.getUserLocale(); Third, register the custom view handler in faces-config.xml.

Posted by Marko Asplund on November 06, 2008 at 03:32 AM PST #

Hi Marko, I tried again with the structure you gave to me and it work now with a little difference on calculateLocale(): LocaleManager localeMgr = (LocaleManager) facesContext.getApplication(). createValueBinding("#{localeManager}").getValue(facesContext); if (localeMgr.getUserLocale() != null) { System.out.println("Custom locale"); locale = new Locale(localeMgr.getUserLocale()); } else { System.out.println("Default english"); locale = new Locale("en"); } return locale; The only the thing left is how to make it work with MenuItem & MenuTreeModelAdapter -- when using dynamic Menu Tabs and Menu Bars. So far this is working if you manually define each menu tab/bar item components in the pages.... Hope you could help me out with this too so that I don't have to change my code... Many thanks, Abdul

Posted by Abdul on November 10, 2008 at 06:36 PM PST #

Post a Comment:
  • HTML Syntax: NOT allowed
About

Java EE Consultants - JHeadstart, ADF, JSF

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today