Question

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.

Was it helpful?

Solution

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">

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

    <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">
<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">

    <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!

OTHER TIPS

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.

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!

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)}

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.

You can use the Bean directly if you interpolate the messages.

label.widget.count = You have a total of #{widgetCount} widgets.
label.welcome.message = Welcome to #{request.contextPath}!
label.welcome.url = Your path is ${pageContext.servletContext}.

${messages['label.widget.count']} is enougth.

This one works great using Spring:

package foo;

import javax.el.ELContext;
import javax.el.ELException;
import javax.el.ExpressionFactory;
import javax.el.ResourceBundleELResolver;
import javax.faces.context.FacesContext;

import org.springframework.web.jsf.el.SpringBeanFacesELResolver;

public class ELResolver extends SpringBeanFacesELResolver {
    private static final ExpressionFactory FACTORY = FacesContext
            .getCurrentInstance().getApplication().getExpressionFactory();
    private static final ResourceBundleELResolver RESOLVER = new ResourceBundleELResolver();

    @Override
    public Object getValue(ELContext elContext, Object base, Object property)
            throws ELException {
        Object result = super.getValue(elContext, base, property);
        if (result == null) {
            result = RESOLVER.getValue(elContext, base, property);
            if (result instanceof String) {
                String el = (String) result;
                if (el.contains("${") | el.contains("#{")) {
                    result = FACTORY.createValueExpression(elContext, el,
                            String.class).getValue(elContext);
                }
            }
        }
        return result;
    }
}

And...

You need to change the EL-Resolver in faces-config.xml from org.springframework.web.jsf.el.SpringBeanFacesELResolver to

Regards

<el-resolver>foo.ELResolver</el-resolver>

Use ResourceBundle and property files.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top