Language localisation in JSF and Seam
This article introduces the mechanisms for internationalising a web application built using JavaServer Faces (JSF) application and the Seam Framework for language localisation (L10N), expanding on the brief introduction in the Seam manual, Chapter 16. Internationalization, localization and themes.
Simple labels
In the most simple case, as described in the Seam manual, you define a user-interface text labels in a properties file for each localisation.
# messages.properties - English localisation
order = Order
# messages_fr.properties - French localisation
order = Commande
Then display the label in a Facelets view using an Expression Language
(EL) expression that uses the messages
Seam component.
<h1>#{messages.order}</h1>
This syntax works, because the Messages
component exposes a
java.util.Map
interface, keyed on the message key, which means that
the EL expression above references the message for the Map key orders
.
Dots in message keys
In practice, it is common to define hundreds or thousands of messages for an application user-interface, and to use a dot-hierarchy notation for the message keys, to organise them. For example:
model.Order = Order
model.Order.carrierReference = Carrier reference
model.Order.carrierReference.help = e.g. package tracking number.
model.Order.deliveryAddress = Delivery address
model.Order.dueDate = Due date
In this case, an expression like {messages.model.Order
} will no
longer work: this is parsed as a reference to an Order
property on the
{messages.model
} map entry. If you try this you get the error:
javax.el.ELException: /example.xhtml: Property 'Order' not found on type java.lang.String
Instead, you have to use the more explicit Map look-up syntax:
<h1>#{messages['model.Order']}</h1>
So far, this is pretty neat, in both senses of the word.
Placeholders in messages
In the application we are currently building, about a quarter of the messages contain a placeholder, whose value is inserted dynamically. For example:
# {0} date format, e.g. yyyy-MM-dd
action.order.orderDate.invalid=Order date must be in {0} format
You can replace the placeholder, as per java.text.MessageFormat
, by
using the JSF h:outputFormat
tag. Unfortuntely, this is somewhat
verbose:
<h:outputFormat value="#{messages['action.order.orderDate.invalid']}">
<!-- Insert the application date format from the configuration. -->
<f:param value="#{configuration.dateFormat}"/>
</h:outputFormat>
Note that the outputFormat
tag works independently of the message
look-up mechanism, so you could insert the message directly in its
value
attribute. For example, you can output the current year in a
Facelets view by using the MessageFormat
date format style to format
the Seam currentDatetime
component:
<!-- Output the current year -->
<h:outputFormat value="{0,date,yyyy}">
<f:param value="#{currentDatetime}"/>
</h:outputFormat>
Things you cannot do in JSF
Sooner or later, you are going to want to do one of the following things.
-
Output a localised label containing a placeholder in an attribute value in a Facelets view, e.g. my name in the link text of
<h:commandLink action="#{action.notify}" value="Notify Peter"/>
-
Use a JSF formatter, such as a date formatter, to format a value that will be inserted into a message placeholder.
-
Insert a JSF component, such as an
h:commandLink
into a message placeholder.
Unfortunately, these are not currently possible with the facilities in JSF, Seam and Facelets. You can either vote for javaserverfaces-spec-public issue 517 and wait for a new version of JSF 2, or you can write your own implementations.