Localization with Spring

Localization enables applications to cater to users of different locations and languages. Spring, as usual, has support for this aspect as well.

Before jumping into what Spring has to offer, let's explore what we get from Java itself.

Locale


Java provides a java.util.Locale class to represent a locale. Locale holds both the language and region code.

The Locale class can be instantiated using the language/locale
new Locale("jp", "JP")

Locale also provides predefined constants for several locales. They can be used if the locale is a known one.

Locale.FRENCH
Locale.SIMPLIFIED_CHINESE

The default locale can be obtained through

Locale.getDefault()

Resource bundles


Java provides a way to define locale-specific resources through resource bundles. The bundles are nothing but simple properties files with key-values. They follow a simple naming convention, to help applications resolve them based on the locale.

e.g.
mypagelabels_ja.properties is used to define a mypagelabels bundle for the Japanese locale. mypagelabels_en_US.properties is used to define a bundle for the English language of US region.

The extra country suffix is useful for defining bundles for countries that may have the same language but different spellings, meanings or slangs.

When no suffixes are provided (mypagelabels.properties), the bundle is considered to be used for default locale of the system.

A few example bundles below

mypagelabels_en_US.properties
greetingtext=Hello Martians
goodbyetext=Have a great day!!

mypagelabels_es_ES.properties
greetingtext=Hola Marcianos
goodbyetext=Que tengas un gran día

Reading resource bundles


Using Java's java.util.ResourceBundle, you can read a resource bundle from classpath for a given basename and locale. If there is no separate bundle available for the provided locale, then the default bundle is loaded.

 ResourceBundle resources = ResourceBundle.getBundle("mypagelabels", new Locale("es", "ES"));
 System.out.println(resources.getString("greetingtext"));
 resources = ResourceBundle.getBundle("mypagelabels", Locale.getDefault());
 System.out.println(resources.getString("greetingtext"));

Spring ResourceBundleMessageSource


MessageSource(s) in spring help resolve messages that are parameterized. And, of course, it also supports the localization of those messages.

ResourceBundleMessageSource is a MessageSource that resolves messages from the resource bundles.

Consider the resource bundle and the following code snippet

mypagelabels_es_ES.properties
greetingtext=Hola {0}


ResourceBundleMessageSource resbundleMsfSrc = new ResourceBundleMessageSource();
resbundleMsfSrc.setBasename("mypagelabels");
System.out.println(resbundleMsfSrc.getMessage("greetingtext", new Object[]{"Mario"}, new Locale("es", "ES")));

The example above would output
"Hola Mario".

It would first resolve the "mypagelabels" bundle that corresponds to es_ES locale, then it resolves the message corresponding' to "greetingtext", and then it replaces the parameter placeholders with the provided params, which is "Mario" in our case.

Note that the placeholders start with number 0. i.e. {0}, {1}, {2}, etc.

ReloadableResourceBundleMessageSource is an alternative to ResourceBundleMessageSource, that can resolve messages from the resource bundles without having to restart the JVM. You can make changes to the resource bundle while the application is running and it would resolve it to the latest change.

Locale Resolution for MVC apps


Spring MVC provides a few components for resolving the user locale and present her with localized content.

To make the application locale aware, you need a LocaleResolver and/or a LocaleChangeInterceptor. And you would register them through the web configuration in the following manner.

@Configuration
public class WebMvcConfiguration extends WebMvcConfigurerAdapter {
    
    @Bean
    public LocaleResolver sessionLocaleResolver() {
        AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
        return localeResolver;
    }
    
    @Bean
    public LocaleChangeInterceptor localeChangeInterceptor() {
        // This interceptor would intercept every single request and set the
        // locale value from the param name provided on the resolver.
        //Useful only if the locale value is available through a request parameter.
        LocaleChangeInterceptor interceptor = new LocaleChangeInterceptor();
        interceptor.setParamName("mylocale");
        return interceptor;
    }
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(localeChangeInterceptor());
    }
}

LocaleResolver


LocaleResolver, as the name suggests, is responsible for resolving the locales. It is used throughout the Spring MVC workflow for fetching the user locale. It is a simple interface with two methods.

    //Returns the locale for the given request
    Locale resolveLocale(HttpServletRequest request);
    
    //Method to set a locale value. Depending upon the LocaleResolver,
    //  the locale can be set on the request, response or on to something else altogether.
    void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale);

Spring provides several flavors of LocaleResolvers out of the box.

AcceptHeaderLocaleResolver


It creates a locale object using the "accept-language" header of the HTTP request and returns it. If the header is not present, then the default locale set on the resolver is used.

There are browser extensions available that can be used to set different locales in the request. e.g. Quick Language Switch extension in chrome.

SessionLocaleResolver


It looks up for an attribute viz. "SessionLocaleResolver.LOCALE" in the HTTP Session and returns the Locale object corresponding to that value. If there is no attribute set on the session, then it would return the default locale that was set on the resolver.

You can set the locale attribute based on user preferences, on login. Or you can set the locale in the session when the user switches to the preferred locale. It can be used in many custom ways to set the user locale.

FixedLocaleResolver


This resolver always returns a fixed locale i.e. the one that was set on the resolver when it was created. I can't think of any good reason for using this resolver, except for test purposes.

LocaleChangeInterceptor


LocaleChangeInterceptor is useful if you would like to send the locale information through HTTP request parameters. It intercepts every single request and if the locale parameter is found, then it sets it on the localeResolver. It can't be used with FixedLocaleResolver or AcceptHeaderLocaleResolver, as they do not support setting of locales.

It can be used to set the locale on the SessionLocaleResolver or if you have a custom resolver that's more geared towards request specific locale handling.

RequestContextUtils


org.springframework.web.servlet.support.RequestContextUtils provides utility methods to get locale and localeResolver, in case you need them for your own custom purposes.

    Locale curlocale = RequestContextUtils.getLocale(request);

    LocaleResolver localeResolver = RequestContextUtils.getLocaleResolver(request);

Sample Application


Check out the sample application that's configured with LocaleResolvers in this GitHub project.


Comments

Popular posts from this blog

How to Timeout JDBC Queries