SeamFramework.orgCommunity Documentation
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.
To ensure that the application server receives the request
parameters in the correct encoding from client requests you have to
configure the tomcat connector. If you use Tomcat or JBoss AS, add
the URIEncoding="UTF-8"
attribute to the
connector configuration. For JBoss AS 4.2 change
${JBOSS_HOME}/server/(default)/deploy/jboss-web.deployer/server.xml
:
<Connector port="8080" URIEncoding="UTF-8"/>
There is alternative which is probably better. You can tell JBoss AS that the encoding for the request parameters will be taken from the request:
<Connector port="8080" useBodyEncodingForURI="true"/>
You'll also need localized strings for all the messages in your application (for example field labels on your views). First you need to ensure that your resource bundle is encoded using the desired character encoding. By default ASCII is used. Although ASCII is enough for many languages, it doesn't provide characters for all languages.
Resource bundles must be created in ASCII, or use Unicode escape codes to represent Unicode characters. Since you don't compile a property file to byte code, there is no way to tell the JVM which character set to use. So you must use either ASCII characters or escape characters not in the ASCII character set. You can represent a Unicode character in any Java file using \uXXXX, where XXXX is the hexidecimal representation of the character.
You can write your translation of labels
(Section 16.3, “Labels”) to your messages resource
bundle in the native encoding and then convert the content of the
file into the escaped format through the tool native2ascii
provided in the JDK. This tool will convert a file written in your
native encoding to one that represents non-ASCII characters as
Unicode escape sequences.
Usage of this tool is described here for Java 5 or here for Java 6. For example, to convert a file from UTF-8:
$ native2ascii -encoding UTF-8 messages_cs.properties > messages_cs_escaped.properties
We need to make sure that the view displays your localized data and messages using the correct character set and also any data submitted uses the correct encoding.
To set the display character encoding, you need to use the
<f:view locale="cs_CZ"/>
tag (here we tell
JSF to use the Czech locale). You may want to change the encoding of
the xml document itself if you want to embed localized strings in the
xml. To do this alter the encoding attribute in xml declaration
<?xml version="1.0" encoding="UTF-8"?>
as
required.
Also JSF/Facelets should submit any requests using the specified
character encoding, but to make sure any requests that don't specify
an encoding you can force the request encoding using a servlet
filter. Configure this in components.xml
:
<web:character-encoding-filter encoding="UTF-8"
override-client="true"
url-pattern="*.seam" />
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:
If there is a locale associated with the HTTP request (the
browser locale), and that locale is in the list of supported locales
from faces-config.xml
, use that locale for the rest
of the session.
Otherwise, if a default locale was specified in the
faces-config.xml
, use that locale for the rest of
the session.
Otherwise, use the default locale of the server.
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
.
If you define your labels using the Seam resource bundle, you'll
be able to use them without having to type <f:loadBundle
... />
on every page. Instead, you can simply type:
<h:outputText value="#{messages['Hello']}"/>
or:
<h:outputText value="#{messages.Hello}"/>
Even better, the messages themselves may contain EL expressions:
Hello=Hello, #{user.firstName} #{user.lastName}
Hello=G'day, #{user.firstName}
You can even use the messages in your code:
@In private Map<String, String> messages;
@In("#{messages['Hello']}") private String helloMessage;
The facesMessages
component is a
super-convenient way to display success or failure messages to the user.
The functionality we just described also works for faces
messages:
@Name("hello")
@Stateless
public class HelloBean implements Hello {
@In FacesMessages facesMessages;
public String sayIt() {
facesMessages.addFromResourceBundle("Hello");
}
}
This will display Hello, Gavin King
or
G'day, Gavin
, depending upon the user's
locale.
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. In addition, Seam provides the
<s:convertDateTime>
tag which always performs conversions
in 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"/>