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.

  1. ReloadableResourceBundleMessageSource is a special kind of MessageSource and is responsible for loading the messages property files. This bean can automatically load changes in the source properties file.
  2. CookieLocaleResolver writes the locale setting to a cookie with a fallback to the specified default locale or the request’s accept-header locale.
  3. 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/

spring mvc internationalization i18n

URL: http://localhost:8081/spring-mvc-i18n/?lang=fr

spring mvc internationalization i18n

URL: http://localhost:8081/spring-mvc-i18n/?lang=en

spring mvc internationalization i18n

And if we inspect the cookies for our application, we see that the my-locale-cookie has been set.

spring mvc internationalization i18n cookie

References

Download

You may also like...

  • Billel Bendjaballah

    change to classpath:messages for basename in xml config also