Pages

Wednesday, February 25, 2009

Change language / Locale in ADF

Sometimes you need to support multiple languages in your ADF application. This probably means that you need to change labels, listbox values and maybe need to change the number format layout like the decimal and number grouping separator. In this example I will show you how you can achieve this. Here are some pics of the JDeveloper 11g example application I made. You can change the language by selecting an language in the listbox and press the refresh button or you can use a language.
Here we go from dutch to english.
In this example I am using resourcebundles to change the values / labels to the right language. You can use properties or java files for the resourcebundles. To use these resourcebundles in our application we have to configure this in the faces-config.xml or do this in the JSF page.
The JSF page I made for this demo

<?xml version='1.0' encoding='windows-1252'?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.1"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:af="http://xmlns.oracle.com/adf/faces/rich"
xmlns:c="http://java.sun.com/jsp/jstl/core">
<c:set var="viewcontrollerBundle"
value="#{adfBundle['nl.whitehorses.locale.view.ViewControllerBundle']}"/>
<jsp:directive.page contentType="text/html;charset=windows-1252"/>
<f:view>
<af:document>
<af:messages/>
<af:form>
<af:panelFormLayout>
<af:panelGroupLayout layout="horizontal">
<af:selectOneChoice label="Locale" id="choice"
valueChangeListener="#{UserPreferences.localeChangeListener}"
value="#{UserPreferences.language}"
autoSubmit="true">
<af:selectItem label="Dutch" value="nl"/>
<af:selectItem label="English" value="en"/>
</af:selectOneChoice>
<af:commandButton text="Refresh"/>
<af:spacer width="10" height="10"/>
<af:commandButton text="Dutch" disabled="#{UserPreferences.language == 'nl'}"
actionListener="#{UserPreferences.localeChangeListener}">
<af:setActionListener to="#{UserPreferences.language}" from="nl"/>
</af:commandButton>
<af:commandButton text="English" disabled="#{UserPreferences.language == 'en'}"
actionListener="#{UserPreferences.localeChangeListener}">
<af:setActionListener to="#{UserPreferences.language}" from="en"/>
</af:commandButton>
</af:panelGroupLayout>
<af:spacer width="10" height="20"/>
<af:outputLabel value="#{viewcontrollerBundle.OUTPUT}"/>
<af:outputLabel value="#{msg.OUTPUT}"/>
<af:outputLabel value="#{jmsg.java_output}"/>
<af:outputLabel value="100000">
<af:convertNumber/>
</af:outputLabel>
<af:selectOneChoice label="Label" value="2">
<af:forEach items="#{bindings.lovDataResult.rangeSet}" var="li">
<af:selectItem label="#{li.lovLabel}" value="#{li.lovValue}"/>
</af:forEach>
</af:selectOneChoice>
</af:panelFormLayout>
</af:form>
</af:document>
</f:view>
</jsp:root>

I use the c:set component in the JSF page to define the resourcebundle, but when you want to use this resourcebundle in more then one page you also can define this in the faces-config.xml.
Here is my faces-config.xml which I use in this demo.

<?xml version="1.0" encoding="windows-1252"?>
<faces-config version="1.2" xmlns="http://java.sun.com/xml/ns/javaee">
<application>
<default-render-kit-id>oracle.adf.rich</default-render-kit-id>
<locale-config>
<default-locale>nl</default-locale>
<supported-locale>nl</supported-locale>
<supported-locale>en</supported-locale>
</locale-config>
<resource-bundle>
<base-name>nl.whitehorses.locale.view.locale</base-name>
<var>msg</var>
</resource-bundle>
<resource-bundle>
<base-name>nl.whitehorses.locale.view.JavaLocale</base-name>
<var>jmsg</var>
</resource-bundle>
</application>
</faces-config>

