Spring MVC Internationalization (i18n) and Localization (i10n) Example

Internationalization is the process of designing a software application so that it can potentially be adapted to various languages and regions without engineering changes. Localization is the process of adapting internationalized software for a specific region or language by adding locale-specific components and translating text [Wiki]. Spring framework is shipped with LocaleResolver to support the internationalization and thus localization as well. This tutorial will help you in learning how to add internationalization support in your spring mvc based web application.

Table of Contents

1) Adding locale specific message resources
2) Adding LocaleResolver configuration in spring context
3) JSP changes to display locale specific messages
4) Project Structure
5) Testing the Application
6) Other Project Files

Let’s start analyzing the changes which you will need to make for adding i18n support into your spring web application.

1) Adding locale specific message resources

If you want to support multiple locales, then first step is obliviously to have each locale specific properties file having texts in that locale specific language. In our example, I am supporting two locales. First is United States with language English, and second locale is Chinese.

messages.properties

lbl.Id=Employee Id
lbl.firstName=First Name
lbl.lastName=First Name
lbl.page=All Employees in System

messages_zh_CN.properties

lbl.Id=\u5458\u5DE5ID
lbl.firstName=\u540D\u5B57
lbl.lastName=\u59D3
lbl.page=\u7CFB\u7EDF\u4E2D\u7684\u6240\u6709\u5458\u5DE5

Notice the naming convention for these properties files. Locale specific files have locale short-code appended into their name.

2) Adding LocaleResolver configuration in spring context

To make Spring MVC application supports the internationalization, you will need to register two beans.

1. SessionLocaleResolver

SessionLocaleResolver resolves locales by inspecting a predefined attribute in a user’s session. If the session attribute doesn’t exist, this locale resolver determines the default locale from the accept-language HTTP header.

<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver">
	<property name="defaultLocale" value="en" />
</bean>

2. LocaleChangeInterceptor

LocaleChangeInterceptor interceptor detects if a special parameter is present in the current HTTP request. The parameter name can be customized with the paramName property of this interceptor. If such a parameter is present in the current request, this interceptor changes the user’s locale according to the parameter value.

<bean id="localeChangeInterceptor" class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
	<property name="paramName" value="lang" />
</bean>

<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
	<property name="interceptors">
		<list>
			<ref bean="localeChangeInterceptor" />
		</list>
	</property>
</bean>

The complete application context file for this application 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"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context/
        http://www.springframework.org/schema/context/spring-context-3.0.xsd">

	<context:component-scan base-package="com.howtodoinjava.demo" />

	<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" />
	
	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix" value="/WEB-INF/views/" />
		<property name="suffix" value=".jsp" />
	</bean>
	
	<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
	    <property name="basename" value="messages" />
	</bean>
	
	<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver">
		<property name="defaultLocale" value="en" />
	</bean>
 
	<bean id="localeChangeInterceptor" class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
		<property name="paramName" value="lang" />
	</bean>
	
	<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
		<property name="interceptors">
            <list>
                <ref bean="localeChangeInterceptor" />
            </list>
        </property>
	</bean>
 
</beans>

3) JSP changes to display locale specific messages

Next step is to make view changes to support displaying locale specific text messages. This can be done using spring TLDs in below manner.

<%@ page contentType="text/html;charset=UTF-8" %>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>

<html>
<head>
	<title>Spring MVC Hello World</title>
</head>

<body>
	<h2><spring:message code="lbl.page" text="All Employees in System" /></h2>

	<table border="1">
		<tr>
			<th><spring:message code="lbl.Id" text="Employee Id" /></th>
			<th><spring:message code="lbl.firstName" text="First Name" /></th>
			<th><spring:message code="lbl.lastName" text="Last Name" /></th>
		</tr>
		<c:forEach items="${employees}" var="employee">
			<tr>
				<td>${employee.id}</td>
				<td>${employee.firstName}</td>
				<td>${employee.lastName}</td>
			</tr>
		</c:forEach>
	</table>

</body>
</html>

4) Project Structure

The complete structure for this application is this:

Spring i18n project structure and files
Spring i18n project structure and files

