Any web application contains boiler plate text: text that is not part of the enterprise data from web services or databases that is manipulated by the end users using the application but that is part of the application definition itself. Text that is shown as prompt, title, message, hint text and in other ways. Developers can sprinkle the boilerplate text all throughout the application, in pages, JavaScript and other code sections. But they should not do that. Changing boiler plate text is a frequent requirement from the business. Having all boilerplate text in a central location makes such changes a lot easier. Additionally, many organizations require applications to be multi-lingual: different groups of users speak different languages and want to have the application support them in their own language. That means boiler plate text is not just defined once in that central location, but once for every language the application needs to support.
Java (Web) applications typically make use of a built in structure for centralizing (and internationalizing) boiler plate text; it is called Resource Bundle. Usually, resource bundles are implemented using property files – one per supported language – that contain key-value pairs; note that the XLIFF file format for resource bundles is gaining ground. The key is referred to in pages, code and wherever a boiler plate text element is required, the value is the language specific and centrally managed translation of the key.
In two previous articles, I have explained how to centralize the boilerplate text elements in ADF applications using resource bundles – either plain file based resource bundles (https://technology.amis.nl/2012/08/09/introduction-to-resource-bundles-in-adf-applications-for-centralizing-management-of-boilerplate-text) or resource bundles implemented using a database (https://technology.amis.nl/2012/08/10/implement-resource-bundles-for-adf-applications-in-a-database-table/). These two articles did not discuss how multiple languages can be supported and how the user can pick his or her favorite language. That is the purpose of this article.
In later articles I will discuss how to support not just different boilerplate text items for different languages but also different sets of boiler plate text for user groups that vary by location, role, department, age or personal preference. We will discuss how to refresh a resource bundle in running application and finally we will see how we can create an in-line resource bundle editing mechanism that allows users to manipulate the boiler plate text in context in a running application.
1. Specify supported locales in faces-config.xml
2. Create resource bundles per language
property file – create new files with the language as appendix to the file name, for example ApplicationBundle_en.properties and ApplicationBundle_nl.properties.
Java Class – the same approach: create a class per supported language and add _<language code> as suffix to the classname.
Additionally, the method getLocaleCode() needs to be defined in each of the classes:
and
3. Add entries for all supported languages to the resource bundle
Either the property files for the database table – depending on the implementation – needs to be extended with the proper key value pairs for the supported languages. Of course the keys are the same for all languages!
Here two new entries for the nl support (Dutch):
4. Run the page.
Try switching to a different preferred language. The default behavior for JSF is to take the locale set in the browser as the current locale. By changing the browser locale, you can instruct JSF therefore to change to a different language.
and the page renders like this:
5. Programmatically taking control of the locale in JSF
You may not always want to go with the locale defined in the browser. You may for example want to offer the user an option of selecting the language in the application itself. The JSF behavior can be overridden. One way of doing so is by explicitly setting the local on the f:view component using the locale attribute, that can either be static or set using an EL expression or by overriding the ViewHandler. I will discuss and implement this approach.
The ViewHandler in JSF takes care of determining the current locale, using the calculateLocale() method. The default implementation derives the locale from the browser setting. The default ViewHandler can be overridden with our own implementation. This is done by creating a class that extends from an existing ViewHandler implementation – overriding the calculateLocale() method – and by configuring the ViewHandler in faces-config.xml.
The LocaleSettingViewHandler is implemented like this:
The remainder of this class is quite important too, as it ensures that all normal operations by the ViewHandler still take place:
and there is the local utility method to evaluate EL expressions:
Here the calculateLocale() method refers in an EL expression to a managed bean. This bean has a language property, that can be manipulated by the user through a drop down component in the page:
defined through:
The managed bean definition:
The ViewHandler is configured in faces-config.xml:
6. Run the page.
The default locale is still English – because the managed bean says so, not because of the browser setting.
Changing the selected language
and refreshing the page, using the newly selected language for retrieving the resource bundle entries:
Resources
Download the ADF application described in this article: ADFFacesWithResourceBundle_step3_multipleLanguagesAndLocaleControl.
Article on http://www.i-coding.de/www/en/jsf/application/locale.html describing how to set the locale.
Other articles in this series:
-
Introduction to Resource Bundles in ADF applications for centralizing (management of) boilerplate text
-
Implement resource bundles for ADF applications in a database table
-
Supporting multiple languages in ADF applications backed by resource bundles – and programmatically controlling the JSF locale
-
Adding customization (or context sensitivity) to boilerplate text from database backed resource bundles in ADF applications
-
Refresh resource bundle from within the ADF application – to absorb changes in database backed bundles
-
Live update of Resource Bundle from within running ADF application
-
Live resource bundle entry editing in a generic way through declarative component and UI component tree manipulation
-
Creating reusable ADF Library with generic live resource bundle editing functionality and reusing it in any ADF application
Dear Lucas,
thank you for this great Post about Internationalization through Database.
We used it in Jdeveloper 12c / ADF 12 c and had one Issue with it when using task-flows as regions:
We extended the ViewHandler (javax.faces.application.ViewHandler) and configured it in faces-config.xml as described in your tutorial.
Then, when we used any task-flow as a region in a page (fragment). The region didn’t show up, the space was just blank. Removing the ViewHandler configuration from faces-config.xml brought the region back.
The solution was
Step 1 of 2) instead of extending the javax.faces.application.ViewHandler we extended from the javax.faces.application.ViewHandlerWrapper:
————————————————————————————————————————————
import java.util.Locale;
import javax.el.ELContext;
import javax.el.ExpressionFactory;
import javax.el.ValueExpression;
import javax.faces.application.ViewHandler;
import javax.faces.application.ViewHandlerWrapper;
import javax.faces.context.FacesContext;
public class LocaleSettingViewHandlerWr extends ViewHandlerWrapper {
private final ViewHandler base;
private final String LANGUAGE_EL_EXPRESSEION = “#{localeManager.currentLocale}”;
public LocaleSettingViewHandlerWr(ViewHandler base) {
this.base = base;
}
@Override
public Locale calculateLocale(FacesContext facesContext) {
String language = (String) evaluateEL(LANGUAGE_EL_EXPRESSEION);
if(language == null){
return base.calculateLocale(facesContext);
}
return new Locale(language);
}
@Override
public ViewHandler getWrapped() {
return base;
}
public static Object evaluateEL(String el){
FacesContext facesContext = FacesContext.getCurrentInstance();
ELContext elContext = facesContext.getELContext();
ExpressionFactory expressionFactory = facesContext.getApplication().getExpressionFactory();
ValueExpression exp = expressionFactory.createValueExpression(elContext, el, Object.class);
return exp.getValue(elContext);
}
}
This ViewHandler has to be configured in faces-config.xml as in your tutorial. !!!!!!
Step 2 of 2) Also set this ViewHandler as ALTERNATE_VIEW_HANDLER in web.xml
————————————————————————————————————————————
Add the following lines to the web.xml file in the right section (somewhere amongst other context params)
<context-param>
<param-name>org.apache.myfaces.trinidad.ALTERNATE_VIEW_HANDLER</param-name>
<param-value>com.oprisk.a4a.app.bundles.LocaleSettingViewHandlerWr</param-value>
</context-param>
Kind regards
Yves Tiesler