Spring MVC i18n Java Config Example

The i18n is the abbreviation of internationalization, this is because there are 18 characters between the first i and the last n in the word internationalization.

Its purpose is to display different language page content to different locale users in the same web application. This can make application development more efficient. Developers do not need to provide multiple web pages for each language user, only one web page is needed.

Spring i18n is supported by spring MVC, it uses the same web page to display different language content to different users by user locale. This article will show you an example of how to implement spring i18n in the spring MVC web application.

1. Spring MVC i18n Example.

  1. Before dive into the source code, let us watch the above example video.
  2. There are two navigation link rows on the page. The upper navigation link row is the language link, the different links use different languages as locale.
  3. The lower navigation link row is two pages link, click a different link will display the same web page with the upper selected language.

2. How To Implement Spring MVC i18n.

  1. First, let us look at the example project source file structure.
    D:\WORK\DEV2QA.COM-EXAMPLE-CODE\SPRINGBOOT\SPRINGMVCI18NEXAMPLE
    │  .classpath
    │  .project
    │  .springBeans
    │  pom.xml
    │
    ├─.settings
    │      org.eclipse.jdt.core.prefs
    │      org.eclipse.m2e.core.prefs
    │      org.eclipse.wst.common.component
    │      org.eclipse.wst.common.project.facet.core.xml
    │      org.eclipse.wst.validation.prefs
    │      org.springframework.ide.eclipse.beans.core.prefs
    │      org.springframework.ide.eclipse.core.prefs
    │
    ├─src
    │  ├─main
    │  │  ├─java
    │  │  │  └─com
    │  │  │      └─dev2qa
    │  │  │          └─i18n
    │  │  │              ├─config
    │  │  │              │      ContextConfig.java
    │  │  │              │      MyDispatcherServlet.java
    │  │  │              │
    │  │  │              └─controller
    │  │  │                      I18nController.java
    │  │  │
    │  │  ├─resources
    │  │  │  └─config
    │  │  │          messages.properties
    │  │  │          messages_en.properties
    │  │  │          messages_jp.properties
    │  │  │          messages_kr.properties
    │  │  │          messages_zh.properties
    │  │  │
    │  │  └─webapp
    │  │      └─WEB-INF
    │  │          └─views
    │  │                  about.jsp
    │  │                  home.jsp
    │  │                  language_bar.jsp
    │  │                  nav_bar.jsp
    │  │
    │  └─test
    │      └─resources
    │              log4j.xml

2.1 Create Message Resource Files For Each Language.

  1. All the properties files that begin with messages_ in src/main/resources/config folder are the message files. Each file contains text data for a special locale.
  2. The message file name format is messages+locale_name.properties ( for example messages_zh.properties contain the Chinese version of the message ). The messages.properties file is the default message text file.
  3. The messages_jp.properties, messages_kr.properties, messages_zh.properties file should be converted to Unicode format, you can read the article How To Convert East-Asian Character To Unicode In Java to learn how to convert.
  4. 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 the related messages.
  5. 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.
  6. 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.

  1. The com.dev2qa.i18n.config.ContextConfig class is the Java Config class in this example, we define below configuration beans in this example.
  2. The messageSource bean: This bean is used to locate messages files created in section 2.1.
  3. The 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 cookies.
  4. The localeInterceptor bean: Used to intercept request URL and parse out the request locale info from URL query string by parameter name. After defining this bean, you also need to register it to spring by overriding the addInterceptors method.
  5. jspViewResolver bean: Used to resolve returned view name to JSP web page path.
  6. 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.

  1. 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.

  1. In this controller class, it will use the @Autowired messageSource bean to get internationalization messages from the message file.
  2. And then add the message in the model attribute. Then in the JSP page source code, use ${attribute_name} to display the message.
  3. 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.

  1. JSP files are the view component in this example. You can display the messages in the i18n message file in the JSP file in two ways.
2.5.1 Display messages.properties message In JSP File Directly.
  1. Declare the spring taglib in the 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
  1. 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
  1. 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
  1. 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
  1. 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

  1. 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.

  1. Because we use CookieLocaleResolver to save user locale data in cookies, and in the example the cookie name is cookie-locale-info, we can see the cookie data in the web browser as below.
  2. Right-click the web page in chrome browser.
  3. Click Inspect menu item in the popup menu list.
  4. Click the Application tab, then click Cookies —> http://localhost:8080 in the 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

Reference

  1. Spring MVC Full Java Based Configuration Example
  2. How To Convert East-Asian Character To Unicode In Java

Leave a Comment

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.