You will be redirect to our new portal geekmonks.com in 10, Happy Learning. Click here to redirect now.

๐ŸŒ Internationalization (i18n) in Spring Boot

Updated on: 04 Nov 2024 - Vivek Singh


๐Ÿงญ Table of Contents


Introduction

Internationalization, or i18n, is the process of designing your application to adapt to various languages and regions without requiring engineering changes.

  • In a Spring Boot microservice, this is crucial for creating a global user experience. Springโ€™s MessageSource abstraction simplifies this process by resolving messages (text strings) based on a userโ€™s locale.

๐Ÿ› ๏ธ Maven Dependencies

Key Point

The necessary API for i18n, primarily the MessageSource interface, is part of the spring-context dependency. When you use the standard spring-boot-starter-web, this is included automatically, so no explicit extra dependency is required in most cases.


pom.xml

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

๐Ÿ’ป Core Code Implementation

Key Point

The implementation involves autowiring the MessageSource bean, which Spring Boot auto-configures. The key method is getMessage(), where we provide the message key, optional arguments, a default message, and the desired Locale. The Locale is typically extracted from the incoming HTTP requestโ€™s Accept-Language header using LocaleContextHolder.getLocale().

Java Controller Example

import java.util.Locale;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloWorldI18n {

    private final MessageSource messageSource;

    // Use constructor injection for MessageSource (Best Practice)
    public HelloWorldI18n(MessageSource messageSource) {
        this.messageSource = messageSource;
    }

    @GetMapping("/say-hello-i18n")
    public String sayHello() {

        // Get the Locale from the current HTTP Request's Accept-Language header
        var locale = LocaleContextHolder.getLocale();

        // Resolve the message key based on the determined locale
        return this.messageSource.getMessage(
                "good.morning", // The message key
                null,           // Arguments for parametric messages
                "Default - Good Morning", // Fallback message if key not found
                locale          // The target locale
        );
    }
}

๐Ÿ“‚ Resource Files for Messages

Key Point

Messages are stored in standard Java .properties files following the naming convention: messages[-<locale>].properties. Spring Boot automatically looks for these files on the classpath (typically in src/main/resources/).

File Structure Examples

File NameLocaleDescription
messages.propertiesDefault (Fallback)Used when no specific locale file is found.
messages_en.propertiesEnglish (US/UK)Used for English-speaking users.
messages_es.propertiesSpanishUsed for Spanish-speaking users.
messages_de.propertiesGermanUsed for German-speaking users. (Note: ger is non-standard)

Content Example

messages_es.properties

good.morning=Buenos dรญas

messages.properties

good.morning=Good Morning (Default)


Project Reference: You can view the source code for this setup on Github: Internationalization POC App


๐Ÿ“ Key Observations

How Locale is Determined

  • The mechanism relies on Spring reading the value of the Accept-Language HTTP Header sent by the client (e.g., a browser or another microservice).
  • Based on this header (e.g., es, de-DE, en-US), Spring finds the most appropriate messages[-<locale>].properties file.
  • The default message file prefix is messages and the suffix is .properties.
    • This can be customized in application.properties using spring.messages.basename.

Example Request Header

Header:Accept-Language: es

Action: Spring resolves the message from messages_es.properties.


๐Ÿ“ Architectural Insight

i18n Message Resolution Flow

This diagram illustrates how a requestโ€™s locale determines the message resolution.

sequenceDiagram
    participant C as Client (e.g., Browser)
    participant A as Spring Boot Microservice
    participant MS as MessageSource (Internal)
    participant RF as Resource Files (.properties)

    C->>A: HTTP GET /say-hello-i18n (Header: Accept-Language: es)
    A->>A: Get Locale from LocaleContextHolder
    A->>MS: messageSource.getMessage("good.morning", ..., locale)
    MS->>RF: Look for messages_es.properties
    alt File Found
        RF-->>MS: Return message "Buenos dรญas"
    else File Not Found
        RF-->>MS: Look for messages.properties (Default)
        RF-->>MS: Return message "Good Morning (Default)"
    end
    MS-->>A: Resolved Message
    A-->>C: HTTP Response (Body: Resolved Message)

โœ… Benefits and Drawbacks

AspectBenefits ๐Ÿ‘Drawbacks ๐Ÿ‘Ž
AccessibilityWider market reach and better user experience for global users.Maintenance Overhead for translating and managing numerous resource files.
CodeClean separation of text content from the Java code (no hardcoded strings).Requires discipline to use message keys everywhere, not just hardcode strings.
ScalabilityEasy to add new languages by simply adding a new .properties file.Context Loss during translation; translators might lack context without proper comments/guidance.

๐Ÿš€ Suggested Improvements

For a robust, production-ready microservice implementation, consider these enhancements:

  1. Custom Base Name: Define the base name explicitly in application.properties to ensure future-proofing and clarity.

    spring.messages.basename=i18n/messages
    

    (This would require placing files in a sub-directory, e.g., src/main/resources/i18n/)

  2. Parametric Messages: Use placeholders in messages for dynamic content, e.g., good.morning.user=Buenos dรญas, {0}. The getMessage method can then pass the argument: messageSource.getMessage("good.morning.user", new Object[]{"Vivek"}, ...)

  3. Externalizing MessageSource: For large microservice deployments or applications requiring real-time updates, consider implementing a custom MessageSource that loads messages from a centralized database or a configuration service (e.g., Consul, Spring Cloud Config).