In the faces-config we can define what our default locale is. The resourcebundle without an underscore and country code should contain the labels which matches the default locale. In this example I also support english so I need to create a xxxx_en.properties or xxx_en.java file which contain the english values. After this we need define the main resourcebundles ( without _en ) and under which variable name it is known.

Here you see an project overview of the different resourcebundles I used in this example

To format number values with the right decimal and number grouping separator we can use the trinidad-config.xml. Here we can use an EL expression to define the separator for the right Locale.

<?xml version="1.0" encoding="windows-1252"?>
<trinidad-config xmlns="http://myfaces.apache.org/trinidad/config">
<skin-family>blafplus-rich</skin-family>
<number-grouping-separator>#{UserPreferences.language=='nl' ? '.' : ','}</number-grouping-separator>
<decimal-separator>#{UserPreferences.language=='nl' ? ',' : '.'}</decimal-separator>
</trinidad-config>

To see the effect of these separators we need to use af:convertNumber on an inputtext or an outputtext.

In the JSF and the trinidad-config.xml I use this backing bean to set or read the selected language.
In this backing bean I also call an ADF MethodAction which refreshes the listbox with the right locale values.

package nl.whitehorses.bean;

import java.io.Serializable;

import java.util.Locale;

import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.faces.event.ValueChangeEvent;

import oracle.adf.model.BindingContext;
import oracle.adf.model.binding.DCBindingContainer;

import oracle.binding.OperationBinding;

public class UserPreferences implements Serializable {

private String language;

public UserPreferences() {
language = Locale.getDefault().getLanguage();
}

private void changeLocale(String language){
System.out.println("changeLocale "+language);
this.language = language;
Locale newLocale = new Locale(this.language);
FacesContext context = FacesContext.getCurrentInstance();
context.getViewRoot().setLocale(newLocale);

// refresh lov method action
DCBindingContainer bc = (DCBindingContainer)BindingContext.getCurrent().getCurrentBindingsEntry();
OperationBinding lovOper = bc.getOperationBinding("getLovData");
lovOper.execute();

}

public void localeChangeListener(ValueChangeEvent valueChangeEvent) {
changeLocale(valueChangeEvent.getNewValue().toString());
}

public void localeChangeListener(ActionEvent actionEvent) {
changeLocale(this.language);
}

public void setLanguage(String language) {
System.out.println("setLanguage "+language);

this.language = language;
}

public String getLanguage() {
return language;
}
}

Here an example of a java resourcebundle.

package nl.whitehorses.locale.view;

import java.util.ListResourceBundle;

public class JavaLocale_en extends ListResourceBundle {

public Object[][] getContents() {
return contents;
}
private Object[][] contents =
{ { "java_output", "java output" }
, { "java_output2","java output 2" }, };
}

Here you can download my 11g workspace

