Spring MVC Internationalization i18n Example
Internationalization is the process of designing your application for the international market; that is, make it able to support virtually any language, timezone or local setting. This tutorial shows how you can build Internationalized i18n web application using Spring MVC.
Project Structure
Make sure your project looks similar to the following structure. Note we configure the application using java configuration so we don’t need a web.xml
src
|--main
| +--java
| +--com
| +--memorynotfound
| +--config
| |--ServletInitializer.java
| |--WebConfig.java
| +--controller
| |--HomeController.java
| +--resources
| |--messages_en.properties
| |--messages_fr.properties
| +--webapp
| +--WEB-INF
| +--views
| |--index.jsp
pom.xml
Creating Localized Language Property Files
First, we create the localized language files. We can add virtually any language which we need to support. For simplicity, I added only English and French. Note, I translated the French using Google Translate. So, don’t mind the possible spelling/grammar errors.
Add the messages_en.properties to the src/main/resources/ folder.
welcome.message = Hello, {0}
welcome.greeting = Nice that you could join the meeting. We begin at {0}.
Add the messages_fr.properties to the src/main/resources/ folder.
welcome.message = Bonjour, {0}
welcome.greeting = Belle que vous pourriez participer à la réunion. Nous commençons à {0}.
Using Locale in Controller
You can either add the java.util.Locale
as an argument in the controller method – and spring will automatically inject it with the current locale – or you can obtain the current locale by calling the LocalContextHolder.getLocale()
method.
package com.memorynotfound.controller;
import org.springframework.stereotype.Controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.context.MessageSource;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.ui.Model;
import java.util.Locale;
@Controller
public class HomeController {
@Autowired
private MessageSource messageSource;
@RequestMapping(value = "/", method = RequestMethod.GET)
public String index(Locale locale, Model model){
// add parametrized message from controller
String welcome = messageSource.getMessage("welcome.message", new Object[]{"John Doe"}, locale);
model.addAttribute("message", welcome);
// obtain locale from LocaleContextHolder
Locale currentLocale = LocaleContextHolder.getLocale();
model.addAttribute("locale", currentLocale);
model.addAttribute("startMeeting", "10:30");
return "index";
}
}
Adding Parameterized Message Programatically
By injecting the MessageSource
in the controller we can create a parameterized message programatically using the MessageSource.getMessage(code, args, locale)
method. This MessageSource
takes care of loading the correct language message based on the given key and locale. We can optionally add some arguments.
@Autowired
private MessageSource messageSource;
String welcome = messageSource.getMessage(
"welcome.message",
new Object[]{"John Doe"},
locale);
Configure Spring MVC i18n
To support Spring MVC Internationalization (i18n), we need to configure 3 beans. A MessageSource
, LocaleResolver
and a LocaleInterceptor
.
ReloadableResourceBundleMessageSource
is a special kind ofMessageSource
and is responsible for loading the messages property files. This bean can automatically load changes in the source properties file.CookieLocaleResolver
writes the locale setting to a cookie with a fallback to the specified default locale or the request’s accept-header locale.LocaleChangeInterceptor
allows to change the current locale on every request via a configurable request parameter.
package com.memorynotfound.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.i18n.CookieLocaleResolver;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;
import java.util.Locale;
@EnableWebMvc
@Configuration
@ComponentScan({"com.memorynotfound"})
public class WebConfig extends WebMvcConfigurerAdapter {
@Bean
public ReloadableResourceBundleMessageSource messageSource(){
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename("classpath:messages");
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
@Bean
public CookieLocaleResolver localeResolver(){
CookieLocaleResolver localeResolver = new CookieLocaleResolver();
localeResolver.setDefaultLocale(Locale.ENGLISH);
localeResolver.setCookieName("my-locale-cookie");
localeResolver.setCookieMaxAge(3600);
return localeResolver;
}
@Bean
public LocaleChangeInterceptor localeInterceptor(){
LocaleChangeInterceptor interceptor = new LocaleChangeInterceptor();
interceptor.setParamName("lang");
return interceptor;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(localeInterceptor());
}
@Bean
public InternalResourceViewResolver viewResolver(){
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
}
The equivalent spring mvc internationalization configuration looks like this:
<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"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd ">
<mvc:annotation-driven />
<context:component-scan base-package="com.memorynotfound" />
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="classpath:messages"/>
<property name="defaultEncoding" value="UTF-8"/>
</bean>
<bean id="cookieResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver">
<property name="defaultLocale" value="en"/>
<property name="cookieName" value="my-locale-cookie"/>
<property name="cookieMaxAge" value="3600"/>
</bean>
<mvc:interceptors>
<bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
<property name="paramName" value="lang"/>
</bean>
</mvc:interceptors>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp" />
</bean>
</beans>
Adding Parameterized Message using Spring TagLibrary
This simple view illustrates how to change the default locale, and displays a couple of messages in the view. The first message is sent from the controller to the view. The second message uses spring taglib <spring:message />
which has an argument, this argument is automatically resolved by the taglib element. Finally, we display the current locale on the page.
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<html>
<head>
<title>Spring MVC Internationalization i18n Example</title>
</head>
<body>
<h1>Spring MVC Internationalization i18n Example</h1>
Language : <a href="?lang=en">English</a> | <a href="?lang=fr">French</a>
<p>${message}</p>
<p><spring:message code="welcome.greeting" arguments="${startMeeting}"/></p>
Current Locale : ${pageContext.response.locale} / ${locale}
</body>
</html>
Demo
URL: http://localhost:8081/spring-mvc-i18n/
URL: http://localhost:8081/spring-mvc-i18n/?lang=fr
URL: http://localhost:8081/spring-mvc-i18n/?lang=en
And if we inspect the cookies for our application, we see that the my-locale-cookie
has been set.
References
- Spring MVC LocaleResolver Documenation
- ReloadableResourceBundleMessageSource JavaDoc
- CookieLocaleResolver JavaDoc
- LocaleChangeInterceptor JavaDoc
- MessageSource JavaDoc
change to classpath:messages for basename in xml config also
Thanks for the suggestion. I’ve updated the article
when autowire Locale, MesageSource in component class , null is cmg. Any idea?
It’s difficult for me to asses your situation without seeing any code or configuration. Your problem makes me believe you haven’t correctly configured the
MessageSource
. Have you configured theResourceBundleMessageSource
orReloadableResourceBundleMessageSource
correctly in your spring configuration?Tutorial is excellent.
Please add details when we support non-english language where we need to save unicodes into properties file.
In that case jsp should have
contentType=”text/html; charset=UTF-8″ pageEncoding=”UTF-8″