Spring MVC i18n Java Config Example

i18n is the abbreviation of internationalization, this is because there are 18 character between the first i and the last n. It’s purpose is to display different languages page content to different locale users in same web application. This can make application development more efficiency. Developer do not need to provide multiple web pages for each language user, only one web page is needed.

Spring MVC support i18n by default, it use same web page to display different languages content to different user by user locale. This article will show you an example about how to implement i18n in spring mvc web application.

1. Spring MVC i18n Example.

Before dive into the source code, let us see below example web page first.

There are two navigation links in the page. The up navigation link is language link, different link use different language as locale. The down navigation link is two pages link, click different link will display different web page with the up link selected language.

2. How To Implement Spring MVC i18n.

First let us look at the example project source file structure.

spring mvc i18n example project files

2.1 Create Message Resource Files For Each Language.

All the properties files that begin with messages_ in src/main/resources/config folder is the message files. Each file contains text data for special locale. The message file name format is messages+locale_name.properties. The messages.properties is the default message file.

The messages_jp.properties, messages_kr.properties and messages_zh.properties file should be converted to unicode format, you can read article How To Convert East-Asian Character To Unicode In Java to learn how to convert.

Below are two messages file content, other files content is similar. You can see messages in the file are key value paired, just use the key in your source code to get related message.

messages.properties

home.title = Home Page
home.welcome = Hello, {0}, {1}, This page use default messages.properties file.
about.title = About Page
about.description = dev2qa.com is maintained by experienced software engineer.

messages_zh.properties

home.title = \u4E3B\u9875
home.welcome = \u4F60\u597D, {0}, {1}.
about.title = \u5173\u4E8E
about.description = dev2qa.com \u7684\u6587\u7AE0\u90FD\u662F\u7531\u7ECF\u9A8C\u4E30\u5BCC\u7684\u5F00\u53D1\u4EBA\u5458\u64B0\u5199.

2.2 Configure messageSource, localeResolver and localeInterceptor Beans In Java Config Class.

com.dev2qa.i18n.config.ContextConfig class is the Java Config class in this example, we define below configuration beans in this example.

  1. messageSource bean: This bean is used to locate messages files created in 2.1.
  2. localeResolver bean: Used to resolve user locale information. We use an instance of org.springframework.web.servlet.i18n.CookieLocaleResolver to save user locale data in cookie.
  3. localeInterceptor bean: Used to intercept request url and parse out the request locale info from url query string by parameter name. After define this bean, you also need to register it to spring by override addInterceptors method.
  4. jspViewResolver bean: Used to resolve returned view name to jsp web page path.

ContextConfig.java

package com.dev2qa.i18n.config;

import java.util.Locale;

import org.springframework.context.MessageSource;
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.i18n.SessionLocaleResolver;
import org.springframework.web.servlet.view.InternalResourceViewResolver;

@EnableWebMvc
@Configuration
@ComponentScan({"com.dev2qa"})
public class ContextConfig extends WebMvcConfigurerAdapter {

    /* The jsp view resolver is spring built-in view resolver, it will return jsp pages saved in webapp/WEB-INF/views folder.
     * The Controller's RequestMapping method returned string will be the jsp file name.
     * And spring will use the return string to build the jsp page file path with below format webapp/WEB-INF/views/return_string.jsp
     * */
    @Bean(name = "jspViewResolver")
    public InternalResourceViewResolver getJspViewResolver() {

        InternalResourceViewResolver ret = new InternalResourceViewResolver();
        // The prefix of the Controller's RequestMapping method returned string.
        ret.setPrefix("/WEB-INF/views/");
        // The suffix of the Controller's RequestMapping method returned string.
        ret.setSuffix(".jsp");
        ret.setOrder(1);
        return ret;

    }


    /* messageSource bean is spring built-in bean name which will manipulate internationalization messages.
     * All message files is saved in src/main/resources/config/ folder, if the config folder do not exist, you need to create it first by hand.
     * Each message file is a properties file, the file base name is messages and suffix with the language or country ISO code, such as messages_en, messages_zh_cn etc.
     * */
    @Bean(name = "messageSource")
    public MessageSource getMessageSource() {
        ReloadableResourceBundleMessageSource ret = new ReloadableResourceBundleMessageSource();

        // Set the base name for the messages properties file. 
        ret.setBasename("classpath:config/messages");

        ret.setCacheSeconds(1);

        ret.setUseCodeAsDefaultMessage(true);

        ret.setDefaultEncoding("utf-8");

        return ret;
    }