20 comments:

  1. Hello Edwin,
    First, I want to thank you ,it's very useful topic, I like your blog very mush,
    but may you explain how did you full the "label Choice list" with data

    Good Man
    Thanks
    Karim

    ReplyDelete
  2. Hi Karim,

    I made a method with has a language parameter and this returns the choices in the right language. ( if you use adf bc then you can add this to the application module ) .

    Now I made method action in ADF and on this a method iterarator and I use this iterator on the choicelist.

    and when I change the locale I refreshes this iterator by executing the method action.

    thanks Edwin

    ReplyDelete
  3. hai friends,
    i need to know does oracle adf support hebrew language ....
    help me out..

    ReplyDelete
  4. Hi

    I am sure Oracle supports hebrew.

    thanks Edwin

    ReplyDelete
  5. Hi Edwin,
    Your post working fine for me. Language and number group and dec separators i can set per session now. Could you also show how to work with date format if i want user to setup its own and different user in his sessions his own>
    Thanks in advance.

    Robert

    ReplyDelete
  6. Hi Edwin,
    Thanks for usefull blog.
    But i want to change Locale as user specific .I mean can i add Locale in session according to different user choice.Currently i did accoding to ur above steps but problem is, if oneof user changes the Locale it will apply to all other users.
    Can u guide me where i did a mistake.
    Thanks for all help.

    Jaydeep

    ReplyDelete
  7. Hi Robert,

    you can use af:inputDate or af:inputtext with a convertor af:convertDateTime pattern="dd-MM-yyyy" this pattern can be set in a backing bean.

    hope this helps

    thanks Edwin

    ReplyDelete
  8. Hi JayDeep.

    very strange the locale should only live in your session, just like the locale of your browser.

    Did you start a new browser or test one session in IE and one in firefox for example.

    Thanks Edwin

    ReplyDelete
  9. Hi Edwin ,
    when i tried to run the sample you gave i got this exception :
    *********************************************
    java.lang.ClassNotFoundException: oracle.adf.share.weblogic.listeners.ADFApplicationLifecycleListener
    at weblogic.utils.classloaders.GenericClassLoader.findLocalClass(GenericClassLoader.java:283)
    at weblogic.utils.classloaders.GenericClassLoader.findClass(GenericClassLoader.java:256)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:251)
    at weblogic.utils.classloaders.GenericClassLoader.loadClass(GenericClassLoader.java:176)


    *********************************************
    This issue seems to be known i tried this solution but it doesn't work also :(
    http://forums.oracle.com/forums/thread.jspa?threadID=837936&tstart=0


    thanks for your help

    ReplyDelete
  10. Hi,

    you can remove it from the weblogic-application.xml.

    this file is in your application/src/meta-inf folder

    thanks

    ReplyDelete
  11. Can I in ADF do the following (if so how) ?

    I want some input text boxes on the UI to be dependent on a language choice list. The style/fonts/local in which a user will enter data into these should be based upon the language selected. If user selects Chinese, he should be able to enter the data in those particular text boxes in chinese. Other things on the UI however should not change.
    Please help me with this.

    ReplyDelete
  12. Hi,

    I want some input text boxes on the UI to be dependent on a language choice list.

    that is possible, you can make a look and feel changer by making different css and register these in the trinidad files. and in these css you can define a chines font.

    hope this helps

    ReplyDelete
  13. Hi thanks for this useful topic
    i want to change "Struts Logic Tag Library" code (greaterEqual or others ....) to Farsi can u help me plz.

    ReplyDelete
  14. Hi,

    this is very nice article!! exactly what i was looking for.

    I have one more question, in my case locale is coming from a BPM process, so how can i set the default locale based on the value from BPM process before the page gets loaded.

    Thanks
    Apoorv

    ReplyDelete
    Replies
    1. Hi,

      it depends, sometimes it uses the browser preferences and off course you can always set the java language and country on the weblogic container. I think it is -Duser.country=us and -Duser.language=en

      thanks

      Delete
    2. No, actually the case is there is lang prefrence variable in the BPM object, so if the value is en for that i need to set the locale to en similarly for if the value is fr i need to set locale to fr.

      Thanks!!
      Apoorv

      Delete
  15. Hi Edwin,

    I have one question regarding the locale format.

    that is, we are using BPM process to get the data from PSFT, and where we want the direct binding data to be formatted based on the formatter provided.





    In the above case the converter is not applying, it displays as string instead of number/sal. But when I gave the value as "123445"(<af:outputText value="123445"), its able to convert to the currency able to see the formated value.

    Could you please let me know what changes need to be done to use as binding values as input and format to currency/number in ADF.

    Thanks in Adv,
    Shivaji

    ReplyDelete
  16. below is the sample code which is not working:

    <af:outputText value="#{bindings.empSal.inputValue}" id="ot29">
    <af:convertNumber type="currency"/>
    </af:outputText>

    ReplyDelete
  17. Hi,
    Just a big thank you very much for this post, helped me in my project at crucial points, while other posts failed to be understandable! :)
    Br,
    K.

    ReplyDelete