Integrating Spring & JavaServer Faces : Internationalization and Localization
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.
Written by Phillip Webb
November 15, 2011 at 5:55 pm
Posted in Integrating Spring & JavaServer Faces, Spring
10 Responses
Subscribe to comments with RSS.
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
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
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
Pedro Casagrande de Campos
August 3, 2012 at 12:14 pm
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
https://github.com/philwebb/springfaces/issues/48 has now been fixed
Phillip Webb
August 8, 2012 at 9:45 am