internationalization in JSF with ResourceBundle entries which are loaded from database internationalization in JSF with ResourceBundle entries which are loaded from database database database

internationalization in JSF with ResourceBundle entries which are loaded from database


Internationalization/localization should preferably be entirely done in the view side. The model shouldn't be aware of this.

In JSF, the <resource-bundle> entry in faces-config.xml and the <f:loadBundle> in XHTML can also point to a fullworthy ResourceBundle class instead of basename of .properties files. In Java SE 6 there's a new ResourceBundle.Control API available which allows full control over loading and filling the bundle.

Knowing those facts, it should be possible to load the bundle messages from the DB with a custom ResourceBundle and Control. Here's a kickoff example:

public class CompetenceBundle extends ResourceBundle {    protected static final String BASE_NAME = "Competence.messages"; // Can be name of @NamedQuery    protected static final Control DB_CONTROL = new DBControl();    private Map<String, String> messages;    public CompetenceBundle() {        setParent(ResourceBundle.getBundle(BASE_NAME,             FacesContext.getCurrentInstance().getViewRoot().getLocale(), DB_CONTROL));    }    protected CompetenceBundle(Map<String, String> messages) {        this.messages = messages;    }    @Override    protected Object handleGetObject(String key) {        return messages != null ? messages.get(key) : parent.getObject(key);    }    @Override    public Enumeration<String> getKeys() {        return messages != null ? Collections.enumeration(messages.keySet()) : parent.getKeys();    }    protected static class DBControl extends Control {        @Override        public ResourceBundle newBundle            (String baseName, Locale locale, String format, ClassLoader loader, boolean reload)                throws IllegalAccessException, InstantiationException, IOException        {            String language = locale.getLanguage();            Map<String, String> messages = getItSomehow(baseName, language); // Do your JPA thing. The baseName can be used as @NamedQuery name.            return new CompetenceBundle(messages);        }    }}

This way you can declare it as follows in faces-config.xml:

<resource-bundle>    <base-name>com.example.i18n.CompetenceBundle</base-name>    <var>competenceBundle</var></resource-bundle>

Or as follows in the Facelet:

<f:loadBundle basename="com.example.i18n.CompetenceBundle" var="competenceBundle" />

Either way, you can use it the usual way:

<h:outputText value="#{competenceBundle.name}" />


I would move the language specific part from your model into recource bundles. Just model Competence, Language and User. If a user requests a page, you display his competence and lookup the language specific competence (CompetenceName) from the recource bundle.

I searched for sample code to get you started and found this, see Listing 19.16 customerDetails.jsp.

Something like:

<fmt:message key="${competence}" />