    /* The localeResolver is used to resolve user locale data. The CookieLocaleResolver object is used to save user locale information in browser cookie.
     * This way user locale info can be transferred between request. If user disable cookie then you can use SessionLocaleResolver instead. */
    @Bean(name = "localeResolver")
    public CookieLocaleResolver getCookieLocaleResolver(){
        // Create a CookieLocaleResolver object.
        CookieLocaleResolver localeResolver = new CookieLocaleResolver();
        // Set cookie name
        localeResolver.setCookieName("cookie-locale-info");
        // Set default locale value.
        localeResolver.setDefaultLocale(Locale.ENGLISH);
        // Set cookie max exist time.
        localeResolver.setCookieMaxAge(3600);

        return localeResolver;
    }
   
   /* If user disable cookie then you can use SessionLocaleResolver instead. */
   /*@Bean(name = "localeResolver")
    public SessionLocaleResolver getSessionLocaleResolver(){
      // Create a SessionLocaleResolver object.
      SessionLocaleResolver localeResolver = new SessionLocaleResolver();
        // Set default locale in session.
      localeResolver.setDefaultLocale(Locale.ENGLISH);
        return localeResolver;
    }*/


    /* The LocaleChangeInterceptor is a interceptor that will intercept user locale change by a parameter value. 
     * For example, if we set the locale change parameter name is locale, then request url http://localhost:8088/index.jsp?locale=en will change 
     * user locale to en and display messages in src/main/resources/config/messages_en.properties.
     *  */
    @Bean(name="localeInterceptor")
    public LocaleChangeInterceptor getLocaleInterceptor(){
        LocaleChangeInterceptor interceptor = new LocaleChangeInterceptor();
        interceptor.setParamName("lang");
        return interceptor;
    }

    /* Also need to register above locale interceptor in spring then it will intercept user request url and parse out the lang query string to get user request locale.*/
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(getLocaleInterceptor());
    }
}

2.3 Create Annotation Configured Dispatcher Servlet.

MyDispatcherServlet.java

package com.dev2qa.i18n.config;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;

public class MyDispatcherServlet implements WebApplicationInitializer {

    public void onStartup(ServletContext servletContext) throws ServletException {
        // Get annotation config web app context object.
        AnnotationConfigWebApplicationContext dispatcherServletContext = new AnnotationConfigWebApplicationContext();
        // Register servlet contex.
        dispatcherServletContext.register(ContextConfig.class);
        // Create dispatcher servlet.
        DispatcherServlet dispatcherServlet = new DispatcherServlet(dispatcherServletContext);
        // Add above dispatcher servlet to servlet context.
        ServletRegistration.Dynamic dispatcher = servletContext.addServlet("spring-i18n-example", dispatcherServlet);
        // Set dispatcher servlet load order.
        dispatcher.setLoadOnStartup(1);
        // This servlet will process all request url end with .html
        dispatcher.addMapping("*.html");
    }
}

2.4 Example Controller Class.

In this controller class, it will use the @Autowired messageSource bean to get internationalization messages from message file. And then add the message in model attribute. Then in the jsp page, use ${attribute_name} to display the message.

READ :   Spring MVC Login Example

I18nController.java

package com.dev2qa.i18n.controller;

import java.util.Locale;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.WebApplicationContext;

@Controller
public class I18nController {

    /* Autowired messageSource bean defined in ContextConfig.java */
    @Autowired
    private MessageSource messageSource;

    // Process request to url http://localhost:8080/i18n/home.html.
    @RequestMapping("/home.html")
    public String displayHomePage(Locale locale, Model model) {

        String nameArr[] = {"Jerry", "Richard"};

        // Get messages by locale value and add parameterized message for the message.
        String homePageWelcomeMessage = messageSource.getMessage("home.welcome", nameArr, locale);
        model.addAttribute("message", homePageWelcomeMessage);

        // obtain locale from LocaleContextHolder
        Locale currentLocale = LocaleContextHolder.getLocale();
        model.addAttribute("locale", currentLocale);

        return "home";
    }

    // Process request to url http://localhost:8080/i18n/about.html.
    @RequestMapping("/about.html")
    public String displayAboutPage(Locale locale, Model model) {

        // obtain locale from LocaleContextHolder
        Locale currentLocale = LocaleContextHolder.getLocale();
        model.addAttribute("locale", currentLocale);

        return "about";
    }
}

