Internationalization in JSF with ResourceBundle records that are loaded from the database

I am working on a Java EE6 project using JPA / EJB / JSF and I am having problems designing multi-language support for entities. There are three relevant objects:

Language (has id)
Competence (has id)
CompetenceName (has a link to competency, a link to the language and string)

Competency has a one-to-many link to CompetenceName, implemented using a Map, containing one object for each language in which the name for Competency exists. Note that competencies are created dynamically , and therefore their names cannot exist in a resource bundle.

When listing competencies on a web page, I want them to be displayed in the language of the current user, which is stored in a session-controlled Bean.

Is there a good way to achieve this without breaking the good MVC design? My first idea was to get a bean session directly from the getName method in the Competence object via FacesContext and look at the CompetenceNames map for it as follows:

public class Competence { ... @MapKey(name="language") @OneToMany(mappedBy="competence", cascade=CascadeType.ALL, orphanRemoval=true) private Map<Language, CompetenceName> competenceNames; public String getName(String controller){ FacesContext context = FacesContext.getCurrentInstance(); ELResolver resolver = context.getApplication().getELResolver(); SessionController sc = (SessionController)resolver.getValue(context.getELContext(), null, "sessionController"); Language language = sc.getLoggedInUser().getLanguage(); if(competenceNames.get(language) != null) return competenceNames.get(language).getName(); else return "resource missing"; } 

This decision seems extremely rude, because the object relies on the controller level and must receive the session controller each time I want its name. A more acceptable solution for MVC would be to take a language parameter, but this means that every single call from JSF will have to include a language obtained from a session-controlled bean that does not look like a good solution.

Does anyone have thoughts or design patterns for this problem?

+6
database jpa internationalization jsf resourcebundle
source share
2 answers

Internationalization / localization should preferably be fully performed on the side of the vision. The model should not know about this.

In JSF, writing <resource-bundle> in faces-config.xml and <f:loadBundle> in XHTML can also point to a full ResourceBundle class instead of the base name of the .properties . Java SE 6 includes the new ResourceBundle.Control API, which allows you to fully control the loading and filling of the package.

Knowing these facts, it should be possible to download package messages from the database using custom ResourceBundle and Control . Here is an example run:

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

Thus, 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 in Facelet:

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

In any case, you can use it in the usual way:

 <h:outputText value="#{competenceBundle.name}" /> 
+14
source share

I would move the language-specific part from your model to bundles of recursors. Just a competency model, language and user. If the user requests a page, you show your competency and learn the specific language (CompetenceName) from a set of resources.

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

Something like:

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

All Articles