How does Spring Boot support internationalization (i18n)?
Internationalization (i18n) is the process of designing an application so that it can be adapted to various languages and regions without engineering changes. Spring Boot provides robust support for i18n, primarily leveraging Spring Framework's `MessageSource` mechanism, allowing developers to externalize user-facing messages and adapt them based on the user's locale.
Core Concepts
At the heart of Spring Boot's i18n support is the MessageSource interface. This interface defines methods for resolving messages based on a key and locale. Spring Boot auto-configures a MessageSource bean for you, typically a ReloadableResourceBundleMessageSource, which reads messages from resource bundles (properties files).
Resource bundles are .properties files that contain key-value pairs where the key identifies a message and the value is the message string for a specific locale. For example, messages.properties for the default locale, messages_en.properties for English, and messages_es.properties for Spanish.
The Locale represents a specific geographical, political, or cultural region. Spring uses a LocaleResolver to determine the current user's locale. This locale is then used by the MessageSource to pick the appropriate message from the resource bundles.
Implementing i18n in Spring Boot
1. Create Message Resource Files
Place your .properties files in the src/main/resources/ directory. By default, Spring Boot looks for files named messages.properties as the base name.
# src/main/resources/messages.properties
welcome.message=Welcome to our application!
farewell.message=Goodbye!
product.name.placeholder=Product: {0}
# src/main/resources/messages_es.properties
welcome.message=¡Bienvenido a nuestra aplicación!
farewell.message=¡Adiós!
product.name.placeholder=Producto: {0}
2. Configure MessageSource
Spring Boot automatically configures a MessageSource bean. You can customize its behavior using properties in application.properties or application.yml. The most common configuration is setting the base name for your message bundles.
# src/main/resources/application.properties
spring.messages.basename=messages,errors
spring.messages.encoding=UTF-8
spring.messages.cache-duration=PT1H # Cache for 1 hour, 'PT-1S' for no caching
The spring.messages.basename property can accept a comma-separated list of base names, allowing you to organize messages into multiple files (e.g., messages.properties, errors.properties).
3. Resolve Locale
Spring Boot provides various LocaleResolver implementations to determine the current locale:
AcceptHeaderLocaleResolver: (Default) Determines the locale from theAccept-Languageheader in the HTTP request.SessionLocaleResolver: Stores the locale in the user's HTTP session.CookieLocaleResolver: Stores the locale in a cookie.
You can configure a different LocaleResolver by defining a bean of type LocaleResolver. Here's an example using SessionLocaleResolver:
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.WebMvcConfigurer;
import org.springframework.web.servlet.i18n.SessionLocaleResolver;
import java.util.Locale;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Bean
public LocaleResolver localeResolver() {
SessionLocaleResolver sessionLocaleResolver = new SessionLocaleResolver();
sessionLocaleResolver.setDefaultLocale(Locale.ENGLISH);
return sessionLocaleResolver;
}
}
To change the locale at runtime, you can also add a LocaleChangeInterceptor to your MVC configuration, typically mapping it to a URL parameter (e.g., ?lang=es).
4. Access Messages
You can access messages programmatically by injecting the MessageSource bean into your components (controllers, services, etc.):
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.Locale;
@Controller
public class HomeController {
@Autowired
private MessageSource messageSource;
@GetMapping("/hello")
@ResponseBody
public String hello(@RequestHeader(name = "Accept-Language", required = false) Locale locale) {
// If using AcceptHeaderLocaleResolver, the locale parameter will be resolved automatically
// If using another resolver, get current locale from RequestContextUtils.getLocale(request)
String welcomeMessage = messageSource.getMessage("welcome.message", null, locale);
String productMessage = messageSource.getMessage("product.name.placeholder", new Object[]{"Laptop"}, locale);
return welcomeMessage + " " + productMessage;
}
}
For templating engines like Thymeleaf, Spring Boot provides direct integration. You can use the #{...} syntax to resolve messages:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<h1 th:text="#{welcome.message}"></h1>
<p th:text="#{farewell.message}"></p>
<p th:text="#{product.name.placeholder('Laptop')}"></p>
</body>
</html>
Additional Considerations
- Placeholders and Arguments: Messages can include placeholders (e.g.,
{0},{1}) that are replaced at runtime with arguments passed to thegetMessage()method or Thymeleaf's#{key(arg1, arg2)}syntax. - Default Messages: The
getMessage()method allows providing a default message that will be used if the key is not found in the resource bundles for the given locale. - MessageSourceResolvable: Spring Framework provides
MessageSourceResolvablefor more complex message resolution, such as using an array of codes. - Reloading Messages: During development,
spring.messages.cache-duration=PT-1Sorspring.messages.cache-duration=0inapplication.properties(or using Spring Boot DevTools) can enable hot-reloading of message files without restarting the application. - Character Encoding: Always ensure your
.propertiesfiles are saved with UTF-8 encoding (which is the default forMessageSourcein Spring Boot) to correctly handle special characters and non-Latin scripts.