My blog has moved!

You should automatically be redirected in 6 seconds. If not, visit
http://blogs.i2m.dk/allan
and update your bookmarks.

Monday 9 March 2009

JSF and I18N

It has been a busy year so far. Every year I start out thinking that I really need to relax because the previous year was too hectic, but every year it ends up being much more hectic. Anyway, I thought I’d post a JSF quick time that might shave some headache for people developing internationalised applications.

Most JSF developers are aware of the resource-bundle tag in faces-config. Using this tag you can specify a resource bundle that should be available to all your pages using a given variable. For example, the code below will make a variable, msgs, available in all your pages without having to use the loadBundle tag.


<faces-config version="1.2">
...
<application>
<resource-bundle>
<base-name>my.class.path.Messages</base-name>
<var>msgs</var>
</resource-bundle>
</application>
...
</faces-config>


So, say that Messages.properties contains the following:


HELLO_WHAT_IS_YOUR_NAME=Hello, what’s your name?


you can from any page reference this text by writing:


<h:outputText value=”#{mgs.HELLO_WHAT_IS_YOUR_NAME}” />

or

<h:outputText value=”#{mgs[‘HELLO_WHAT_IS_YOUR_NAME’]}” />


I prefer the former as my favourite IDE (NetBeans) support code completion for all the messages in a managed resource bundle.

So far so good. But what if you want to do something a bit more advanced, such as outputting “Hello Allan, welcome to my site. Your last visit was 7. February 2009 at 10:45”. You are not limited to static text in your resource bundle. You can provide placeholders for content that will be replaced at run-time. Let’s have a look at the Message.properties:


WELCOME_MESSAGE=Hello {0}, welcome to my site. Your last visit was {1,date,long} at {1,time,short}


You’ll notice that the message is parameterised using two parameters. The first parameter we are expecting a string containing the name of the user, the second parameter is a java.util.Date containing both the date and time when user last visited the site. Notice that I’ve reused the second paramenter ({1}), one time for the date and the other for the time. On your JSF page you would use the message as follows (imaging that you have a bean called welcome containing a property for getting the name of the user and last visit):


<h:outputFormat value=”#{msgs.WELCOME_MESSAGE}”>
<f:param value=”#{welcome.userName}” />
<f:param value=”#{welcome.lastVisit}” />
</h:outputFormat>


Pretty neat, eh?

What about the classical case of singular and plural messages? Imagine that you want to display the number of records in a table “There are 10 records in the table”. If the Message.properties contained:


RECORDS_IN_TABLE=There are {0} records in the table


You’d end up an embarrassing problem if there was only one record in the table:

“There are 1 records in the table” (This sentence is grammatical incorrect)

Instead you would want it to output:

“There is 1 record in the table”

To do this, you have to use the choice pattern:


RECORDS_IN_TABLE=There {0, choice, 0#are|1#is|2#are} {0} {0, choice, 0#records|1#record|2#records}


The format might look a bit awkward, but once you have understood the pattern it is simple:


{0,choicemeans, taking the first parameter and base the output on a choice of formats
0#areif the first parameter contains 0 (or below), then it should print “are”
|1#isor, if the first parameter contain 1, then it should print “is”
|2#are}or, if the first parameter contains 2 (or above), then it should print “are”


Now, that is nice! So let’s examine the output of these examples:


<h:outputFormat value=”#{msgs.RECORDS_IN_TABLE}”>
<f:param value=”#{0}” />
</h:outputFormat>
There are 0 records in the table

<h:outputFormat value=”#{msgs.RECORDS_IN_TABLE}”>
<f:param value=”#{1}” />
</h:outputFormat>
There is 1 record in the table

<h:outputFormat value=”#{msgs.RECORDS_IN_TABLE}”>
<f:param value=”#{2}” />
</h:outputFormat>
There are 2 records in the table

<h:outputFormat value=”#{msgs.RECORDS_IN_TABLE}”>
<f:param value=”#{10}” />
</h:outputFormat>
There are 10 records in the table


Enjoy!

7 comments:

Anonymous said...

Many thanks Allan!
Very good article.

But, i can view messages (just!) with #{mgs[‘my_mess_prop’]}.

If I use #{mgs.my_mess_prop}, then appServer (Glassfish) threw exception:
javax.el.PropertyNotFoundException: The class 'java.lang.String' does not have the property 'my_mess_prop'.

Ayrat.
at javax.el.BeanELResolver.getBeanProperty(BeanELResolver.java:547)
ets.

Allan Lykke Christensen said...

Sounds like mgs is set up as a backing bean rather than a resource bundle?

Anonymous said...

I don't know Al :)
I have experience with JSF just second day.

But i18n set up in config file as
<application>
<resource-bundle>
<base-name>i18n.messages</base-name>
<var>msgs</var>
</resource-bundle>
<variable-resolver>org.springframework.web.jsf.DelegatingVariableResolver</variable-resolver>
</application>

Ayrat

Allan Lykke Christensen said...

:-) Okay, I'll be easy on you then.. Since your var is called msgs you should also use this variable on the page, e.g. {#msgs.NAME_OF_MESSAGE}

Anonymous said...

hi Al

Yes, I use 'msgs'.
#{msgs[‘my_mess_prop’]} is correct, but #{msgs.my_mess_prop} not works.

Probably, some EL-library needed.

Thanks

Anonymous said...

Hi,

Do you know if it is possible to change the message when you use a onClick="if (confirmDeleteSelectedRows() == false) return false;"

over a button to put the alert on different languages?

Thanks indeed.

Allan Lykke Christensen said...

Yes, you can include messages in your JavaScript as well. Since this JavaScript method is producing your confirm box: confirmDeleteSelectedRows(), I can't tell you exactly how to i18n the confirm box for that. But you could create it simply like this:

onclick="if (!confirm('#{msgs.MY_CONFIRM_MESSAGE}')) return false;"