Phil Webb's Blog

Random thoughts from a software developer

Integrating Spring & JavaServer Faces : Internationalization and Localization

with 10 comments

If you are working on a JSF application that is targeted to multiple languages, you may well be familiar with the <f:loadBundle> tag. Even if your application does not support internationalization using message bundles is still probably a good idea. Under the hood the <f:loadBundle> tag reads messages from a Java java.util.ResourceBundle and, whilst this will work, Spring developers often prefer the org.springframework.context.MessageSource interface.

As an alternative to <f:loadBundle> I have been developing a new <s:messageSource> component that can be used to expose messages from any Spring MessageSource, as well as offering a few other advantages.

The new component is a drop-in replacement for <f:loadBundle>.

<s:messageSource source="#{messageSource}" var="messages"/>
<p>
  <h:outputText value="#{messages.hello}"/>
</p>

The source attribute can be any EL expression that resolves to a MessageSource instance. If the source is not specified the Spring ApplicationContext will be used. The var attribute is the name of the variable that will be used to access the messages.

Unlike standard JSF, the key of the message to load will be built from the ID of the page being rendered. For example, assuming the page above is from the file WEB-INF/pages/messages/simple.xhtml, the key used to load the hello message will be pages.messages.simple.hello. Using these compound keys prevents message key clashes and keeps the page mark-up nice and concise. You can use the prefix attribute to override this behaviour if you need to.

If you make reference to message in your XHTML that you have forgotten to define you will either see a warning message (when in development) or an exception will be thrown (when in production).

As with standard JSF, your messages and include place-holders for use with <h:outputFormat>

pages.message.simple.welcome=Welcome to {1} with {0}
<h:outputFormat value="#{messages.welcome}">
  <f:param value="Spring"/>
  <f:param value="JSF"/>
</h:outputFormat>

The <h:outputFormat> tag is a little bit verbose, so for convenience, Spring messages can be used as Maps. This allows you to reference place-holders in a much more concise way:

<h:outputText value="#{messages.welcome['Spring']['JSF']}"/>

The same syntax allows you to map Java objects to messages. By default objects are mapped by building a message key from class name. For example, the following class:

package org.example;
public class ExampleObject {
}

Can be referenced in JSF:

<h:outputText value="#{messages[exampleInstance]}"/>

Resolving to the following message:

org.example.ExampleObject=example

For enum objects the message key includes the enum name as well as the class:

package org.example;
public enum ExampleObject {
  ONE, //mapped to message key org.example.ExampleObject.ONE
  TWO  //mapped to message key org.example.ExampleObject.TWO
}

Object messages can also make reference to properties that should form part of the message:

org.example.PersonName=Name is {first} {last}
...

package org.example;
public class PersonName {
  ...
  public String getFirst() {...}
  public String getLast() {...}
}

You can also define your own object message strategies by using a message source that implements the org.springframework.springfaces.message.ObjectMessageSource interface.

If you want to check out any of this code take a look at the org.springframework.springfaces.message and org.springframework.springfaces.message.ui packages from the GitHub Project.

About these ads

Written by Phillip Webb

November 15, 2011 at 5:55 pm

10 Responses

