Spring Boot – Internationalization

In this tutorial, we are going to learn about the concept of Internationalization and how to achieve it using Spring Boot.

Need for Internationalization

Imagine that you have brought a nice imported TV from Japan that is equipped with a wonderful set of features. You have opened the box and found an instruction manual that is written in the Japanese. Will you be comfortable in operating the TV with the given set of instructions?

“Definitely not.”

Same is the case with any software application. Consider, you want to cater your online retail website to the people across the world and you have not presented the website interface to the user in their local language. This definitely hampers the user experience and in-turn has a lot of impact on your global market revenue.

This is where Internationalization (i18n) comes into picture.

Internationalization is the process of ensuring that your website accommodates multiple languages and cultural conventions to make the creation of localized sites possible.

How i18n is achieved in Spring Boot?

Spring Boot provides a lot of built in features to support Internationalization through ResourceBundles, LocaleResolvers and Interceptors etc…

We will learn about these components in detail.

Let us try to understand how Internationalization is achieved in Spring Boot through an example.

Example: Display the welcome message in a language selected by the user. For simplicity, let us assume our application supports only two languages – “English” and “German”. We will use English as a default Language.

We are going to create this Simple Spring Boot application using Thymeleaf template. Thymeleaf is a library based on Java and it is used to create a web application in Spring Boot. It provides good support for rendering XHTML/HTML5 content in web applications using Templates.

Application Packaging Structure

Package Structure

Maven Configurations

To add Thymeleaf support to our application, just add the following dependency in our existing pom.xml.

<dependency>
         <groupId>org.springframework.boot</groupId>
       	 <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

Final maven pom.xml file looks like:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.3.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
  </parent>
  <groupId>com.example</groupId>
  <artifactId>demo</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <name>demo</name>
  <description>Demo project for Spring Boot</description>
  <properties>
    <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>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
    
  </dependencies>
  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build>
</project>

Resource Bundles

Spring Boot automatically fetches locale-specific files placed under the classpath. (i.e., src/main/resources folder).

Sample Files structure below:

src/
|--  main/
      |--    resources/
               |--   messages.properties
               |--   messages_de.properties
  • Default locale file should be named as messages.properties
  • Other locale files will be named as messages_<<LocaleName>>.properties ( where LocaleName is any of ‘fr’ for French, ‘de’ for German etc….)

In any of the files, key names will not change. Only the key values will change based on locale. During run-time, if the application is not able to find any specific key related to a locale, it will fall back to the key present in the default locale.

messages.properties (Default Locale)

welcome.message=Hello from Simple Internationalization Application
language.change=Change Language to:
language.english=English
language.german= German

messages_de.properties

welcome.message=Hallo von Einfach Internationalisierung Anwendung
language.change=Sprache andern in
language.english=Englisch
language.german= Deutsche

LocaleResolver

It is an interface that allows to resolve the current locale of the user via the request.

It allows for implementations based on request, session, cookies, etc. More details on LocaleResolver is found here.

The default implementation is AcceptHeaderLocaleResolver, simply using the request’s locale provided by the respective HTTP header.

In our example, we are using SessionLocaleResolver to resolve the current user’s locale. Code snippet attached below:

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

LocaleChangeInterceptor

Interceptor allows for changing the current locale on every request, via a configurable request parameter (default parameter name: “locale“).

For the sake of simplicity, in our example, we used the parameter name as “lang”.

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

We need to register this interceptor with Spring Boot Registry. So, add the following method:

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

ApplicationConfiguration

Place the code related to LocaleResolver, LocaleChangeInterceptor and addInterceptor in a Single Configuration class.

package com.example.demo.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.i18n.LocaleChangeInterceptor;
import org.springframework.web.servlet.i18n.SessionLocaleResolver;
import java.util.Locale;
@Configuration
public class ApplicationConfig 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());
    }
}

Controller class

Just write a simple controller that handles the incoming web requests and picks hello.html present at “src/main/resources/templates”.

package com.example.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class HelloWebController {
    
    @RequestMapping("/")
    public String hello() {
        return "hello";
    }
 }

HTML template

Use the following template to render the response to user:

<!DOCTYPE HTML>
<html xmlns:th="https://www.thymeleaf.org">
<head><script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></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="de" th:text="#{language.german}"></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>

Now, we are ready with the application code/configurations.

Restart the application and hit the URL: https://localhost:8080

Now, change the language to “German”, and the URL gets changed to https://localhost:8080/?lang=de

Notice that the text changed to German as shown in the above screenshot.

Conclusion

In this tutorial, we have learnt about the usage of Internationalization features available in Spring Boot and how to customize it.

Translate »