In this tutorial, we are going to learn about the concept of Internationalization and how to achieve it using Spring Boot.
Table of Contents
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
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.