Resource Bundles in JSF 2.0 Applications
Setting up resource message bundles in JSF to provide multilingal messages and captions is often overlooked when first creating an application. Leaving it till later in the project means you will have to go back and manually change the constants over to resource based values. Resource bundles JSF 1.2 were far from perfect but fortunately, using resource bundles in JSF 2.0 is very easy and this tutorial will show you how to add bundles and use them in your JSF 2.0 pages.
For this example, we’ll create a new application using the jee6-servlet-basic-archetype
Knappsack Maven archetype. The full source can be downloaded and run using mvn clean jetty:run
from the command line.
Message resources are stored in properties files which consist of name value pairs that binds a message key string with the message value. We’ll use the following example
firstName=First Name lastName=Last Name forgotPassword=Forgot Password? usernameTaken={0} is already taken
This file needs to be saved and referenced in a package, which would normally be in the same place as your source code, but Maven provides a separate area for resources. Save the file in the src/main/resources/org/fluttercode/resourcedemo/
folder with the name MessageResources.properties
. Open up the faces-config
file and add the following XML :
<application> <resource-bundle> <base-name>org.fluttercode.resourcedemo.MessageResources</base-name> <var>msgs</var> </resource-bundle> </application>
Here, we have told JSF about the resource bundle and assigned a variable name to it. Now we will go and add a reference to one of the messages in our home page. Open home.xhtml
and replace the initial message with the following :
<h:outputText value="#{msgs.firstName}"/>
This is a JSF output text component that gets its value from the resource bunde, in this case, the firstName
value. If you are using JBoss Tools, you will see that it can perform autocompletion for you on both the msgs
value and the actual property keys on the msgs
resources. It also displays the actual property value in the preview window for the page. If you run the app now by typing mvn jetty:run
in the command line, you will see that the word First Name
appears in the page.
Access Resources From Code
Typically, in your application you will generate messages for the user that also needs to be obtained from the resource bundle. To do this, we will create a bean that can fetch the resource bundle for us and extract strings from it.
public class MessageProvider { private ResourceBundle bundle; public ResourceBundle getBundle() { if (bundle == null) { FacesContext context = FacesContext.getCurrentInstance(); bundle = context.getApplication().getResourceBundle(context, "msgs"); } return bundle; } public String getValue(String key) { String result = null; try { result = getBundle().getString(key); } catch (MissingResourceException e) { result = "???" + key + "??? not found"; } return result; } }
This class fetches the resource bundle from the faces context which will determine the best bundle to use based on the supported locales and the client locale. The second method uses the first method to fetch a string resource from the bundle.
We’ll create a JSF backing bean to use this bean to return a message to the user.
@Named @RequestScoped public class SomeBean { public String getMessage() { String msg = new MessageProvider().getValue("someMessage"); return MessageFormat.format(msg, "SomeValue"); } }
In our home.jsf
page, we display our message by adding
Message = #{someBean.message}
Which results in the following phrase being displayed :
Message = SomeValue is not valid
Having just the one default properties file in place is the bare minimum for using string resource bundles in your applications, and I would recommend using that for any application, even if it is never going to be multi-lingual. At the very least, it keeps your string constants in one place and at best, it makes it really easy to support multiple languages at a later date.
Handling JSF Locale Information
If you go multi-lingual you will need to provide multiple versions of the resource file for different locales, and list the supported locales in your faces-config.xml
file.
To test this, create multiple copies of the MessageResources
file in the same directory with different names such as MessageResources_de.properties
or MessageResources_fr.properties
and in the content for each, use the same values, but add the locale on the end.
MessageResources_fr.properties
firstName=First Name(fr) lastName=Last Name(fr) forgotPassword=Forgot Password?(fr) someMessage={0} is not valid(fr)
In the faces-config.xml
file you can add the supported locales and set the default locale to use.
<application> <locale-config> <default-locale>en_US</default-locale> <supported-locale>de</supported-locale> <supported-locale>en_GB</supported-locale> <supported-locale>fr</supported-locale> </locale-config> <resource-bundle> <base-name>org.fluttercode.resourcedemo.MessageResources</base-name> <var>msgs</var> </resource-bundle> </application>
Now, depending on where you are in the world, and your browser locale, if you plan on following along, you’ll have to substitute your own locale for en_US. Since we have a MessageProperties
file without a locale in the file name, if it cannot find a matching locale, it will use this default bundle. If the default locale is specified in faces-config
,then that locale will be used instead of the non-specific default.
Play around with renaming some of the resource files, especially the file that matches your own locale. If the file exists, but it is not included in the list of supported locales in faces-config
it won’t be used. You can also change the default-locale
value to one other than your own locale and see how the locale would be selected.
JSF will also select a locale that matches based on the language if not the specific region. In my case, having a locale of en_US
means that if available, JSF will select the MessageResources_en
bundle if there is no bundle specifically for en_US
.
Download the Maven source code for this project and run it locally by unzipping it into a folder and typing mvn clean jetty:run
and going to http://localhost:8080/resourcedemo/.
11 thoughts on “Resource Bundles in JSF 2.0 Applications”
Comments are closed.
I have problem with my Russian – English localized test site. Google bot has English locale, so I don’t have searching for Russian.
http://www.google.ru/#sclient=psy&hl=ru&newwindow=1&q=www.agost.ru&aq=f&aqi=&aql=&oq=&gs_rfai=&pbx=1&fp=14f0067de1f53fbc
Hi Andy,
thanks for this code. A question what comes to my mind was – why do you did not implement the MessageProvider as a Singleton? I’m sure there’s a reason for that. Please let me know.
The reason it isn’t a singleton is because the singleton is shared across the whole application by all users, whereas users need their own instance because they could have different locales.
However, the spirit of your question is correct, we don’t need to keep creating instances of this bean, and it was left like that to lead on to the next article which is now published.
Hey Andy, thanks for the nice examples.
They all worked for me except for a test I did putting a bundle message in a JPA 2.0 entity, for example:
@Size(min = 3, max=64, message=”#{bundle[‘field.size’]}”)
private Field someField;
This would print #{bundle[‘field.size’]} as the message.
I’ve tried this hard but without success.
Any hint on how to make it work?
Thanks a lot.
Rodrigo, You can’t use JSF EL expression like this in the validator since it is a completely separate API. You have to create a ValidationMessages.properties.
Cheers,
Andy
PS Sorry for the delay.
Andy,
Thanks, I use your blog entries a lot, compliment. When I used this one, I got something working, but got the feeling it was ‘complicated’. So I tried, and tried and tried and came up with just this in the MessageProvider and no other classes (e.g. for looking up bundles)
public String getValue(String key) {
final FacesContext c = FacesContext.getCurrentInstance();
final Application application = c.getApplication();
String result = null;
try {
result = application.evaluateExpressionGet(c, key, String.class);
} catch (PropertyNotFoundException e2) {
result = “???” + key + “??? not found”;
}
return result;
}
Which takes a real EL and parses, parses it and returns what you need. In more scopes, more variables/message bundles etc. It looks simple, so I think I might be missing some functionality.
Hey Ronald, yes, you can do that, and if you are using JBoss Solder they have an API to make it even easier to execute EL Expressions. My solution was written so that you can see how to code against resource bundles directly and also because the other resource articles makes use of it.
One problem with this is that EL Expressions will be checked against every known EL resolver and will probably take longer. Getting it from the resource bundle directly is more like looking it up in a map. In terms of code written though they are very similar, grab the bundle and do a lookup versus getting the faces context and evaluating it. However, this is a convenience function that creates a value expression and uses that to look up the value.
Cheers,
Andy Gibson
Yep, I agree. Learned a lot from your example(s) and yours is faster, for sure. It’s always a (one of the) trade-off(s). In this case convenience over speed.
is it possible to define several resource-bundles and is yes how?
In my faces-config file I wrote
en
eu.minipay.common
bundleCommon
en
eu.minipay.account
bundleAccount
the second bundle is invisible.
obs the XML is not shown properly, I paste without the
application
locale-config
default-locale en /default-locale
/locale-config
resource-bundle>
base-name eu.minipay.common /base-name>
var bundleCommon /var
resource-bundle
resource-bundle
base-name eu.minipay.account /base-name
var bundleAccount/var
/resource-bundle
/application
ok
for those who care, it seems that property files must be placed under src otherwise are not visible by the server.
I had them under WB-INF/resources but his does not work.