views:

1706

answers:

6

Does Facelets have any features for neater or more readable internationalised user interface text labels that what you can otherwise do using JSF?

For example, with plain JSF, using h:outputFormat is a very verbose way to interpolate variables in messages.

Clarification: I know that I can add a message file entry that looks like:

label.widget.count = You have a total of {0} widgets.

and display this (if I'm using Seam) with:

<h:outputFormat value="#{messages['label.widget.count']}">
   <f:param value="#{widgetCount}"/>
</h:outputFormat>

but that's a lot of clutter to output one sentence - just the sort of thing that gives JSF a bad name.

A: 

Use ResourceBundle and property files.

Javaxpert
Could you explain a little more? I don't know what you mean, since OP already said he's using that.
Sietse
+2  A: 

I've never come across another way of doing it other than outputFormat. It is unfortunately quite verbose.

The only other thing I can suggest is creating the message in a backing bean and then outputting that rather than messageFormat.

In my case I have Spring's MessageSource integrated with JSF (using MessageSourcePropertyResolver). Then, it's fairly easy in your backing beans to get parameterised messages - you just need to know which Locale your user is in (again, I've got the Locale bound to a backing bean property so it's accessible via JSF or Java).

I think parameters - particular in messages - are one thing JSF could really do better!

Phill Sacre
+2  A: 

I have been thinking about this more, and it occurs to me that I could probably write my own JSTL function that takes a message key and a variable number of parameters:

<h:outputText value="#{my:message('label.widget.count', widgetCount)}"/>

and if my message function HTML-encodes the result before output, I wouldn't even need to use the h:outputText

#{my:message('label.widget.count', widgetCount)}
Peter Hilton
If you do this and get it working, please email me teh codez ;) Seriously though, it would be a great resource if you were able to release it somewhere.
Phill Sacre
+3  A: 

Since you're using Seam, you can use EL in the messages file.

Property:

label.widget.count = You have a total of #{widgetCount} widgets.

XHTML:

<h:outputFormat value="#{messages['label.widget.count']}" />

This still uses outputFormat, but is less verbose.

Sietse
The only limitation of this approach is that you are tied to using this label with #{widgetCount} or you have to explicityly outject "widgetCount" to use it.
Damo
+2  A: 

You could create your own faces tag library to make it less verbose, something like:

<ph:i18n key="label.widget.count" p0="#{widgetCount}"/>

Then create the taglib in your view dir: /components/ph.taglib.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE facelet-taglib PUBLIC "-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN" "https://facelets.dev.java.net/source/browse/*checkout*/facelets/src/etc/facelet-taglib_1_0.dtd"&gt;

<facelet-taglib xmlns="http://java.sun.com/JSF/Facelet"&gt;
    <namespace>http://peterhilton.com/core&lt;/namespace&gt;

    <tag>
        <tag-name>i18n</tag-name>
        <source>i18n.xhtml</source>
    </tag>

</facelet-taglib>

create /components/i18n.xhtml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
    xmlns:ui="http://java.sun.com/jsf/facelets"
        xmlns:h="http://java.sun.com/jsf/html"        
    xmlns:f="http://java.sun.com/jsf/core"&gt;

    <h:outputFormat value="#{messages[key]}">
            <!-- crude but it works -->
        <f:param value="#{p0}" />
        <f:param value="#{p1}" />
        <f:param value="#{p2}" />
        <f:param value="#{p3}" />
    </h:outputFormat>

</ui:composition>

You can probably find an elegant way of passing the arguments with a little research.

Now register your new taglib in web.xml

<context-param>
<param-name>facelets.LIBRARIES</param-name>
<param-value>
        /components/ph.taglib.xml
    </param-value>
</context-param>

Just add xmlns:ph="http://peterhilton.com/core" to your views and you're all set!

Daan van Yperen
Good answer, and close to what we've done so far. The main problem is that you can't use it inside attribute values.
Peter Hilton
+1  A: 

You can use the Seam Interpolator:

<h:outputText value="#{interpolator.interpolate(messages['label.widget.count'], widgetCount)}"/>

It has @BypassInterceptors on it so the performance should be ok.

Damo