Making a web app to support multiple languages.
This is achieved in JSF2 by using message properties files (resource bundles) and load the correct key/value pair from these resource files, based on the current locale the web application is set.
One configuration is in the "faces-config.xml", to setup the default locale and the location of the messages files. JSF would load these resources once accessed.
Here's the relevant section in "faces-config.xml":
<application> <locale-config> <default-locale>en</default-locale> <supported-locale>zh</supported-locale> </locale-config> <resource-bundle> <base-name>com.jxee.messages</base-name> <var>msgs</var> </resource-bundle> </application>
Note: According to the above configuration, the message resource bundle should reside in this directory: "web_application_classpath_root/com/jxee". This means that a valid place would be "WEB-INF/classes/com/jxee". Since we are supporting English and Chinese in this example, two resource files should be created, one for English and one for Chinese.
The following is the proejct directory screen shot for these two files:
The JSF pages need to set the current locale when rendering the pages. This can be done by the following code ( Since every page needs to set this property, the template is a good place to put it):
<f:view locale="#{langBean.appLocale}">
This example enables the web application user to choose the language, by using a Primefaces context menu. The backing bean of the context menu would set the current local for the web application.
Here's the context menu soruce (again, the template is a good place to include it):
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:p="http://primefaces.org/ui"> <h:form> <p:contextMenu> <p:menuitem value="#{msgs.english}" actionListener="#{langBean.change2English}" rendered="#{langBean.appLocale.language != 'en'}" immediate="true" ajax="false"/> <p:menuitem value="#{msgs.chinese}" action="#{langBean.change2Chinese}" rendered="#{langBean.appLocale.language != 'zh'}" immediate="true" ajax="false"/> <p:separator/> <p:menuitem value="Primefaces Homepage" url="http://www.primefaces.org"/> </p:contextMenu> </h:form> </html>
One thing need to mention in the above code is that since we use Primefaces tags, we need to set attribute ajax="false" , in order to make the form submitted and re-rendered, after the backing bean change the current locale for the whole application. Otherwise it would only change the labels with a refresh/redirect.
Here's the backing bean for the above context menu to change the application locale:
package com.jxee.action; import java.io.Serializable; import java.util.Locale; import javax.annotation.PostConstruct; import javax.faces.bean.ManagedBean; import javax.faces.bean.SessionScoped; import javax.faces.context.FacesContext; import javax.faces.event.ActionEvent; import org.apache.log4j.Logger; @SessionScoped @ManagedBean(name="langBean") public class LocaleBean implements Serializable{ private static Logger log = Logger.getLogger(LocaleBean.class); private Locale appLocale = Locale.ENGLISH; @PostConstruct public void init() { FacesContext.getCurrentInstance().getViewRoot().setLocale(this.appLocale); log.debug(">>> locale inited to: " + this.appLocale.getLanguage()); } public Locale getAppLocale() { return appLocale; } public void setAppLocale(Locale appLocale) { this.appLocale = appLocale; } // as an action listener public void change2English(ActionEvent e) { this.appLocale = Locale.ENGLISH; FacesContext.getCurrentInstance().getViewRoot().setLocale(this.appLocale); log.debug(">>> locale changed to English: " + this.appLocale.getLanguage()); } // as an action method public String change2Chinese() { this.appLocale = Locale.CHINESE; FacesContext.getCurrentInstance().getViewRoot().setLocale(this.appLocale); log.debug(">>> locale changed to Chinese: " + this.appLocale.getLanguage()); return null; } }
OK, looks like everything is setup. Now just need to change the "/template/template1.xhtml" to include the <f:view locale="..."/> and the language chooser context menu. Then in the JSF page sources, we need to change very hard coded labels to something like "#{msgs.label_key_name}".
Here's the updated template1.xhtml:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:p="http://primefaces.org/ui"> <f:view locale="#{langBean.appLocale}"> <h:head> <title> <ui:insert name="title">Title</ui:insert> </title> </h:head> <h:body> <ui:insert name="menu"> <ui:include src="menubar.xhtml"/> <ui:include src="contextmenu.xhtml"/> </ui:insert> <p:spacer height="20"/> <ui:insert name="content"/> </h:body> </f:view> </html>
The updated JSF page "/tst/testSingleton.xhtml" (also updated main menu and studentSearch.xhtml):
<ui:composition xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:p="http://primefaces.org/ui" template="/template/template1.xhtml"> <ui:define name="title">Test EJB3.1 @Singleton</ui:define> <ui:define name="content"> <h:form> <p:panel header="#{msgs.testSingletonHeader}" toggleable="true" style="width:60%"> <h:panelGrid columns="1"> Click "Test" to see if it's the same instance: <h:outputText id="out" value="#{st.message}" escape="false"/> </h:panelGrid> <p:separator/> <p:commandButton value="#{msgs['test']}" action="#{st.test}" update="out"/> <p:spacer width="7"/> <p:commandButton value="#{msgs.clear}" actionListener="#{st.reset}" update="out"/> </p:panel> </h:form> </ui:define> </ui:composition>
Here's the "com.jxee.messages_zh.properties" (with Eclipse 3.7 "Indigo", it translated to unicode while i inputing Chinese, that cool! Othewise, you might want use JDK tool "native2ascii" to translate the Chinese characters to unicode):
### main menu labels student=\u5B66\u751F studentSearch=\u5B66\u751F\u67E5\u8BE2 studentNew=\u8F93\u5165\u65B0\u751F blah=blah and blah ajaxTest=Ajax \u8BD5\u9A8C getParamTest=Get Param \u8BD5\u9A8C ejbTest=EJB \u8BD5\u9A8C asyncEjbTest=Asnyc EJB \u8BD5\u9A8C singletonEjbTest=Singleton EJB \u8BD5\u9A8C username=\u7528\u6237\u540D password=\u53E3\u4EE4 login=\u829D\u9EBB\u5F00\u95E8 logout=\u79BB\u5F00 search=\u67E5\u8BE2 ### context menu labels: for Chinese locale, shows English labels english=English chinese=Chinese ### test Singleton ejb labels test=\u8BD5\u9A8C clear=\u518D\u6765\u4E00\u904D\u5E7F\u64AD\u4F53\u64CD (-; testSingletonHeader=\u8BD5\u9A8C EJB3.1 @Singleton ### student search screen name=\u59D3\u540D mobile=\u624B\u673A createdDate=\u8F93\u5165\u65E5\u671F studentFound=\u67E5\u8BE2\u5230\u7684\u5B66\u751F addNewStudent=\u6DFB\u52A0\u5B66\u751F
Now take a look at what we've got:
ss1: The language chooser context menu, before switching to Chinese:
ss2: The language chooser context menu, after switched to Chinese:
ss3: The i18Ned screen "/student/studentSearch.jsf":
not too bad!