2.5 JSP Files.

JSP files is the view component in this example. You can display the messages in the i18n message file in jsp file in two ways.

2.5.1 Display messages.properties message in jsp file directly.
  1. Declare spring taglib in jsp file header.
    <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> 
  2.  Display messages in jsp file with spring tag and message key.
    <spring:message code="home.title"/>
    
2.5.2 Display message transferred through model attribute.
  1. ${attribute_name} : For example ${message}, ${locale}
2.5.3 home.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title><spring:message code="home.title"/></title>
</head>
<body>

    <jsp:include page="./language_bar.jsp"></jsp:include>

    <jsp:include page="./nav_bar.jsp"></jsp:include>

    ${message}<br/><br/>

    Your Locale is ${pageContext.response.locale} / ${locale}
</body>
</html>
2.5.4 about.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title><spring:message code="about.title"/></title>
</head>
<body>

    <jsp:include page="./language_bar.jsp"></jsp:include>

    <jsp:include page="./nav_bar.jsp"></jsp:include>

    <spring:message code="about.description"/>
</body>
</html>
2.5.5 language_bar.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Insert title here</title>
</head>
<body>
    <a href="${pageContext.request.contextPath}/home.html?lang=en">English</a>&nbsp;&nbsp;&nbsp;
    <a href="${pageContext.request.contextPath}/home.html?lang=zh">Chinese</a>&nbsp;&nbsp;&nbsp;
    <a href="${pageContext.request.contextPath}/home.html?lang=jp">Japanese</a>&nbsp;&nbsp;&nbsp;
    <a href="${pageContext.request.contextPath}/home.html?lang=kr">Korean</a>
    <br/><br/>
</body>
</html>
2.5.6 nav_bar.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Insert title here</title>
</head>
<body>
    <a href="${pageContext.request.contextPath}/home.html">Home</a> &nbsp;&nbsp;&nbsp;<a href="${pageContext.request.contextPath}/about.html">About</a><br/><br/>
</body>
</html>

2.6 pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.dev2qa</groupId>
    <artifactId>i18n</artifactId>
    <name>SpringMVCi18nExample</name>
    <packaging>war</packaging>
    <version>1.0.0-BUILD-SNAPSHOT</version>

    <!-- Spring framework used constants variable  -->
    <properties>
        <spring.version>5.0.0.BUILD-SNAPSHOT</spring.version>
        <servlet.api.version>3.1.0</servlet.api.version>
    </properties>

    <repositories>
        <!-- This repository is where the spring framework dependencies jar file download. -->
        <repository>
            <id>spring-snapshots</id>
            <name>Spring Snapshots</name>
            <url>https://repo.spring.io/libs-snapshot</url>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </repository>
    </repositories>

    <dependencies>
        <!-- Below are the basic spring container dependencies library. -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <!-- Below dependency is used for spring mvc framework. -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <!-- Below dependency is used for servlet and jsp. -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>${servlet.api.version}</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>javax.servlet.jsp-api</artifactId>
            <version>2.3.1</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>

        <dependency>
            <groupId>taglibs</groupId>
            <artifactId>standard</artifactId>
            <version>1.1.2</version>
        </dependency>

    </dependencies>

    <build>
        <finalName>spring-mvc-i18n</finalName>
        <plugins>
            <!-- Because this project do not use web.xml so need below settings. -->
            <plugin>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.3</version>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.2</version>
                <configuration>
                    <username>tomcat</username>
                    <password>tomcat</password>
                    <!-- update true will override the existing deployed war file in tomcat -->
                    <update>true</update>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.7.0</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

3. Look At Request Locale Cookie.

Because we use CookieLocaleResolver to save user locale data in cookie, and in the example the cookie name is cookie-locale-info, we can see the cookie data in web browser as below.

  1. Right click the web page in chrome browser.
  2. Click Inspect menu item in the popup menu list.
  3. Click Application tab, then click Cookies —> http://localhost:8080 in left list panel, in the right panel you can see the cookie-locale-info and it’s value.
    see locale info in cookie with chrome web browser
READ :   Spring Bean Dependency Injection Methods Comparison - Setter Vs Constructor

Reference

  1. Spring MVC Full Java Based Configuration Example
  2. How To Convert East-Asian Character To Unicode In Java
(Visited 16 times, 1 visits today)

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.