5) Testing the Application

Hit the URL : http://localhost:8080/springmvcexample/employee-module/getAllEmployees

As you see that all labels are displayed in English language.

Spring i18n - En Locale
Spring i18n – En Locale

Hit the URL : http://localhost:8080/springmvcexample/employee-module/getAllEmployees?lang=zh_CN

Now, locale has been changed to chinese and all labels are displayed in chinese language.

Spring i18n - CN Locale
Spring i18n – CN Locale

6) Other Project Files

Let’s list down other files involved into this application.

web.xml

<web-app id="WebApp_ID" version="2.4" 
	xmlns="http://java.sun.com/xml/ns/j2ee" 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee 
	https://www.oracle.com/java/technologies/;
	
	<display-name>Spring Web MVC Hello World Application</display-name>
	
	<servlet>
		<servlet-name>spring</servlet-name>
			<servlet-class>
				org.springframework.web.servlet.DispatcherServlet
			</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>

	<servlet-mapping>
		<servlet-name>spring</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>

</web-app>

EmployeeController.java

package com.howtodoinjava.demo.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.howtodoinjava.demo.service.EmployeeManager;

@Controller
@RequestMapping("/employee-module")
public class EmployeeController 
{
	@Autowired
	EmployeeManager manager;
	
	@RequestMapping(value="/getAllEmployees", method = RequestMethod.GET)
    public String welcome(Model model) 
	{
		model.addAttribute("employees",manager.getAllEmployees());
        return "employeesListDisplay";
    }
}

EmployeeDAO.java

package com.howtodoinjava.demo.dao;

import java.util.List;
import com.howtodoinjava.demo.model.EmployeeVO;

public interface EmployeeDAO 
{
	public List<EmployeeVO> getAllEmployees();
}

EmployeeDAOImpl.java

package com.howtodoinjava.demo.dao;

import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Repository;
import com.howtodoinjava.demo.model.EmployeeVO;

@Repository
public class EmployeeDAOImpl implements EmployeeDAO {

	public List<EmployeeVO> getAllEmployees() 
	{
		List<EmployeeVO> employees = new ArrayList<EmployeeVO>();
		
		EmployeeVO vo1 = new EmployeeVO();
		vo1.setId(1);
		vo1.setFirstName("Lokesh");
		vo1.setLastName("Gupta");
		employees.add(vo1);
		
		EmployeeVO vo2 = new EmployeeVO();
		vo2.setId(2);
		vo2.setFirstName("Raj");
		vo2.setLastName("Kishore");
		employees.add(vo2);
		
		return employees;
	}
}

EmployeeManager.java

import java.util.List;
import com.howtodoinjava.demo.model.EmployeeVO;

public interface EmployeeManager 
{
	public List<EmployeeVO> getAllEmployees();
}

EmployeeManagerImpl.java

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.howtodoinjava.demo.dao.EmployeeDAO;
import com.howtodoinjava.demo.model.EmployeeVO;

@Service
public class EmployeeManagerImpl implements EmployeeManager {

	@Autowired
	EmployeeDAO dao;
	
	public List<EmployeeVO> getAllEmployees() 
	{
		return dao.getAllEmployees();
	}
}

EmployeeVO.java

package com.howtodoinjava.demo.model;

import java.io.Serializable;

public class EmployeeVO implements Serializable 
{
	private static final long serialVersionUID = 1L;

	private Integer id;
	private String firstName;
	private String lastName;

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public String getFirstName() {
		return firstName;
	}

	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}

	public String getLastName() {
		return lastName;
	}

	public void setLastName(String lastName) {
		this.lastName = lastName;
	}

	@Override
	public String toString() {
		return "EmployeeVO [id=" + id + ", firstName=" + firstName
				+ ", lastName=" + lastName + "]";
	}
}

Please let me know if any queries or thoughts.

Happy Learning !!

Was this post helpful?

Join 7000+ Fellow Programmers

Subscribe to get new post notifications, industry updates, best practices, and much more. Directly into your inbox, for free.

