Internationalization in ICEfaces

Table of Contents

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 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.



Development Tools Used

The following tools were used to create the project.

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)

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.

3. Setup faces-config.xml Locales

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

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>

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.

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:

messages_en.properties Contents
firstName=First Name
lastName=Last Name

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:

messages_de.properties Contents
firstName=Vorname
lastName=Nachname

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:

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>

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.

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:

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()));
		}
	}
}

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:

Get Current Locale from ViewRoot
FacesContext.getCurrentInstance().getViewRoot().getLocale()

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:

Changing the Locale via ViewRoot
FacesContext.getCurrentInstance().getViewRoot().setLocale(localeToSet);

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:

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:

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.


Tutorial Source Code Downloads

Example Source Notes
Internationalization in ICEfaces project internationalization source code Basic example project demonstrating how to use internationalization in a JSF or ICEfaces application.
Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.

© Copyright 2021 ICEsoft Technologies Canada Corp.