Resolving Text Messages (message Properties)

Problem

For an application to support internationalization (I18N for short, as there are 18 characters between the first character, i, and the last character, n), it requires the capability of resolving text messages for different locales.

Solution

Spring’s application context is able to resolve text messages for a target locale by their keys. Typically, the messages for one locale should be stored in one separate properties file. This properties file is called a resource bundle.

MessageSource is an interface that defines several methods for resolving messages. The ApplicationContext interface extends this interface so that all application contexts are able to resolve text messages. An application context delegates the message resolution to a bean with the exact name messageSource. ResourceBundleMessageSource is the most common MessageSource implementation that resolves messages from resource bundles for different locales.

How It Works

As an example, you can create the following resource bundle, messages_en_US.properties, for the English language in the United States. Resource bundles will be loaded from the root of the classpath.

alert.checkout=A shopping cart has been checked out.

To resolve messages from resource bundles, you use ResourceBundleMessageSource as your MessageSource implementation. This bean’s name must be set to messageSource for the application context to detect it. You have to specify the base name of the resource bundles for ResourceBundleMessageSource.

<beans …>
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename">
<value>messages</value>
</property>
</bean>
</beans>

For this MessageSource definition, if you look up a text message for the United States locale, whose preferred language is English, the resource bundle messages_en_US.properties, which matches both the language and country, will be considered first. If there’s no such resource bundle or the message can’t be found, the one messages_en.properties that matches the language only will be considered. If this resource bundle still can’t be found, the default messages.properties for all locales will be chosen finally. For more information on resource bundle loading, you can refer to the javadoc of the java.util.ResourceBundle class. Now you can ask the application context to resolve a message by the getMessage() method. The first argument is the key corresponding to the message and the third is the target locale.

package com.shop;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
public class Main {
public static void main(String[] args) throws Exception {
ApplicationContext context =
new FileSystemXmlApplicationContext("beans.xml");
String alert = context.getMessage("alert.checkout", null, Locale.US);
System.out.println(alert);
}
}

The second argument of the getMessage() method is an array of message parameters. In the text message, you can define multiple parameters by index:

alert.checkout=A shopping cart costing {0} dollars has been checked out at {1}.

You have to pass in an object array to fill in the message parameters. The elements in this array will be converted into strings before filling in the parameters.

package com.shop;
public class Main {
public static void main(String[] args) throws Exception {
String alert = context.getMessage("alert.checkout", new Object[] { 4, new Date() }, Locale.US);
System.out.println(alert);
}
}

In the Main class, you can resolve text messages because you can access the application context directly. But for a bean to resolve text messages, it has to implement either the ApplicationContextAware interface or the MessageSourceAware interface. Now you can delete the message resolution from the Main class.

package com.shop;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceAware;
public class Cashier implements BeanNameAware, MessageSourceAware,
StorageConfig {
private MessageSource messageSource;
public void setMessageSource(MessageSource messageSource) {
this.messageSource = messageSource;
}
public void checkout(ShoppingCart cart) throws IOException {
String alert = messageSource.getMessage("alert.checkout",
new Object[] { total, new Date() }, Locale.US);
System.out.println(alert);
}
}