Spring Boot 3.2: Internationalization
Table of contents
Introduction
Internationalization (i18n) is the process of designing your application to be adapted to various languages, regional
peculiarities, and technical requirements of a target market.
Internationalization is a crucial step in the process of localizing your product. In this tutorial, we will show you how
to use Spring Boot to create a simple internationalized application,
how to get translated messages from messages_xx.properties
files, render translated HTML from HTML template
using Thymeleaf and how to use LocaleResolver
to change the language of the application.
Used technologies
- Java 21
- Spring Boot 3.2
- Thymeleaf 3
- Maven
- SimpleLocalize Editor
Configuration
1. Create messages_xx.properties
files
The messages are stored in the messages_XX.properties
files. The XX
is the language code. For
example, messages_pl_PL.properties
is the Polish version of the messages.
footerText=© 2023 SimpleLocalize. Wszelkie prawa zastrzeżone.
linkText=Utwórz konto SimpleLocalize
message=Dziękujemy za wypróbowanie naszego demo SimpleLocalize dla Spring Boot!
title=Hej {0}!
If you are using SimpleLocalize, you can download the messages_XX.properties
files providing your apiKey
in simplelocalize.yml
file and invoking simplelocalize download
command.
2. Configure messages_xx.properties
location
The default location for messages is src/main/resources/messages
.
You can change this by setting the spring.messages.basename
property in your application.properties
file
or by providing your ResourceBundleMessageSource
bean.
@Bean
public ResourceBundleMessageSource messageSource() {
var resourceBundleMessageSource=new ResourceBundleMessageSource();
resourceBundleMessageSource.setBasenames("i18n/messages"); // directory with messages_XX.properties
resourceBundleMessageSource.setUseCodeAsDefaultMessage(true);
resourceBundleMessageSource.setDefaultLocale(Locale.of("en"));
resourceBundleMessageSource.setDefaultEncoding("UTF-8");
resourceBundleMessageSource.setAlwaysUseMessageFormat(true);
return resourceBundleMessageSource;
}
3. Configure resolving locale from requests
The default locale resolver is AcceptHeaderLocaleResolver
which resolves the locale from the Accept-Language
header.
You can change this by setting the spring.mvc.locale-resolver
property in your application.properties
file or by
providing your LocaleResolver
bean,
creating LocaleChangeInterceptor
and registering it via addInterceptors
method (see WebMvcConfigurer
class).
@Bean
public LocaleResolver localeResolver() {
SessionLocaleResolver sessionLocaleResolver=new SessionLocaleResolver();
sessionLocaleResolver.setDefaultLocale(Locale.of("en"));
return sessionLocaleResolver;
}
@Bean
public LocaleChangeInterceptor localeChangeInterceptor() {
LocaleChangeInterceptor localeChangeInterceptor=new LocaleChangeInterceptor();
localeChangeInterceptor.setParamName("lang");
return localeChangeInterceptor;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(localeChangeInterceptor());
}
4. Create HTML template with Thymeleaf
If you want to get an HTML document with translated messages, for example, to send it via email, you can use Thymeleaf. If you are not familiar with Thymeleaf, you can read more about it here. In short, Thymeleaf is a modern server-side Java template engine for both web and standalone environments.
<!doctype html>
<html xmlns="http://www.w3.org/1999/xhtml" th:attr="lang=${lang}">
<head>
<title>Spring Boot Email Example</title>
</head>
<body>
<header>
<h1 th:utext="#{title(${userName})}"></h1>
</header>
<article>
<p th:text="#{message}"></p>
<a th:href="${url}" th:text="#{linkText}"></a>
</article>
<footer>
<p th:text="#{footerText}"></p>
</footer>
</body>
</html>
Quick Thymeleaf guide:
th:attr="lang=${lang}"
- sets the language of the documentth:utext="#{title(${userName})}"
- gets a message withtitle
key and inserts the value of theuserName
variableth:text="#{message}"
- gets a message withmessage
keyth:href="${url}"
- inserts a value of theurl
variable- You can use
th:utext
instead ofth:text
to avoid escaping HTML characters. - You can use
th:attr
to set thelang
attribute on thehtml
tag.
You can use https://mjml.io to create responsive HTML emails for free.
Localized texts
Use MessageSource
to get translated messages. This is the default way to get translated messages in Spring Boot.
import org.springframework.context.MessageSource;
@Autowired
private MessageSource messageSource;
@Test
void shouldGetTranslatedTextFromLocalFileAndLocale() {
//given
Locale locale=Locale.of("pl","PL");
//when
String titleTextWithArgument=messageSource.getMessage("title",new Object[]{"Foo Bar"},locale);
//then
assert titleTextWithArgument.equals("Hej Foo Bar!");
}
Localized API exceptions
You can also return translated API exceptions by using standard Spring Boot @ControllerAdvice
.
For this purposes, I created ErrorController
with two methods annotated with @ExceptionHandler
to catch exceptions.
@ExceptionHandler(Exception.class)
- catches all exceptions not caught by other methods@ExceptionHandler(IllegalArgumentException.class)
- catchesIllegalArgumentException
exceptions
Both methods gets Locale
from LocaleContextHolder
and returns translated message from messages_xx.properties
file
using MessageSource
bean.
@RestControllerAdvice
public class ErrorController
{
@Autowired
private MessageSource messageSource;
@ExceptionHandler(Exception.class)
@ResponseStatus(code = HttpStatus.INTERNAL_SERVER_ERROR)
public ResponseEntity<ApiError> exception()
{
String message = getLocalizedMessage("exception.internalServerError");
HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
return ResponseEntity
.status(status)
.body(new ApiError(message, status));
}
@ExceptionHandler(IllegalArgumentException.class)
@ResponseStatus(code = HttpStatus.BAD_REQUEST)
public ResponseEntity<ApiError> badRequest()
{
String message = getLocalizedMessage("exception.badRequest");
HttpStatus status = HttpStatus.BAD_REQUEST;
return ResponseEntity
.status(status)
.body(new ApiError(message, status));
}
private String getLocalizedMessage(String translationKey)
{
Locale locale = LocaleContextHolder.getLocale();
return messageSource.getMessage(translationKey, null, locale);
}
}
By default, Spring Boot uses Accept-Language
header to resolve user locale,
but in this
tutorial we changed this behavior by registering custom LocaleChangeInterceptor
bean
.
To change the language of exception, you need to use lang
parameter, e.g. /api/my-health?lang=pl_PL
.
The number of ways to return translated messages is endless, but the solution above might be one of the best ones as we keep all the logic and translation keys for exceptions in one place.
Localized HTML content (Thymeleaf)
Use ThymeleafEngine
bean to render HTML with translated messages. This is probably the most
popular way to render HTML with translated messages in Spring Boot.
@Autowired
private TemplateEngine templateEngine;
public String renderHtmlFromTemplate(Locale locale,String userName)
{
Context context=new Context();
context.setLocale(locale);
context.setVariable("userName",userName);
context.setVariable("lang",locale.getLanguage());
context.setVariable("url","https://simplelocalize.io");
return templateEngine.process("my-html-template",context);
}
Localized web pages (Thymeleaf)
You can also return translated web pages (HTML) by using standard Spring Boot @Controller
.
Spring Boot will automatically resolve user locale and render HTML from my-html-template.html
template with translated
messages.
This is the default way to return translated web pages in Spring Boot.
@Controller
public class WelcomeController
{
@GetMapping("/welcome")
public String renderHtmlFromTemplate(Model model)
{
model.addAttribute("userName", "Jakub");
return "my-html-template";
}
}
Run the application and open http://localhost:8080/welcome
in your browser.
You can change the language by adding ?lang=pl_PL
to the URL.