Just a Spoonful of Java EE Makes the AngularJS Localization Problem Go Away Blog
I presented this demo at DevNexus 2014 in Atlanta today. It is Reza's demo with just a pinch more Java EE thrown in. This blog entry covers how to fully internationalize an AngularJS application with just a pinch of Java EE.
Reza hosts the code for this demo on his github. The demo has a chat client and a todo list. This blog entry only looks at the chat client, and assumes basic familiarity with AngularJS. The content is broken up into two parts, the first part shows how to localize an Angular JS app using the locale support of Java EE. The second part, to be published at a later date, shows how to apply Facelets templating to accomplish page modularity on top of AngularJS.
The first pinch of Java EE
Reza is using basic authentication to give the chat app a quick concept of user identity. Java EE basic authentication is documented in the Java EE tutorial. Assuming the app has been configured for basic authentication, any JSP or Facelets page can be an AngularJS application and take advantage of that identity with a simple EL expression. The first inline script block of chat.jsp is the following:
- <script type="text/javascript">
- var principal = '${pageContext.request.userPrincipal.name}';
- </script>
Using Facelets, you can get the same effect with this code from chat.xhtml:
- <script type="text/javascript">
- var principal = '#{request.userPrincipal.name}';
- </script>
In
both the JSP and Facelets cases, we take the name of the currently
logged in user and stick it in a JavaScript top level variable.
Later, in the AngluarJS controller for this demo, controllers.js we expose that value on the controller scope: $scope.user = principal;
There is one other bothersome restriction when using Facelets. It must be well formed XML. This means you must write:
- <input class="textbox" placeholder="#{i18n.placeholderMessage}"
- ng-model="newMessage" autofocus="autofocus" required="required" />
instead of
- <input class="textbox" placeholder="#{i18n.placeholderMessage}"
- ng-model="newMessage" autofocus required >
If this is a dealbreaker for you, just don't use the Facelets part and stick to JSP. You can still localize just fine.
Localization in Java EE
Localization in Java EE is built on the simple ResourceBundle
feature, which has been present in Java since 1996. The average
AngularJS developer (currently about 24 years old) would be six years
old at that time. This feature is documented in the Java tutorial. To add localization to the UI of a Java EE application, you need to pull in the FacesServlet
add a faces-config.xml
file to your WEB-INF
directory. Pulling in the FacesServlet
is easy, just make sure to access your .xhtml or JSP pages with the /faces
prefix. There are other ways to pull in the FacesServlet
but they are beyond the scope of this blog entry. The faces-config.xml
file must contain a <resource-bundle>
and a <locale-config>
element. For Reza's example, this is:
- <?xml version='1.0' encoding='UTF-8'?>
- <faces-config version="2.2"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd">
- <application>
- <resource-bundle>
- <base-name>i18n</base-name>
- <var>i18n</var>
- </resource-bundle>
- <locale-config>
- <default-locale>en</default-locale>
- <supported-locale>de</supported-locale>
- <supported-locale>ar</supported-locale>
- </locale-config>
- </application>
- </faces-config>
Line 7 declares the <application>
element. This element contains any application singleton declarations. Lines 8 - 11 declare a ResourceBundle
that can be accessed from any JSP or Facelets page in the app. The <base-name>
element, on line 9, declares the fully qualified class name of the ResourceBundle
. In Reza's app, this is the localized i18n
file in the src/main/resources
directory. It is currently localized in en, ar, and de
locales. Line 10 declares the symbol under which the name=value
pairs of the ResourceBundle
are exposed via EL. This means that anything in the i18n
file is available like this:
- <label class="chat-label">#{i18n.welcomeMessage} </label>
Lines 13 - 17 declare the locale configuration for this app. Line 14 declares the default locale. This is what will be used if the browser doesn't send any locale preference, or a match between the desired locale and supported locales cannot be found. Lines 15 and 16 declare that this application additionally supports German and Arabic locales. The JSF specification requires the container must look at the preferences expressed by the browser and find the best fit, given the locale config of the application.
The only remaining consideration is to ensure the dir
attribute on the HTML
element is correctly set. This is only necessary for languages
that read right-to-left such as Hebrew or Arabic. This can be
accomplished with a simple EL expression. This example is taken
from the Facelets chat.xhtml file:
- <html xmlns="http://www.w3.org/1999/xhtml"
- ng-app="chatApplication"
- dir="#{facesContext.viewRoot.locale.language eq 'ar' ? 'rtl' : 'ltr' }">
The
xmlns declaration on line 1 is required for Facelets. It is not
required for JSP. Line 2 is the AngularJS application
directive. Line 3 is the EL expression that evaluates to rtl
if the locale is Arabic, and ltr
otherwise.
The Arabic localized chat looks something like the following.
The follow-up entry will look at how to apply another pinch of Java EE to spice up your AngularJS application with localized page modularity.