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