🌱 Spring Boot Q45 / 69

How does Spring Boot support internationalization (i18n)?

AI-Powered Answer ✓ Answered

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.

properties
# src/main/resources/messages.properties
welcome.message=Welcome to our application!
farewell.message=Goodbye!
product.name.placeholder=Product: {0}
properties
# 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.

properties
# 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 the Accept-Language header 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:

java
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.):

java
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:

html
<!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 the getMessage() 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 MessageSourceResolvable for more complex message resolution, such as using an array of codes.
  • Reloading Messages: During development, spring.messages.cache-duration=PT-1S or spring.messages.cache-duration=0 in application.properties (or using Spring Boot DevTools) can enable hot-reloading of message files without restarting the application.
  • Character Encoding: Always ensure your .properties files are saved with UTF-8 encoding (which is the default for MessageSource in Spring Boot) to correctly handle special characters and non-Latin scripts.