Saturday, February 09, 2013

Exposing all Spring i18n messages to Angular.JS or any rich front end

Spring does a great job with internationalization. Simple, straightforward, great! However I need to easily access the messages from a rich Angular.JS based front end. For that matter it would be ideal to expose the messages in a JSON format that is loaded at the beginning of the app in the browser right? Then it makes sense to have a simple directive to load them all from JSP/JSTL, like:
<script>
  var messages = ${ju:getMessages(locale)};
</script>
By default you easily find methods to get key by key but when you need all the messages you will need to create your own MessageSource. So we declare it:
   <bean id="messageSource"
          class="com.sample.web.CustomReloadableResourceBundleMessageSource">
          <qualifier value="messageSource"/>
        <!--  <property name="basename" value="classpath:messages" />  -->
        <property name="basenames">
            <value>/WEB-INF/i18n/messages</value>
        </property> 
        <property name="cacheSeconds">
            <value>60</value>
        </property>
        <property name="fallbackToSystemLocale" value="false" />
    </bean>
The java code for the custom Message Source:
package com.sample.web;

import java.util.Locale;
import java.util.Properties;

import org.springframework.context.support.ReloadableResourceBundleMessageSource;

public class CustomReloadableResourceBundleMessageSource extends ReloadableResourceBundleMessageSource {

 public Properties getAllProperties(Locale locale) {
  clearCacheIncludingAncestors();
  PropertiesHolder propertiesHolder = getMergedProperties(locale);
  Properties properties = propertiesHolder.getProperties();

  return properties;
 }
}
A new method for our taglib named getMessages() (If you missed the tutorial for creating the first just search in this blog for it:
<?xml version="1.0" encoding="UTF-8"?>
<taglib xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemalocation="http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
    version="2.0">
    <tlib-version>2.1</tlib-version>
    <uri>JsonUtils</uri>
     
    <function>
        <name>toJson</name>
        <function-class>com.sample.utils.JsonUtils</function-class>
        <function-signature>
            String toJson(java.lang.Object)
        </function-signature>
    </function>
    
    <function>
        <name>getMessages</name>
        <function-class>com.sample.utils.JsonUtils</function-class>
        <function-signature>
            String getMessages(java.util.Locale)
        </function-signature>
    </function>
</taglib>
The new utility method for the taglib:
package com.sample.utils;

import java.io.IOException;
import java.util.Locale;

import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.map.JsonMappingException;

import com.sample.serialization.JacksonObjectMapper;
import com.sample.utils.web.ApplicationServletContextListener;
import com.sample.web.CustomReloadableResourceBundleMessageSource;

public final class JsonUtils {

 private JsonUtils() {
 }

 public static String toJson(Object value) throws JsonGenerationException, JsonMappingException, IOException {
  JacksonObjectMapper mapper = new JacksonObjectMapper();
  return mapper.writeValueAsString(value);
 }

 public static String getMessages(Locale locale) throws JsonGenerationException, JsonMappingException, IOException {
  CustomReloadableResourceBundleMessageSource messageSource = (CustomReloadableResourceBundleMessageSource) ApplicationServletContextListener
    .getBean("messageSource");
  JacksonObjectMapper mapper = new JacksonObjectMapper();
  return mapper.writeValueAsString(messageSource.getAllProperties(locale));
 }
}
Finally don't forget to inject the locale in your View from Controller. I prefer a single "locale" variable so the front end engineer just have to use the simple statement that started this post.

No comments:

Followers