Saturday 1 August 2009

JSF: a custom format panel control for localising component layout

This post describes a custom JavaServer Faces component for controlling the flow layout of controls based on localised strings.

The h:outputFormat component

The h:outputFormat control is a standard JSF control for formatting strings. It is often used with resource bundles.

This is a faces-config.xml declaration for adding two languages to a JSF application and defining a resource bundle resources/messages.properties.

  <application>
    <locale-config>
      <default-locale>en</default-locale>
      <supported-locale>fr</supported-locale>
    </locale-config>
    <message-bundle>resources.messages</message-bundle>
  </application>

The bundle provides the following value:

staticCalc={0} divided by {1} is {2,number,#0.000}

By loading the resource bundle in a view, we can insert parameter values into the string using h:outputFormat:

<h:outputFormat value="#{bundle.staticCalc}">
  <f:param value="1" />
  <f:param value="3" />
  <f:param value="#{1 div 3}" />
</h:outputFormat>
A rendered outputFormat component

This is great, but h:outputFormat only accepts parameters as children.

Introducing the i18n:formatPanel custom component

Lets say we want to build something like this in JSF:

A translatable string containing HTML form elements

This is straightforward enough. But we have a problem when it comes to localisation. Grammatical differences between languages may require the controls to appear in any order, depending on the user's Locale. The simplest approach is to redesign the dialog to tabular form. The old-school approach would be to do a separate version for each language, but this is crude and repetitive. We need a custom control.

You can find information on writing JSF controls in the Java EE 5 Tutorial.

The custom control is made up of the following artifacts:

  • HtmlFormatPanel. A concrete component. This bean-like class allows programmatic access to properties in backing beans. The core logic of the control (a panel) already exists in the API, so the class just extends UIPanel.
  • FormatPanelRenderer. The renderer. This emits the HTML and controls how child components are rendered.
  • META-INF/faces-config.xml. The file for registering the component with the JSF implementation.
  • FormatPanelTag. The JSP custom tag implementation.
  • META-INF/i18n.tld. The tag library definition file for use with JSPs.
  • META-INF/i18n.taglib.xml. The tag library for Facelets, an alternative view technology to JSPs that is included as standard in JSF 2.0.

This component works much like h:outputFormat, but accepts a wider range of children.

Using the component

To demonstrate the component, a new string resource is added to messages.properties:

#the translatable string form behind a formatPanel
activeCalc={0} divided by {1} {2} {3,number,#0.000}

Also, a translated version of the file (messages_fr.properties) is added where the sentence structure has changed:

#Let's pretend this is French
activeCalc=Sacr\u00E9 Bleu! {3,number,#0.000} {2} {1} by divided {0}

In order to use the component in a page, the tag library must be declared. (The exact form will vary depending the view technology you are using; see the existing JSF declarations for examples.)

xmlns:i18n="http://illegalargumentexception.googlecode.com/i18n"

Here, the sentence is built using a mixture of input components, output components and parameters combined with the string from the resource bundle:

<i18n:formatPanel formatString="#{bundle.activeCalc}">
  <h:inputText value="#{numberBean.a}" />
  <h:inputText value="#{numberBean.b}" />
  <h:commandButton action="#{numberBean.calculate}" value="equals" />
  <f:param value="#{numberBean.result}" />
</i18n:formatPanel>

The order of the children corresponds to the order of the parameters in the resource string.

formatPanel with English layout

The component as it appears when the browser language preference is set to French:

formatPanel component with French layout

Sources

All the sources are available in a public Subversion repository.

Repository: http://illegalargumentexception.googlecode.com/svn/trunk/code/java/
License: MIT
Project: JsfFormatPanel

You can download a binary version here: jsfFormatPanel_1.0.zip.

Notes

This code was cursorily tested using JSPs on JBoss 4.2.2 (JSF 1.2) and Facelets on Glassfish 2.1 (upgraded to use JSF 2.0).

No doubt a similar control has already been implemented for a commercial or free component library. If you know of one, go add an answer to the stackoverflow.com question that inspired this post. Such a control is likely to have undergone more testing than I have put into this one.

No comments:

Post a Comment

All comments are moderated