Subscribe to comments with RSS.

  1. Hi,
    I just found your project in github. What IDE do you use? Can you please suggest steps with which I can work your code (assuming starting with fresh ubuntu installation)?. No need to elaborate on howto install things, just list of required things (e.g. install xxx + yyy + zzz and import project to kkk).
    Thanks in advance!
    Yoav

    yoav

    January 12, 2012 at 2:19 pm

    • Hi Yoav,

      I run Ubuntu at home, from a fresh installation this is what you need to do:

      1) Install Oracle Java 7

      I don’t think that this version is currently shipped with Ubuntu or in any Ubuntu repository, you might get away with the open JDK but I tend to stick to the Oracle release. There are some installation instructions at https://help.ubuntu.com/community/Java#Oracle_Java_7

      2) Install Maven

      This might be in the Ubuntu repository but I tend to always install it manually. Version 3.0.3 is available from http://maven.apache.org/download.html and installation instructions are at http://maven.apache.org/download.html#Installation

      3) Install Spring Tools Suite

      STS is an eclipse distribution that includes a number or plugins already installed. You can get it from http://www.springsource.com/developer/sts. You could also use a standard version of eclipse (www.eclipse.org) if you prefer, but you will need to install the Maven plugin (http://eclipse.org/m2e/). I would stick to STS as it already has everything you need.

      4) Install git

      sudo apt-get install git

      Once you have your environment installed you should be able to simply checkout and import the project into STS.

      From the terminal:
      git clone http://github.com/philwebb/springfaces.git

      This should checkout the latest code, you then just need to import into STS

      - Start STS
      - Choose File -> Import Existing Maven Projects
      - Select the root project folder
      - You should see a tree with about 10 projects shows, click OK to import them all

      If you want to play with a sample project you should look at the springfaces-traveladvisor code. To run from inside eclipse:
      - Download tomcat 6 (http://tomcat.apache.org/download-60.cgi)
      - From STS select Window -> Show View – Other -> Servers
      - Right click on the Servers view and Choose New -> Server
      - Choose Apache Tomcat 6 from the list and point the folder at your local tomcat installation
      - Right click on the Servers view and Choose “Add and Remove…”
      - Select the springfaces-traveladvisor project
      - Click play to start the server
      - Browse to http://localhost:8080/springfaces-traveladvisor/spring/advisor/cities/search

      Hope that helps,
      Ping me if you need more information.

      Phil.

      Phillip Webb

      January 12, 2012 at 4:49 pm

  2. Hi
    Thank you got the detailed “how to”!
    I followed your instruction and at the end, maven build failed (I tried both STS versions 2.8 and the new 2.9 )

    I get the following error marked on the POM by STS:
    Multiple annotations found at this line:
    - ArtifactTransferException: Failure to transfer net.sourceforge.htmlunit:htmlunit-core-js:jar:2.9
    from http://repo1.maven.org/maven2 was cached in the local repository, resolution will not be
    reattempted until the update interval of central has elapsed or updates are forced. Original error:
    Could not transfer artifact net.sourceforge.htmlunit:htmlunit-core-js:jar:2.9 from/to central (http://
    repo1.maven.org/maven2): Stream Closed
    - Missing artifact cglib:cglib-nodep:jar:2.1_3
    - Missing artifact org.hamcrest:hamcrest-core:jar:1.1
    - ArtifactTransferException: Failure to transfer org.hamcrest:hamcrest-core:jar:1.1 from http://
    repo1.maven.org/maven2 was cached in the local repository, resolution will not be reattempted until
    the update interval of central has elapsed or updates are forced. Original error: Could not transfer
    artifact org.hamcrest:hamcrest-core:jar:1.1 from/to central (http://repo1.maven.org/maven2):
    Stream Closed
    - ArtifactTransferException: Failure to transfer cglib:cglib-nodep:jar:2.1_3 from http://
    repo1.maven.org/maven2 was cached in the local repository, resolution will not be reattempted until
    the update interval of central has elapsed or updates are forced. Original error: Could not transfer
    artifact cglib:cglib-nodep:jar:2.1_3 from/to central (http://repo1.maven.org/maven2): Stream Closed
    - Missing artifact net.sourceforge.htmlunit:htmlunit-core-js:jar:2.9

    and I get the following error while trying to run in the server:

    Publishing failed with multiple errors
    Error reading file /home/aba/.m2/repository/org/springframework/spring-web/3.1.0.RELEASE/spring-web-3.1.0.RELEASE.jar
    /home/aba/.m2/repository/org/springframework/spring-web/3.1.0.RELEASE/spring-web-3.1.0.RELEASE.jar (No such file or directory)
    Error reading file /home/aba/.m2/repository/org/hibernate/javax/persistence/hibernate-jpa-2.0-api/1.0.1.Final/hibernate-jpa-2.0-api-1.0.1.Final.jar
    /home/aba/.m2/repository/org/hibernate/javax/persistence/hibernate-jpa-2.0-api/1.0.1.Final/hibernate-jpa-2.0-api-1.0.1.Final.jar (No such file or directory)

    Any idea what might be the cause of the errors I get?
    Why do you prefer the TC 6.0 over the server which comes with the STS?

    Thanks,
    Yoav

    Yoav

    January 14, 2012 at 9:25 pm

    • I am not sure exactly what the problem is here. The error looks like a network fault during the download of one of the dependencies. You could try a command line build outside of eclipse to see if that works. Open a terminal in the cloned directory and type “mvn clean install” (assuming you have Maven installed). You often get better messages on the command line.

      One annoying habit that Maven has is that if a download fails it will not reattempt another download for 24 hours. To get around this try “mvn -U clean install” or alternatively delete the .m2/repository directory from your home folder before running the build. You might also need to tweak settings if you are running behind a proxy server.

      I have just retried my build locally with a cleaned Maven folder and everything appears to work.

      BTW I have no real preference over Tomcat 6 vs the server shipped with STS, I just happened to have used Tomcat more in the past.

      Phil.

      Phillip Webb

      January 14, 2012 at 9:48 pm

      • Thanks!
        That worked for me.

        yoav

        January 17, 2012 at 6:45 am

  3. Hi Phil,

    I’m having problems to use inside some primefaces component. As I see it primefaces requires that the message is decoded as String:

    java.lang.ClassCastException: org.springframework.springfaces.message.ui.MessageSourceMap$MessageCodeValue cannot be cast to java.lang.String
    at org.primefaces.component.commandbutton.CommandButtonRenderer.encodeMarkup(CommandButtonRenderer.java:57)

    Have you meet this problem?

    Cheers, Pedro

    • I have not seen this before. It seems that PrimeFaces expects values to be strings. What does your el expression look like? It is a bit ugly but try adding toString() to the end (eg #{messages.welcome.toString()}).

      Phillip Webb

      August 3, 2012 at 12:21 pm

      • This is the tag, I tried to use toString() and it worked as expected. I’m trying to debug where MessageSourceMap differs from the default jsf2 implementation.

        pccampos

        August 3, 2012 at 12:28 pm

    • The MessageSourceMap is a Map implementation rather than a String. It has been implemented this way to allow message parameters to be expanded in the form #{messages.welcome['Spring']['JSF']}. Perhaps if this feature is causing more harm than good I could add an option to always return Strings. Feel free to raise an issue (https://github.com/philwebb/springfaces/issues) if this is something you need.

      Phillip Webb

      August 3, 2012 at 2:32 pm


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: