Seam FrameworkCommunity Documentation

Chapter 17. Internationalization, localization and themes

17.1. Internationalizing your app
17.1.1. Application server configuration
17.1.2. Translated application strings
17.1.3. Other encoding settings
17.2. Locales
17.3. Labels
17.3.1. Defining labels
17.3.2. Displaying labels
17.3.3. Faces messages
17.4. Timezones
17.5. Themes
17.6. Persisting locale and theme preferences via cookies

Seam makes it easy to build internationalized applications. First, let's walk through all the stages needed to internationalize and localize your app. Then we'll take a look at the components Seam bundles.

A JEE application consists of many components and all of them must be configured properly for your application to be localized.

Starting at the bottom, the first step is to ensure that your database server and client is using the correct character encoding for your locale. Normally you'll want to use UTF-8. How to do this is outside the scope of this tutorial.

Each user login session has an associated instance of java.util.Locale (available to the application as a component named locale). Under normal circumstances, you won't need to do any special configuration to set the locale. Seam just delegates to JSF to determine the active locale:

It is possible to set the locale manually via the Seam configuration properties org.jboss.seam.international.localeSelector.language, org.jboss.seam.international.localeSelector.country and org.jboss.seam.international.localeSelector.variant, but we can't think of any good reason to ever do this.

It is, however, useful to allow the user to set the locale manually via the application user interface. Seam provides built-in functionality for overriding the locale determined by the algorithm above. All you have to do is add the following fragment to a form in your JSP or Facelets page:


<h:selectOneMenu value="#{localeSelector.language}">
    <f:selectItem itemLabel="English" itemValue="en"/>
    <f:selectItem itemLabel="Deutsch" itemValue="de"/>
    <f:selectItem itemLabel="Francais" itemValue="fr"/>
</h:selectOneMenu>
<h:commandButton action="#{localeSelector.select}"
    value="#{messages['ChangeLanguage']}"/>

Or, if you want a list of all supported locales from faces-config.xml, just use:


<h:selectOneMenu value="#{localeSelector.localeString}">
    <f:selectItems value="#{localeSelector.supportedLocales}"/>
</h:selectOneMenu>
<h:commandButton action="#{localeSelector.select}"
    value="#{messages['ChangeLanguage']}"/>

When the user selects an item from the drop-down, then clicks the command button, the Seam and JSF locales will be overridden for the rest of the session.

The brings us to the question of where the supported locales are defined. Typically, you provide a list of locales for which you have matching resource bundles in the <locale-config> element of the JSF configuration file (/META-INF/faces-config.xml). However, you have learned to appreciate that Seam's component configuration mechanism is more powerful than what is provided in Java EE. For that reason, you can configure the supported locales, and the default locale of the server, using the built-in component named org.jboss.seam.international.localeConfig. To use it, you first declare an XML namespace for Seam's international package in the Seam component descriptor. You then define the default locale and supported locales as follows:


<international:locale-config default-locale="fr_CA" supported-locales="en fr_CA fr_FR"/>

Naturally, if you pronounce that you support a locale, you better provide a resource bundle to match it! Up next, you'll learn how to define the language-specific labels.

JSF supports internationalization of user interface labels and descriptive text via the use of <f:loadBundle />. You can use this approach in Seam applications. Alternatively, you can take advantage of the Seam messages component to display templated labels with embedded EL expressions.

Seam provides a java.util.ResourceBundle (available to the application as a org.jboss.seam.core.resourceBundle). You'll need to make your internationalized labels available via this special resource bundle. By default, the resource bundle used by Seam is named messages and so you'll need to define your labels in files named messages.properties, messages_en.properties, messages_en_AU.properties, etc. These files usually belong in the WEB-INF/classes directory.

So, in messages_en.properties:

Hello=Hello

And in messages_en_AU.properties:

Hello=G'day

You can select a different name for the resource bundle by setting the Seam configuration property named org.jboss.seam.core.resourceLoader.bundleNames. You can even specify a list of resource bundle names to be searched (depth first) for messages.


<core:resource-loader>
    <core:bundle-names>
        <value>mycompany_messages</value>
        <value>standard_messages</value>       
    </core:bundle-names>
</core:resource-loader>

If you want to define a message just for a particular page, you can specify it in a resource bundle with the same name as the JSF view id, with the leading / and trailing file extension removed. So we could put our message in welcome/hello_en.properties if we only needed to display the message on /welcome/hello.jsp.

You can even specify an explicit bundle name in pages.xml:


<page view-id="/welcome/hello.jsp" bundle="HelloMessages"/>

Then we could use messages defined in HelloMessages.properties on /welcome/hello.jsp.

There is also a session-scoped instance of java.util.Timezone, named org.jboss.seam.international.timezone, and a Seam component for changing the timezone named org.jboss.seam.international.timezoneSelector. By default, the timezone is the default timezone of the server. Unfortunately, the JSF specification says that all dates and times should be assumed to be UTC, and displayed as UTC, unless a timezone is explicitly specified using <f:convertDateTime>. This is an extremely inconvenient default behavior.

Seam overrides this behavior, and defaults all dates and times to the Seam timezone.

Seam also provides a default date converter to convert a string value to a date. This saves you from having to specify a converter on input fields that are simply capturing a date. The pattern is selected according the the user's locale and the time zone is selected as described above.

Seam applications are also very easily skinnable. The theme API is very similar to the localization API, but of course these two concerns are orthogonal, and some applications support both localization and themes.

First, configure the set of supported themes:


<theme:theme-selector cookie-enabled="true">
    <theme:available-themes>
        <value>default</value>
        <value>accessible</value>
        <value>printable</value>
    </theme:available-themes>
</theme:theme-selector>

Note that the first theme listed is the default theme.

Themes are defined in a properties file with the same name as the theme. For example, the default theme is defined as a set of entries in default.properties. For example, default.properties might define:

css ../screen.css
template /template.xhtml

Usually the entries in a theme resource bundle will be paths to CSS styles or images and names of facelets templates (unlike localization resource bundles which are usually text).

Now we can use these entries in our JSP or facelets pages. For example, to theme the stylesheet in a facelets page:


<link href="#{theme.css}" rel="stylesheet" type="text/css" />

Or, when the page definition resides in a subdirectory:


<link href="#{facesContext.externalContext.requestContextPath}#{theme.css}" 
    rel="stylesheet" type="text/css" />

Most powerfully, facelets lets us theme the template used by a <ui:composition>:


<ui:composition xmlns="http://www.w3.org/1999/xhtml"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
    template="#{theme.template}">

Just like the locale selector, there is a built-in theme selector to allow the user to freely switch themes:


<h:selectOneMenu value="#{themeSelector.theme}">
    <f:selectItems value="#{themeSelector.themes}"/>
</h:selectOneMenu>
<h:commandButton action="#{themeSelector.select}" value="Select Theme"/>

The locale selector, theme selector and timezone selector all support persistence of locale and theme preference to a cookie. Simply set the cookie-enabled property in components.xml:


<theme:theme-selector cookie-enabled="true">
    <theme:available-themes>
        <value>default</value>
        <value>accessible</value>
        <value>printable</value>
    </theme:available-themes>
</theme:theme-selector>

<international:locale-selector cookie-enabled="true"/>