Skip to main content

Spring Boot i18n: Tutorial sa Setup ng Internationalization

I-configure ang MessageSource, gumawa ng locale-specific na properties file, i-resolve ang mga locale, at i-render ang multilinggwal na Thymeleaf template — pagkatapos ay i-automate ang mga pagsasalin gamit ang AI.

1

Magdagdag ng mga Dependency

Kasama na sa Spring Boot Starter Web ang MessageSource auto-configuration. Idagdag ang Thymeleaf para sa server-rendered na i18n template, at ang validation starter para sa localized na error message.

Awtomatikong kino-configure ng Spring Boot ang isang MessageSource bean na nagbabasa mula sa messages.properties sa classpath. Kailangan lang ninyo ng explicit configuration kung gusto ninyong i-customize ang basename, encoding, o caching behavior.
pom.xml
<!-- pom.xml — Spring Boot Starter Web includes MessageSource auto-config -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- Thymeleaf for server-side rendered templates with i18n -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

<!-- Validation (for localized error messages) -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>
2

I-configure ang MessageSource at LocaleResolver

Nilo-load ng MessageSource ng Spring ang mga pagsasalin mula sa mga .properties file gamit ang basename convention: messages.properties (default), messages_de.properties (German), messages_ja.properties (Japanese). I-configure ang LocaleResolver para matukoy kung aling locale ang gagamitin kada request.

Mga Translation File

messages.properties
# src/main/resources/messages.properties (default / English)
nav.home=Home
nav.about=About
nav.settings=Settings