6 thoughts on “Spring MVC Internationalization (i18n) and Localization (i10n) Example”

  1. Hi! I have an issue with something in the xml configuration file, possibly this part:

    &amp;amp;lt;bean class=&amp;amp;quot;org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping&amp;amp;quot;&amp;amp;gt;
            &amp;amp;lt;property name=&amp;amp;quot;interceptors&amp;amp;quot;&amp;amp;gt;
                &amp;amp;lt;list&amp;amp;gt;
                    &amp;amp;lt;ref bean=&amp;amp;quot;localeChangeInterceptor&amp;amp;quot; /&amp;amp;gt;
                &amp;amp;lt;/list&amp;amp;gt;
            &amp;amp;lt;/property&amp;amp;gt;
        &amp;amp;lt;/bean&amp;amp;gt;
    

    I get the following error:

    org.springframework.beans.factory.CannotLoadBeanClassException: Cannot find class [org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter] for bean with name 'org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter#0'
    


    Is it because I’m mixing different “configuration styles”? Here’s my whole file:

    &amp;amp;lt;?xml version=&amp;amp;quot;1.0&amp;amp;quot; encoding=&amp;amp;quot;UTF-8&amp;amp;quot;?&amp;amp;gt;
    &amp;amp;lt;beans xmlns=&amp;amp;quot;http://www.springframework.org/schema/beans&amp;amp;quot;
    	xmlns:xsi=&amp;amp;quot;http://www.w3.org/2001/XMLSchema-instance&amp;amp;quot;
    	xmlns:context=&amp;amp;quot;http://www.springframework.org/schema/context&amp;amp;quot;
    	xmlns:mvc=&amp;amp;quot;http://www.springframework.org/schema/mvc&amp;amp;quot;
    	xsi:schemaLocation=&amp;amp;quot;
    		http://www.springframework.org/schema/beans/
        	http://www.springframework.org/schema/beans/spring-beans.xsd
        	http://www.springframework.org/schema/context/
        	http://www.springframework.org/schema/context/spring-context.xsd
        	http://www.springframework.org/schema/mvc/
            http://www.springframework.org/schema/mvc/spring-mvc.xsd&amp;amp;quot;&amp;amp;gt;
    
    	&amp;amp;lt;!-- Step 3: Add support for component scanning --&amp;amp;gt;
    	&amp;amp;lt;context:component-scan
    		base-package=&amp;amp;quot;com.jdgamboa&amp;amp;quot; /&amp;amp;gt;
    		
    	&amp;amp;lt;bean class=&amp;amp;quot;org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter&amp;amp;quot; /&amp;amp;gt;
    
    	&amp;amp;lt;!-- Step 4: Add support for conversion, formatting and validation support --&amp;amp;gt;
    	&amp;amp;lt;mvc:annotation-driven validator=&amp;amp;quot;validator&amp;amp;quot;&amp;amp;gt;
    	&amp;amp;lt;/mvc:annotation-driven&amp;amp;gt;
    
    	&amp;amp;lt;!-- Localization of hibernate messages during validation! --&amp;amp;gt;
    	&amp;amp;lt;bean id=&amp;amp;quot;validationMessageSource&amp;amp;quot;
    		class=&amp;amp;quot;org.springframework.context.support.ReloadableResourceBundleMessageSource&amp;amp;quot;&amp;amp;gt;
    		&amp;amp;lt;property name=&amp;amp;quot;basename&amp;amp;quot; value=&amp;amp;quot;classpath:validation&amp;amp;quot; /&amp;amp;gt;
    	&amp;amp;lt;/bean&amp;amp;gt;
    
    	&amp;amp;lt;bean name=&amp;amp;quot;validator&amp;amp;quot;
    		class=&amp;amp;quot;org.springframework.validation.beanvalidation.LocalValidatorFactoryBean&amp;amp;quot;&amp;amp;gt;
    		&amp;amp;lt;property name=&amp;amp;quot;validationMessageSource&amp;amp;quot;&amp;amp;gt;
    			&amp;amp;lt;ref bean=&amp;amp;quot;validationMessageSource&amp;amp;quot; /&amp;amp;gt;
    		&amp;amp;lt;/property&amp;amp;gt;
    	&amp;amp;lt;/bean&amp;amp;gt;
    
    	&amp;amp;lt;!-- Step 5: Define Spring MVC view resolver --&amp;amp;gt;
    	&amp;amp;lt;bean
    		class=&amp;amp;quot;org.springframework.web.servlet.view.InternalResourceViewResolver&amp;amp;quot;&amp;amp;gt;
    		&amp;amp;lt;property name=&amp;amp;quot;prefix&amp;amp;quot; value=&amp;amp;quot;/WEB-INF/view/&amp;amp;quot; /&amp;amp;gt;
    		&amp;amp;lt;property name=&amp;amp;quot;suffix&amp;amp;quot; value=&amp;amp;quot;.jsp&amp;amp;quot; /&amp;amp;gt;
    	&amp;amp;lt;/bean&amp;amp;gt;
    
    	&amp;amp;lt;bean id=&amp;amp;quot;messageSource&amp;amp;quot;
    		class=&amp;amp;quot;org.springframework.context.support.ResourceBundleMessageSource&amp;amp;quot;&amp;amp;gt;
    		&amp;amp;lt;property name=&amp;amp;quot;basenames&amp;amp;quot; value=&amp;amp;quot;message&amp;amp;quot; /&amp;amp;gt;
    	&amp;amp;lt;/bean&amp;amp;gt;
    
    	&amp;amp;lt;!-- Add support for reading web resources: css, images, js, etc ... --&amp;amp;gt;
    	&amp;amp;lt;mvc:resources location=&amp;amp;quot;/resources/&amp;amp;quot;
    		mapping=&amp;amp;quot;/resources/**&amp;amp;quot;&amp;amp;gt;&amp;amp;lt;/mvc:resources&amp;amp;gt;
    		
    	&amp;amp;lt;bean id=&amp;amp;quot;localeResolver&amp;amp;quot;
    		class=&amp;amp;quot;org.springframework.web.servlet.i18n.SessionLocaleResolver&amp;amp;quot;&amp;amp;gt;
    		&amp;amp;lt;property name=&amp;amp;quot;defaultLocale&amp;amp;quot; value=&amp;amp;quot;es&amp;amp;quot; /&amp;amp;gt;
    	&amp;amp;lt;/bean&amp;amp;gt;
    
    	&amp;amp;lt;bean id=&amp;amp;quot;localeChangeInterceptor&amp;amp;quot; class=&amp;amp;quot;org.springframework.web.servlet.i18n.LocaleChangeInterceptor&amp;amp;quot;&amp;amp;gt;
            &amp;amp;lt;property name=&amp;amp;quot;paramName&amp;amp;quot; value=&amp;amp;quot;lang&amp;amp;quot; /&amp;amp;gt;
        &amp;amp;lt;/bean&amp;amp;gt;
         
        &amp;amp;lt;bean class=&amp;amp;quot;org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping&amp;amp;quot;&amp;amp;gt;
            &amp;amp;lt;property name=&amp;amp;quot;interceptors&amp;amp;quot;&amp;amp;gt;
                &amp;amp;lt;list&amp;amp;gt;
                    &amp;amp;lt;ref bean=&amp;amp;quot;localeChangeInterceptor&amp;amp;quot; /&amp;amp;gt;
                &amp;amp;lt;/list&amp;amp;gt;
            &amp;amp;lt;/property&amp;amp;gt;
        &amp;amp;lt;/bean&amp;amp;gt;
    
    &amp;amp;lt;/beans&amp;amp;gt;
    

    Thanks!

  2. I want to know why the local script format changes in properties file when we write in Chines or Hindi..Thanks

  3. Hi there! For an app localization project, I have tried POEditor, a collaborative translation management platform, but before that I used this free converter tool http://yml2po.com/ to convert yaml to po files. I think it’s a good solution, I recommend trying it. Thanks, Happy Holidays!

Comments are closed.

HowToDoInJava

A blog about Java and its related technologies, the best practices, algorithms, interview questions, scripting languages, and Python.