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.