Spring MVC Internationalization Tutorial

Internationalization (i18n) is the process of making your application and services capable of delivering in different languages. This tutorial will show how the Spring Boot framework manages internationalization.

Overview

The internet has become global, implying that any application or website can target millions of users worldwide. Although half of the world’s population may have access to the internet today, only about 1/4th of world users are English speakers. To expand to new markets, software developers must develop content in multi-languages.
 
Internationalization is a basic need for most applications, and with Spring Boot, it can be achieved very simplySpring boot provides extended support for Internationalization (i18n) through Spring Interceptor, Locale Resolvers, and Resource Bundles for different locales. This article will take you to understand how internationalization can be achieved in Spring Boot.

Preparation:

In this example, I’ll be using the following tools:

  • JDK 1.8
  • Intellij Idea
  • Maven
  • Springboot Application version 2.5.5

Structure of the post:

In addition, the development outline of this blog will be as follow:

  1. Create Spring Boot Web Application
  2. Include Dependencies
  3. Create Configuration beans
  4. Create Controller beans
  5. Multilingual properties
  6. Implement a View
  7. Running the Application

Step1: Create Spring Boot Web Application

Firstly, we create a simple Spring Boot web application.  You can use the Spring Initializr page to create an initial project template.
In this tutorial, we will be using a project with the following metadata.

 

After importing the project into IDE, we will be creating two sub-packages inside our main package ‘com.appsdeveloperblog.springbooti18n‘ as follows:

  • Controller:  This contains class ‘InternationalController‘ with filename ‘InternationalController.java
  • Config:  This contains class ‘InternationalizationConfig‘ with filename ‘InternationalizationConfig.java

In the end, the final project structure will look like this:

 

We will learn aspects about the config, and controller classes in detail in the next section.

Step 2: Include Dependencies

After that, we need to include the following dependencies in our application:
  • Spring Boot Starter Web,
  • Spring Boot Starter Thymeleaf 
The source code of  “pom.xml” is as follows:
<?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/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.appsdeveloperblog</groupId>
    <artifactId>spring-boot-i18n</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>appsdeveloperblog-spring-boot-i18n</name>
    <description>Spring Boot i18n demo</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.0</version>
        <relativePath/>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

Step 3: Create Configuration Bean

Spring uses LocaleResolver to extract locale related to web requests and uses this locale object to select appropriate language messages. First, it requires a bean of LocaleResolver. We will discuss this in detail below:

Locale Resolver

There are many LocaleResolverType provided by spring, but the most common areas are below:

Type Explanation
AcceptHeaderLocaleResolver This simply selects Locale from the Accept-Language header sent by the client web browser
CookieLocaleResolver Get locale information using cookies. The setLocale() method stores Locale information in a cookie. Here,
Resolved locale data is persisted as long as cookies are alive
SessionLocaleResolver Get Locale information from the session. The setLocale() method stores Locale information in the session.
Here, Resolved locale data is persisted in a single HTTP session.
FixedLocaleResolver Fixed Locale is used. no setLocale() method

Spring provides various other locale resolvers under the standard package “org.springframework.web.servlet.i18n.
For more details, refer to the following official link:
 org.springframework.web.servlet.i18n.

Let’s understand CookieLocaleResolver and SessionLocaleResolver locale types in detail:

CookieLocaleResolver

If you use locale object based on the user’s PC or browser settings then, it cannot be changed when the client wants to view the webpage in a different language. Therefore, the Locale setting using Cookie is another option as below. Now, based on user preference we can set the locale in the cookies.  A sample code for locale settings using CookieLocaleResolver is as below:

@Bean
public CookieLocaleResolver localeResolver() {
  CookieLocaleResolver localeResolver = new CookieLocaleResolver();
  localeResolver.setDefaultLocale(Locale.US); 
  localeResolver.setCookieName("locale"); 
  localeResolver.setCookieMaxAge(60 * 60); 
  localeResolver.setCookiePath("/"); 
  return localeResolver;
}

SessionLocaleResolver

If a user wants to set locale to each individual HTTP session, then SessionLocaleResolver is favored:

@Bean
public LocaleResolver localeResolver() {
    SessionLocaleResolver localeResolver = new SessionLocaleResolver();
    localeResolver.setDefaultLocale(Locale.US);
    return localeResolver;
}

In our example, we will use this “SessionLocaleResolver” for simplicity.

Interceptor

Secondly, we require another bean for the interceptor in our class.

LocaleChangeInterceptor is used to change the new Locale based on the language parameter value added to a request. This is required when many users call our spring application with a different locale.
To make this work,  we need to add the LocaleChangeInterceptor to the application’s registry interceptor. The configuration class should extend the WebMvcConfigurer class and override the addInterceptors method.
@Bean
public LocaleChangeInterceptor localeChangeInterceptor() {
    LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
    localeChangeInterceptor.setParamName("lang");
    return localeChangeInterceptor;
}

@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(localeChangeInterceptor());
}

The complete source code for the configuration class “InternationalizationConfig.java” is as below:

package com.appsdeveloperblog.springbooti18n.config;

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.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
import org.springframework.web.servlet.i18n.SessionLocaleResolver;

import java.util.Locale;

