View Source

h1. Internationalization in ICEfaces Tutorial

Internationalization is a common requirement of modern web applications, and is simple to achieve in JSF and ICEfaces. Supported languages can be specified and dynamically changed by page markup or from the backend. In addition JSF will automatically detect the locale of a client web browser and apply it if the project is configured properly.

This tutorial assumes the reader has an understanding of JSF, ICEfaces, and [Java i18n|http://java.sun.com/javase/technologies/core/basic/intl/] and creating and working with projects related to those technologies.

The goal of this tutorial is to create a basic single page ICEfaces 2.0 project that supports English (en) and German (de) and has a few component labels that are internationalized. We will then create a basic locale switcher to let a user choose between the two languages.
\\
\\
----
{panel}Here is the entire list of steps worked through during this tutorial:

# [Make the internationalization Project|#step1]
# [Add ICEfaces|#step2]
# [Setup faces-config.xml Locales|#step3]
# [Create Resource Bundles|#step4]
# [Create main.xhtml|#step5]
# [Create PageBean.java|#step6]
# [Deploy and Test the Application|#step7]
* [Tutorial Source Code Downloads|#downloads]
{panel}
----

h3. Development Tools Used

The following tools were used to create the project.
* [Eclipse|http://eclipse.org/downloads/packages/eclipse-ide-java-ee-developers/heliossr1] IDE for Java EE Developers - Version Helios
* [Tomcat 7.x|http://tomcat.apache.org/download-70.cgi] Web Server
* [Java 6.x|http://www.oracle.com/technetwork/java/javase/downloads/]
* ICEfaces 2

h3. {anchor:step1}1. Make the {{internationalization}} Project

* Using Eclipse create a new Dynamic Web Project called {{internationalization}}
** Target runtime: Apache Tomcat v7.0
** Dynamic web module version: 3.0
** Configuration: JavaServer Faces v2.0 Project (Mojarra)

h3. {anchor:step2}2. Add ICEfaces

Add the {{icefaces.jar}} to your project from the ICEfaces 2 bundle. This can be added to the project through a custom User Library or by putting it into {{internationalization/WEB-INF/lib/}}. The approach doesn't matter as long as the jar is included in the deployed {{war}} file.

h3. {anchor:step3}3. Setup {{faces-config.xml}} Locales

Open {{faces-config.xml}} and paste in the following application node:

{code:title=faces-config.xml application locales}
<application>
<locale-config>
<default-locale>en</default-locale>
<supported-locale>de</supported-locale>
</locale-config>

<resource-bundle>
<base-name>
org.icefaces.tutorial.internationalization.resources.messages
</base-name>
<var>msgs</var>
</resource-bundle>
</application>
{code}

This code performs two tasks related to internationalization. First we specify our default locale (English [en]) and our supported locale(s), in this case only German [de]. If we wanted to add more locales we could specify further {{supported-locale}} nodes.

The second piece of code tells our application where the internationalized resource bundle is. The {{base-name}} points to a series of property files that we'll create in Step 4. The {{var}} is the name used to access the messages from our page markup.

h3. {anchor:step4}4. Create Resource Bundles

Create two basic files in the package {org.icefaces.tutorials.internationalization.resources}}:

* messages_en.properties
* messages_de.properties

These property files were specified in {{faces-config.xml}}, and will use a key/value format. The key will be used in the page markup, and the value will be the associated label for the language specified in the filename.

Paste the following key/value pairs into the {{messages_en.properties}} file:

{code:title=messages_en.properties Contents}
firstName=First Name
lastName=Last Name
{code}

As you can see when we reference "firstName" in the page markup, we will get the value "First Name" returned.

Similary paste the German version of these values into the {{messages_de.properties}} file:

{code:title=messages_de.properties Contents}
firstName=Vorname
lastName=Nachname
{code}

h3. {anchor:step5}5. Create {{main.xhtml}}

Now we will create a simple page that will use the internationalized strings. Create a new page called {{main.xhtml}} and paste the code below:

{code:title=main.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">
<f:view locale="#{pageBean.currentLocale}">
<h:head>
<title>Internationalization - Main</title>
</h:head>
<h:body>
<h:form>
<h:panelGrid columns="2">
<h:selectOneMenu value="#{pageBean.dropdownItem}"
valueChangeListener="#{pageBean.localeChanged}">
<f:selectItems value="#{pageBean.availableLocales}"/>
</h:selectOneMenu>
<h:commandButton value="Change Locale"/>

<h:outputLabel for="firstName" value="#{msgs.firstName}"/>
<h:inputText id="firstName"/>

<h:outputLabel for="lastName" value="#{msgs.lastName}"/>
<h:inputText id="lastName"/>
</h:panelGrid>
</h:form>
</h:body>
</f:view>
</html>
{code}

The {{<h:selectOneMenu>}} will be managed through our upcoming backing bean. The component will be used to allow users to dynamically switch their locale for the entire app.

Notice that the {{<h:outputLabel}} value attributes are internationalized. We use the {{var}} {{msgs}} that was previously declared in {{faces-config.xml}}, and a key that corresponds to the resource bundle. So {{#\{msgs.firstName\}}} will correspond to "First Name" in the English locale, or "Vorname" in the German locale.

We manually specify the current locale in the {{<f:view>}} container, via the {{locale}} attribute. This allows us to modify that variable and have it apply properly to the entire page.

h3. {anchor:step6}6. Create {{PageBean.java}}

Create a new Java class file called {{PageBean}} in the package {{org.icefaces.tutorial.internationalization.beans}} and paste the code below:

{code:title=PageBean.java}
package org.icefaces.tutorial.internationalization.beans;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.context.FacesContext;
import javax.faces.event.ValueChangeEvent;
import javax.faces.model.SelectItem;

@ManagedBean(name="pageBean")
@SessionScoped
public class PageBean implements Serializable {
private List<SelectItem> availableLocales;
private Locale currentLocale;
private String dropdownItem;

public PageBean() {
setCurrentLocale(FacesContext.getCurrentInstance().getViewRoot().getLocale());

dropdownItem = currentLocale.getLanguage();
}

private void generateAvailableLocales() {
availableLocales = new ArrayList<SelectItem>(0);

// Add the default locale
availableLocales.add
(makeLocaleItem(FacesContext.getCurrentInstance().getApplication().getDefaultLocale()));

// Add any other supported locales
for (Iterator<Locale> iter =
FacesContext.getCurrentInstance().getApplication().getSupportedLocales();
iter.hasNext();) {
availableLocales.add(makeLocaleItem(iter.next()));
}
}

private SelectItem makeLocaleItem(Locale toWrap) {
if (toWrap != null) {
return new SelectItem(toWrap.getLanguage(),
toWrap.getDisplayName());
}

return null;
}

public List<SelectItem> getAvailableLocales() {
if (availableLocales == null) {
generateAvailableLocales();
}

return availableLocales;
}

public void setAvailableLocales(List<SelectItem> availableLocales) {
this.availableLocales = availableLocales;
}

public Locale getCurrentLocale() {
return currentLocale;
}

public void setCurrentLocale(Locale currentLocale) {
this.currentLocale = currentLocale;
}

public String getDropdownItem() {
return dropdownItem;
}

public void setDropdownItem(String dropdownItem) {
this.dropdownItem = dropdownItem;
}

public void applyLocale(Locale toApply) {
System.out.println("Apply Locale: " +
toApply.getDisplayName() + " (" + toApply.getLanguage() + ")");

setCurrentLocale(toApply);

FacesContext.getCurrentInstance().getViewRoot().setLocale(toApply);
}

public void localeChanged(ValueChangeEvent event) {
if (event.getNewValue() != null) {
applyLocale(new Locale(event.getNewValue().toString()));
}
}
}
{code}

The bean code is fairly long, but has a few important concepts to learn.

The {{currentLocale}} variable is what we use to track what {{java.util.Locale}} JSF detected or the user has selected. The initial status is set through the constructor by getting the current locale from the ViewRoot (which corresponds to {{<f:view>}} in the page), using the code:

{code:title=Get Current Locale from ViewRoot}
FacesContext.getCurrentInstance().getViewRoot().getLocale()
{code}

The {{availableLocales}} list is used to populate the {{<h:selectOneMenu>}} on the page. Inside the {{generateAvailableLocales}} method we use FacesContext to retrieve the default locale (from the {{<default-locale>}} value in {{faces-config.xml}}) and all supported locales (from the {{<supported-locale}} value(s) in {{facets-config.xml}}). Then we wrap those values in {{javax.faces.model.SelectItem}} objects so the page can understand them and use them properly in the {{<h:selectOneMenu>}} component.

When the locale is changed via the page level dropdown, we use the {{localeChanged}} value change listener method to apply the new locale to the page. Applying the locale is achieved through the {{ViewRoot}}, as such:

{code:title=Changing the Locale via ViewRoot}
FacesContext.getCurrentInstance().getViewRoot().setLocale(localeToSet);
{code}

h3. {anchor:step7}7. Deploy and Test the Application

Now that all necessary files have been created, build and deploy the web application {{internationalization}} and visit it in a web browser. The page will look similar to the following:
!internationalization-tutorial-screenshot.png!

Selecting a different Locale and clicking the "Change Locale" button will modify the two field labels, based on the contents of our messages.properties resource bundles.

If you change your web browser locale to English (en) or German (de), restart the browser, and revisit the page it will automatically detect and apply the proper locale. For example, adding a locale in Firefox:
!internationalization-browser-screenshot.png!
After this change returning to the {{internationalization}} application would automatically set German as the page locale through JSF.

As you can see internationalization is both easy to setup and powerful to use. Resource bundles can also be access from the bean level, and default JSF error messages can be overridden, but that is beyond the scope of this tutorial.

----
h3. {anchor:downloads}Tutorial Source Code Downloads

|| Example || Source || Notes ||
| Internationalization in ICEfaces project |[internationalization source code |^internationalization.zip|Download Source Code]| Basic example project demonstrating how to use internationalization in a JSF or ICEfaces application. |