Spring MVC Internationalization (i18n): Step by Step Guide

Internationalization (i18n) 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.

Spring framework is shipped with LocaleResolver and MessageSource interfaces to support internationalization and thus localization as well. The beans of these types allow the application to access String resources, called messages, stored in a variety of languages.

To implement i18n, for each language you maintain a list of messages in language-specific property files with identical keys. We get a localized message using the message key, and Spring returns the message from language-specific property file.

Let’s start analyzing the changes that you will need to make to add i18n support to your spring web application.

Step 1: Add Locale-specific Message Resources

If you want to support multiple locales, then the first step is to have each locale-specific properties file have texts in that locale-specific language.

In our example, I am supporting two locales. The first is the United States with language English, and the second locale is Chinese. The US locale is the default.

/src.main/resources/messages.properties

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

/src.main/resources/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 property files. Locale-specific files have locale short-code appended to their name.

Additionally, define the base name of the message bundle files in application.properties file.

spring.messages.basename=messages

Step 2: Adding LocaleResolver Bean

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

2.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
public LocaleResolver localeResolver() {
  SessionLocaleResolver localeResolver = new SessionLocaleResolver();
  localeResolver.setDefaultLocale(Locale.US); // Set the default locale
  return localeResolver;
}

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

The default parameter name is ‘locale‘.

@Bean
public LocaleChangeInterceptor localeChangeInterceptor() {
  LocaleChangeInterceptor interceptor = new LocaleChangeInterceptor();
  interceptor.setParamName("lang"); // Set the request parameter name to change the locale
  return interceptor;
}

The complete application context file for this application looks like this:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.i18n.SessionLocaleResolver;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;

import java.util.Locale;

@Configuration
public class WebConfig implements WebMvcConfigurer {

  // Define the LocaleResolver bean
  @Bean
  public LocaleResolver localeResolver() {
    SessionLocaleResolver localeResolver = new SessionLocaleResolver();
    localeResolver.setDefaultLocale(Locale.US); // Set the default locale
    return localeResolver;
  }

  // Define the LocaleChangeInterceptor bean
  @Bean
  public LocaleChangeInterceptor localeChangeInterceptor() {
    LocaleChangeInterceptor interceptor = new LocaleChangeInterceptor();
    interceptor.setParamName("lang"); // Set the request parameter name to change the locale
    return interceptor;
  }

  @Override
  public void addInterceptors(InterceptorRegistry registry) {
    // Register the LocaleChangeInterceptor
    registry.addInterceptor(localeChangeInterceptor());
  }
}

Step 3: View Template Changes to Display Locale-specific Messages

The next step is to make view changes to support displaying locale-specific text messages. This example uses JSPs so it can be done using spring TLDs in the below manner. Notice the use of ‘spring:message‘ tags to print the messages.

<%@ 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>

Step 4: Accessing Localized Messages in Controller

While sending API responses, if you want to access the localized messages, you can inject the MessageSource bean in the controller class and use messageSource.getMessage() method.

The following is a sample controller class that access the localized messages in API controller.

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

import java.util.Locale;

@Controller
public class GreetingController {

    @Autowired
    private MessageSource messageSource;

    @GetMapping("/greeting")
    public String greeting(@RequestParam(name = "locale", required = false) String localeParam, Model model) {
        Locale locale = Locale.getDefault();

        if (localeParam != null && !localeParam.isEmpty()) {
            locale = new Locale(localeParam);
        }

        String greetingMessage = messageSource.getMessage("greeting", null, "Default Greeting", locale);
        model.addAttribute("greeting", greetingMessage);

        return "greeting";
    }
}

Step 5: Test 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

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

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

Spring i18n - CN Locale
Spring i18n – CN Locale

Other Project Files

EmployeeController.java

@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

import java.util.List;
import com.howtodoinjava.demo.model.EmployeeVO;
 
public interface EmployeeDAO 
{
  public List<EmployeeVO> getAllEmployees();
}

EmployeeDAOImpl.java

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

import java.io.Serializable;
 
public class EmployeeVO implements Serializable 
{
  private static final long serialVersionUID = 1L;
 
  private Integer id;
  private String firstName;
  private String lastName;
 
  //Getters and setters
}

Please let me know if any queries or thoughts.

Happy Learning !!

Comments

Subscribe
Notify of
guest
6 Comments
Most Voted
Newest Oldest
Inline Feedbacks
View all comments

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.

Our Blogs

REST API Tutorial

Dark Mode

Dark Mode