@Configuration
public class InternationalizationConfig implements WebMvcConfigurer {

    @Bean
    public LocaleResolver localeResolver() {
        SessionLocaleResolver localeResolver = new SessionLocaleResolver();
        localeResolver.setDefaultLocale(Locale.US);
        return localeResolver;
    }

    @Bean
    public LocaleChangeInterceptor localeChangeInterceptor() {
        LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
        localeChangeInterceptor.setParamName("lang");
        return localeChangeInterceptor;
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(localeChangeInterceptor());
    }
}

Step 4: Create Controller

Next, we need the controller to serve our welcome page request. The complete source code for “InternationalController.java” is as below:

package com.appsdeveloperblog.springbooti18n.controller;

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

@Controller
public class InternationalController {
    @RequestMapping(value = "/welcome", method= RequestMethod.GET)
    public String welcome() {
        return "welcome";
    }

    @RequestMapping("/")
    public String home(Model model) {
        return "redirect:welcome";
    }
}

Step 5: Multilingual Properties Files

The base has an internationalized application in the language files. In Spring Boot, we create multi-language message files like messages:

  • properties,
  • messages_es.properties,
  • messages_en.properties, etc
Here, the base name of the file (messages) is followed by the language code. In addition, if we want to be more specific, it can have the country code. Each file contains the key and value pairs. Using this spring identifies each of the texts that are present in many languages.
Important to realize the key for the same text must be the same in all the files. For example,
hello  = Hello!!!!!! (message_en.properties)
hello  = Bonjour!!!!!!(message_fr.properties)
hello  = Hallo!!!!!!(message_nl.properties)
hello  = こんにちは!!!!!! (message_jp.properties)

By default, these files need to be in the “src/main/resources” folder. In our example, we created two language files, i.e. English, and French message properties files.

The French language messages_fr.properties will be as indicated –

welcome.message=Bonjour de appsdeveloperblog.com. C'est un endroit ideal pour apprendre.
language.change=Changer la langue en
language.english=Englisch
language.french= francais

Default language messages.properties and messages_en.properties will be as indicated –

welcome.message=Hello from appsdeveloperblog.com. It's a great place for learning.
language.change=Change language to :
language.english=English
language.french= French

Step 6: Implement a View

To display internationalization messages in Thymeleaf, we must include the message key like this #{key} within a Thymeleaf tag. The most common will be to do it in th:text because it is the most typical to display text, but it can be used in any other tag, such as, for example th:value

We will create a webpage within the project’s resources/templates directory.

Here is the source code for “welcome.html“.

<!DOCTYPE HTML>
<html xmlns:th="https://www.thymeleaf.org">
<head>
    <script
            src="https://code.jquery.com/jquery-3.6.0.min.js"
            integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4="
            crossorigin="anonymous"></script>
</head>
<body>
<span th:text="#{language.change}"></span>:
<select id="locales">
    <option value=""></option>
    <option value="en" th:text="#{language.english}"></option>
    <option value="fr" th:text="#{language.french}"></option>
</select>
<h1 th:text="#{welcome.message}"></h1>
</body>
<script type="text/javascript">
      $(document).ready(function () {
          $("#locales").change(function () {
              var selectedOption = $('#locales').val();
              if (selectedOption != '') {
                  window.location.replace('?lang=' + selectedOption);
              }
          });
      });
   </script>
</html>

Step 7: Running the Application

Now, we can create the main class to start the Spring Boot Application:

package  com.appsdeveloperblog.springbooti18n;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringBootI18nApplication {

    public static void main(String[] args) {

        SpringApplication.run(SpringBootI18nApplication.class, args);
    }
}

In the end, we will run our application either by executing the following command or using the ‘Run’ button from IDE. You will find that the application started on Tomcat port 8080.

mvn spring-boot:run

Once the application starts, go to the browser and open the following URL.

http://localhost:8080/

It will redirect to the welcome page, which is created in step 6.

 

Thereafter, based on the language selected from the dropdown, the page is automatically updated with the selected language.

For example, the French page is as below

 

Exceptions with Locale Messages

To this end, we may also use the support of Spring Boot Internationalization to provide local exceptions or information messages.

We can define our error or information messages in the file messages.properties and other locale-specific files. Once the application encounters an error, Spring Boot will pick respective locale messages and construct the error message itself based on available locales.

For example, messages_en.properties

innboundrequest.200="Invalid inbound message payload."
innboundrequest.201="Unknown inbound message attribute {0}."
innboundrequest.202="Unknown attribute value {0} for Request ID {1}."
innboundrequest.203="Inbound message missing."

messages_fr.properties

innboundrequest.200="Donn\u00E9es utiles de message entrant non valides"
innboundrequest.201="Attribut {0} de message entrant inconnu"
innboundrequest.202="Valeur d'attribut inconnue {0} pour l'ID de demande {1}"
innboundrequest.203="Message entrant manquant"

Conclusion

In general, if your application serves many different clients around the world, the application must be able to respond based on the country and language.

In this post, we learned how Spring Boot provides out-of-box support for internalization. This makes our applications more intuitive and understandable for global users.

Check the Spring Boot tutorials page to learn more about building applications with Spring.

Happy Learning!


Leave a Reply

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