greeting=Hello, {0}!
cart.itemCount={0,choice,0#No items|1#1 item|1<{0,number} items}

error.notFound=Page not found
error.serverError=Something went wrong. Please try again.

Configuration ng MessageSource

I18nConfig.java
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;

@Configuration
public class I18nConfig {

    @Bean
    public MessageSource messageSource() {
        ReloadableResourceBundleMessageSource source =
            new ReloadableResourceBundleMessageSource();
        source.setBasename("classpath:messages");
        source.setDefaultEncoding("UTF-8");
        source.setCacheSeconds(3600); // reload interval in dev
        return source;
    }

    // Wire MessageSource into Bean Validation
    @Bean
    public LocalValidatorFactoryBean validator(MessageSource messageSource) {
        LocalValidatorFactoryBean bean = new LocalValidatorFactoryBean();
        bean.setValidationMessageSource(messageSource);
        return bean;
    }
}
Kung ibinabalik ng translation ang pangalan ng key sa halip na ang isinaling teksto, ang pinaka-karaniwang sanhi ay maling basename. Ang default ay 'messages', na tumutugma sa messages.properties sa classpath. Kung iba ang pangalan ng mga file ninyo o nasa subdirectory ang mga ito, i-set nang tahasan ang spring.messages.basename.

Pag-resolve ng Locale

I-configure kung paano tinutukoy ng Spring ang aktibong locale para sa bawat request. Pinananatili ng CookieLocaleResolver ang pinili ng user sa iba’t ibang session. Pinapahintulutan ng LocaleChangeInterceptor ang mga user na magpalit ng locale sa pamamagitan ng query parameter tulad ng ?lang=de.

LocaleConfig.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.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.i18n.CookieLocaleResolver;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;

import java.util.Locale;

@Configuration
public class LocaleConfig implements WebMvcConfigurer {

    @Bean
    public LocaleResolver localeResolver() {
        CookieLocaleResolver resolver = new CookieLocaleResolver("lang");
        resolver.setDefaultLocale(Locale.ENGLISH);
        resolver.setCookieMaxAge(3600 * 24 * 365); // 1 year
        return resolver;
    }

    @Bean
    public LocaleChangeInterceptor localeChangeInterceptor() {
        LocaleChangeInterceptor interceptor = new LocaleChangeInterceptor();
        interceptor.setParamName("lang"); // ?lang=de switches locale
        return interceptor;
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(localeChangeInterceptor());
    }
}
3

Gamitin ang mga Pagsasalin sa Code

Maaaring i-access ang mga isinaling message sa mga controller sa pamamagitan ng MessageSource injection, sa mga Thymeleaf template gamit ang #{...} syntax, at sa mga REST API gamit ang auto-resolved na Locale parameter.

Controller na may MessageSource

HomeController.java
import org.springframework.context.MessageSource;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

import java.util.Locale;

@Controller
public class HomeController {

    private final MessageSource messageSource;

    public HomeController(MessageSource messageSource) {
        this.messageSource = messageSource;
    }

    @GetMapping("/")
    public String home(Model model, Locale locale) {
        // Spring injects the resolved Locale automatically
        String greeting = messageSource.getMessage(
            "greeting",
            new Object[]{"World"},
            locale
        );
        model.addAttribute("greeting", greeting);
        return "home";
    }
}

Mga Thymeleaf Template

Nire-resolve ng #{...} expression ng Thymeleaf ang mga message key mula sa inyong mga .properties file nang awtomatiko. Magpasa ng mga parameter gamit ang #{key(arg0, arg1)} syntax. Ginagamit ng template ang locale na ni-resolve ng inyong LocaleResolver.

home.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title th:text="#{nav.home}">Home</title>
</head>
<body>
    <!-- Simple message lookup -->
    <h1 th:text="#{greeting('World')}">Hello, World!</h1>

    <!-- Navigation with i18n -->
    <nav>
        <a href="/" th:text="#{nav.home}">Home</a>
        <a href="/about" th:text="#{nav.about}">About</a>
        <a href="/settings" th:text="#{nav.settings}">Settings</a>
    </nav>

    <!-- Parameterized messages -->
    <p th:text="#{cart.itemCount(3)}">3 items</p>

    <!-- Language switcher -->
    <div>
        <a th:href="@{/(lang=en)}">English</a>
        <a th:href="@{/(lang=de)}">Deutsch</a>
        <a th:href="@{/(lang=ja)}">日本語</a>
    </div>

    <!-- Conditional text based on locale -->
    <p th:if="${#locale.language == 'ja'}"
       th:text="#{greeting('ユーザー')}">
        こんにちは、ユーザーさん!
    </p>
</body>
</html>
Ang mga Thymeleaf expression tulad ng #{greeting('World')} ay nagpapasa ng mga argument sa MessageFormat. Ang static na teksto sa loob ng mga HTML tag ay nagsisilbing fallback kapag tinitingnan ang template nang wala ang Spring — kapaki-pakinabang para sa mga designer na direktang nagtatrabaho sa mga template.

Localization ng REST API

Para sa mga REST API, awtomatikong nire-resolve ng Spring ang Locale mula sa Accept-Language header. I-inject ito bilang method parameter at ipasa sa MessageSource. Nagpapalit ng wika ang mga client sa pamamagitan ng pagpapadala ng iba’t ibang Accept-Language header.

ApiController.java
import org.springframework.context.MessageSource;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.Locale;
import java.util.Map;

@RestController
@RequestMapping("/api")
public class ApiController {

    private final MessageSource messageSource;

    public ApiController(MessageSource messageSource) {
        this.messageSource = messageSource;
    }

    @GetMapping("/greeting/{name}")
    public ResponseEntity<Map<String, String>> greeting(
            @PathVariable String name,
            Locale locale) {  // Resolved from Accept-Language header
        String msg = messageSource.getMessage(
            "greeting", new Object[]{name}, locale
        );
        return ResponseEntity.ok(Map.of("message", msg));
    }

    // curl -H "Accept-Language: de" localhost:8080/api/greeting/Max
    // → {"message": "Hallo, Max!"}
}
Karaniwang gumagamit ang mga REST API ng AcceptHeaderLocaleResolver (header-based), habang ang mga web app ay gumagamit ng CookieLocaleResolver (cookie-based). Kung pareho ninyong sineserbisyuhan mula sa iisang app, isaalang-alang ang custom na LocaleResolver na nagche-check muna ng cookie, pagkatapos ay bumabagsak sa Accept-Language header.

Mga Bean Validation Message

Awtomatikong nire-resolve ng Spring ang mga message ng validation constraint mula sa inyong MessageSource. Gumamit ng mga curly-brace placeholder tulad ng {validation.name.required} sa inyong constraint annotation, at ideklara ang mga pagsasalin sa inyong mga .properties file.

Bean Validation i18n
import jakarta.validation.constraints.*;

public class CreateUserRequest {

    @NotBlank(message = "{validation.name.required}")
    @Size(min = 2, max = 50, message = "{validation.name.size}")
    private String name;

    @Email(message = "{validation.email.invalid}")
    private String email;
}

// In messages.properties:
// validation.name.required=Name is required
// validation.name.size=Name must be between {min} and {max} characters
// validation.email.invalid=Please enter a valid email address
//
// In messages_de.properties:
// validation.name.required=Name ist erforderlich
// validation.name.size=Name muss zwischen {min} und {max} Zeichen lang sein
// validation.email.invalid=Bitte geben Sie eine gültige E-Mail-Adresse ein
4

Pangasiwaan ang Plural at Variable

Gumagamit ang Spring ng java.text.MessageFormat para sa interpolation at plural. Hinahawakan ng ChoiceFormat pattern ang mga basic plural rule, ngunit para sa full ICU plural support (6 na anyo ng Arabic, 3 ng Russian), idagdag ang ICU4J library.

MessageFormat Plurals
# MessageFormat plural syntax in messages.properties
# Uses java.text.ChoiceFormat — NOT ICU plural rules
cart.itemCount={0,choice,0#No items|1#1 item|1<{0,number} items}

# For more complex plurals, use ICU4J:
# 1. Add dependency: com.ibm.icu:icu4j
# 2. Use ICUMessageSource instead of ResourceBundleMessageSource
#
# Then you can write ICU-style plurals:
# cart.items={count, plural, one {# item} other {# items}}

# Variables with MessageFormat:
welcome.message=Welcome, {0}! You have {1,number} new {1,choice,1#notification|1<notifications}.
order.total=Order total: {0,number,currency}
event.date=Event date: {0,date,long}
Hindi magkapareho ang ChoiceFormat at ICU plural rules. Gumagamit ito ng numeric ranges (0#, 1#, 1<) sa halip na CLDR categories (zero, one, two, few, many, other). Para sa mga wikang may kumplikadong plural rules tulad ng Arabic, Polish, o Russian, hindi sapat ang ChoiceFormat — gamitin na lang ang MessageFormat ng ICU4J.

I-automate ang Mga Pagsasalin

Kapag kumpleto na ang inyong i18n setup, isalin ang inyong mga .properties file gamit ang AI. Sa IDE ninyo, hilingin sa inyong AI assistant na isalin ang source file, o gamitin ang i18n Agent CLI sa inyong CI/CD pipeline.

Terminal
# Translate your .properties files with AI
# In your IDE, ask your AI assistant:
> Translate src/main/resources/messages.properties to German, Japanese, and Spanish

✓ messages_de.properties created (1.2s)
✓ messages_ja.properties created (1.5s)
✓ messages_es.properties created (1.1s)

# Or use the CLI in CI/CD:
npx i18n-agent translate src/main/resources/messages.properties --lang de,ja,es
Isalin nang paunti-unti — kapag nagdagdag kayo ng mga bagong key sa messages.properties, isalin lang ang mga bagong key sa halip na i-regenerate ang lahat ng locale file. Pinapanatili nito ang anumang mga pagsasaling na-review na ng tao sa mga umiiral na file.

I-automate ang Kalidad ng Pagsasalin

Matukoy ang mga nawawalang key at sirang placeholder bago maipadala sa release gamit ang i18n-validate. Subukan ang inyong UI gamit ang pseudo-translations sa pamamagitan ng i18n-pseudo bago pa dumating ang mga tunay na pagsasalin.

Zero-Config gamit ang spring-locale-chain

Ang spring-locale-chain ay isang open-source na Spring Boot starter na awtomatikong nagko-configure ng LocaleResolver, LocaleChangeInterceptor, at supported-locale validation sa iisang dependency. Itakda ang inyong mga sinusuportahang locale sa application.yml at ang library na ang bahala sa natitira.

pom.xml
<!-- Add spring-locale-chain for zero-config locale resolution -->
<dependency>
    <groupId>io.github.i18n-agent</groupId>
    <artifactId>spring-locale-chain</artifactId>
    <version>1.0.0</version>
</dependency>

Inirerekomendang File Structure

Project Structure
my-spring-app/
├── src/main/
│   ├── java/com/example/
│   │   ├── config/
│   │   │   ├── I18nConfig.java          # MessageSource bean
│   │   │   └── LocaleConfig.java        # LocaleResolver + interceptor
│   │   ├── controller/
│   │   │   └── HomeController.java      # Uses MessageSource
│   │   └── MyApplication.java
│   └── resources/
│       ├── messages.properties          # Default (English)
│       ├── messages_de.properties       # German
│       ├── messages_ja.properties       # Japanese
│       ├── messages_es.properties       # Spanish
│       ├── application.yml              # Spring config
│       └── templates/
│           └── home.html                # Thymeleaf with #{...}
├── pom.xml
└── build.gradle

Mga Karaniwang Pagkakamali

Nagpapakita ng Kalat ang Mga Non-ASCII Character

Bilang default, ISO-8859-1 ang encoding ng mga Java .properties file, hindi UTF-8. Ang mga character tulad ng umlauts (ü) o CJK characters ay nagre-render bilang kalat. Ayusin: itakda ang spring.messages.encoding=UTF-8 sa application.yml, o gumamit ng Unicode escapes tulad ng \\u00FC sa inyong mga .properties file. Default sa UTF-8 ang ReloadableResourceBundleMessageSource ng Spring Boot, ngunit hindi ang ResourceBundleMessageSource.

Pumapalya ang ChoiceFormat para sa Mga Plural na Hindi Ingles

Ang ChoiceFormat ng Java's ({0,choice,0#|1#|1<}) ay sumusuporta lang sa numeric ranges — hindi nito kayang ipahayag ang CLDR plural categories tulad ng 'few' o 'many'. Ang mga wika tulad ng Arabic (6 na anyo), Polish (3 anyo), at Russian (3 anyo) ay nangangailangan ng ICU4J para sa tamang pluralization. Huwag ipagpalagay na kaya ng ChoiceFormat ang lahat ng wika.

Hindi Naia-apply ang Mga Pagbabago sa Pagsasalin

Bilang default, walang hanggan ang pag-cache ng ResourceBundleMessageSource sa mga bundle. Sa development, gamitin ang ReloadableResourceBundleMessageSource na may cacheSeconds=0 para makita ang mga pagbabago nang hindi nire-restart. Sa production, magtakda ng makatwirang cache duration (hal., 3,600 seconds) para mabalanse ang performance at bilis ng pag-update.

Hindi Inaasahang Pag-fallback sa JVM Locale

Bilang default, nagfa-fallback ang Spring sa default locale ng JVM (Locale.getDefault()), hindi sa inyong messages.properties file. Itakda ang spring.messages.fallback-to-system-locale=false sa application.yml para palaging gamitin ang default bundle. Kung hindi, ang server na naka-set sa 'fr' ang JVM locale ay magpapakita ng French sa halip na English kapag may nawawalang key sa hinihinging locale.

Subukan ang i18n Agent Ngayon

I-drop dito ang inyong translation file

JSON, YAML, PO, XML, CSV, Markdown, Properties

o i-click para mag-browse

Mga target language

Hindi kailangan ang signupInstant na estimate

Locale Fallback kasama ang spring-locale-chain

Kapag nawawala ang isang translation key sa isang regional locale tulad ng pt-BR, diretsong tumatalon ang Spring Boot sa default locale sa halip na suriin muna ang parent locale na pt.

Terminal
<!-- Maven -->
<dependency>
  <groupId>ai.i18nagent</groupId>
  <artifactId>spring-locale-chain</artifactId>
</dependency>
Configuration
# application.yml
locale-chain:
  fallbacks:
    pt-BR:
      - pt
      - en
    zh-Hant-HK:
      - zh-Hant
      - zh
      - en

Tingnan ang aming Locale Fallback Guide para sa buong listahan ng mga sinusuportahang framework at 75 built-in chain. Learn more →

Mga Madalas Itanong