Skip to main content

Just a Spoonful of Java EE Makes the AngularJS Localization Problem Go Away

Posted by edburns on February 25, 2014 at 8:37 PM PST

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:

  1. <script type="text/javascript">
  2. var principal = '${pageContext.request.userPrincipal.name}';
  3. </script>

Using Facelets, you can get the same effect with this code from chat.xhtml:

  1. <script type="text/javascript">
  2. var principal = '#{request.userPrincipal.name}';
  3. </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:

  1. <input class="textbox" placeholder="#{i18n.placeholderMessage}"
  2.       ng-model="newMessage" autofocus="autofocus" required="required" />

instead of

  1. <input class="textbox" placeholder="#{i18n.placeholderMessage}"
  2.       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:

  1. <?xml version='1.0' encoding='UTF-8'?>
  2. <faces-config version="2.2"
  3.    xmlns="http://xmlns.jcp.org/xml/ns/javaee"
  4.    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  5.    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd">
  6.    
  7.     <application>
  8.         <resource-bundle>
  9.             <base-name>i18n</base-name>
  10.             <var>i18n</var>
  11.         </resource-bundle>
  12.        
  13.         <locale-config>
  14.             <default-locale>en</default-locale>
  15.             <supported-locale>de</supported-locale>
  16.             <supported-locale>ar</supported-locale>
  17.         </locale-config>
  18.        
  19.     </application>
  20.    
  21. </faces-config>
  22.  

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:

  1. <label class="chat-label">#{i18n.welcomeMessage} {{user}}</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:

  1. <html xmlns="http://www.w3.org/1999/xhtml"
  2.      ng-app="chatApplication"
  3.      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.

Arabic localized chat

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.

AttachmentSize
chat-ar.png46.37 KB