Using MessageSource to get properties (i18N)


For International Support we need to implement properties files for each language.

In order to support this, Spring provides a ResourceBundleMessageSource which we define in the spring-config.xml.

With this in place, we can supply Spring with a list of properties files and a locale to use.  We can now use this combination to provide messages and dialog customised for the country we are servicing at the time.

Example

Before we continue, let’s first setup the pom.xml and spring-config.xml for our project as follows:

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.skills421.examples.spring</groupId>
<artifactId>Spring3.2.6.Demo</artifactId>
<version>0.0.1-SNAPSHOT</version>

<properties>
<spring.version>3.2.6.RELEASE</spring.version>
<junit.version>4.11</junit.version>
</properties>

<dependencies>

<!-- Spring 3 dependencies -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>

<!-- JUnit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>

</dependencies>
</project>

spring-config.xml

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">

<context:annotation-config />
<context:component-scan base-package="com.skills421.examples.spring.service" />

<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames">
<list>
<value>i18n-messages</value>
</list>
</property>
</bean>
</beans>

Here we have specified a context:compoent-scan so that Spring can scan our java package com.skills421.examples.spring.service for Spring Beans specified with the @Component annotation.

In addition, we have specified a ResourceBundleMessageSource bean named messageSource. This takes a list of property files as a basenames parameter. If we do not supply an extension for these files then Spring will assume an extension of .properties.

In our example, we have supplied i18n-messages. Spring will therefore look for i18n-messages.properties, but will also look for all property files with the same name but with a Locale extension.

We will create the properties files i18n-messages_en.properties and i18n_messages_it.properties which will both be included by of definition in the spring-config.xml.

i18n_messages_en.properties

greeting.morning=good morning
greeting.afternoon=good afternoon
greeting.evening=good evening
config.codenotfound=The Message for code {0} could not be found

i18n_messages_it.properties

greeting.morning=buongiorno
greeting.afternoon=buon pomeriggio
greeting.evening=buonasera
config.codenotfound=il messaggio per il codice {0} non è stato trovato

With these in place, we can now create our ConfigService bean. This is a little contrived, but demonstrates what we are trying to achieve.

ConfigService.java

package com.skills421.examples.spring.service;

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

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.MessageSource;
import org.springframework.stereotype.Component;

@Component("config")
public class ConfigService
{
private Properties configMap;
private String configfile;
private Locale locale;

@Autowired
private MessageSource messageSource;

public Locale getLocale()
{
if(locale==null)
{
locale = Locale.UK;
}

return locale;
}

public void setLocale(Locale locale)
{
this.locale = locale;
}

public String getConfigfile()
{
return configfile;
}

@Value("config.properties")
public void setConfigfile(String configfile)
{
this.configfile = configfile;
}

public Properties getConfigMap()
{
if(configMap==null)
{
configMap = new Properties();
try
{
configMap.load(Thread.currentThread().getContextClassLoader().getResourceAsStream(configfile));
}
catch(IOException e)
{
e.printStackTrace();
}
}

return configMap;
}

public MessageSource getMessageSource()
{
return messageSource;
}

public void setMessageSource(MessageSource messageSource)
{
this.messageSource = messageSource;
}

public String getProperty(String name)
{
Object value = this.getConfigMap().get(name);

if(value==null)
{
value =  messageSource.getMessage("config.codenotfound", new String[]{name}, "config, codenotfound not defined", this.getLocale());
}

return (String) value;
}
}

This file includes a configfile property that is injected using the annotation @Value. This is simply a config file that we want to read to get application configuration information – say for a Gui application or similar.

it also includes a messageSource bean (as defined in our spring-config.xml) that is @Autowired.

The configMap property is lazily loaded using Java in the getConfigMap() method.

The locale method can be injected, but for this example we simply inject it from the test case later on.

Finally, we make use of the messageSource bean to read from the appropriagte i18n-messages properties file, depending on the Locale. Note that in the getLocale() method, if a locale has not been set, we default to Locale.UK.

The messageSource take four parameters:

  • the name of the string to be retrieved from the parameters file
  • an array of objects – incase our message takes any parameters, or null if no parameters
  • a default message that should be returned if the property cannot be found
  • the Locale for which we get the message

Let’s modify our ConfigService bean from our previous example to return a localized error message if it cannot find the property we have asked for

TestConfig.java

Finally, let’s create out test suite to test our beans.

package com.skills421.example.spring;

import java.util.Locale;

import org.junit.After;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.skills421.examples.spring.service.ConfigService;

public class TestConfig
{
private static AbstractApplicationContext context;

@BeforeClass
public static void setupAppContext()
{
context = new ClassPathXmlApplicationContext("spring-config.xml");
}

@AfterClass
public static void closeAppContext()
{
if(context!=null)
{
context.close();
}
}

@After
public void printBlankLine()
{
System.out.println();
}

@Test
public void testAllPropertiesFromConfigService()
{
ConfigService configService = context.getBean("config",ConfigService.class);

for(Object propKey : configService.getConfigMap().keySet())
{
String propName = (String) propKey;

System.out.println(propName+" = "+configService.getProperty(propName));
}
}

@Test
public void testII8N_UK()
{
String greeting = context.getMessage("greeting.morning", null, "Good Morning to you",Locale.UK);
System.out.println(greeting);
}

@Test
public void testII8N_Italy()
{
String greeting = context.getMessage("greeting.morning", null, "Good Morning to you",Locale.ITALY);
System.out.println(greeting);
}

@Test
public void testConfigNameNotFound_UK()
{
ConfigService configService = context.getBean("config",ConfigService.class);

System.out.println(configService.getProperty("unknown"));
}

@Test
public void testConfigNameNotFound_Italy()
{
ConfigService configService = context.getBean("config",ConfigService.class);
configService.setLocale(Locale.ITALY);

System.out.println(configService.getProperty("unknown"));
}
}

Output

In running our test suite, we get the following output.

buongiorno

company.name = Skills421
company.url = www.skills421.com
company.contact = John Dunning

The Message for code unknown could not be found

good morning

il messaggio per il codice unknown non ? stato trovato

Notice how we are getting messages in both English and Italian.

Advertisement

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s