Spring Boot i18n: instrukcja konfiguracji internacjonalizacji
Skonfiguruj MessageSource, utwórz pliki właściwości dla poszczególnych języków, rozwiązuj ustawienia regionalne i renderuj wielojęzyczne szablony Thymeleaf, a następnie zautomatyzuj tłumaczenia z AI.
Dodaj zależności
Spring Boot Starter Web zawiera gotową automatyczną konfigurację MessageSource. Dodaj Thymeleaf do renderowanych na serwerze szablonów i18n oraz starter walidacji do zlokalizowanych komunikatów o błędach.
<!-- 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>Skonfiguruj MessageSource i LocaleResolver
MessageSource Springa wczytuje tłumaczenia z plików .properties zgodnie z konwencją nazwy bazowej: messages.properties (domyślny), messages_de.properties (niemiecki), messages_ja.properties (japoński). Skonfiguruj LocaleResolver, aby dla każdego żądania określać używane ustawienia regionalne.
Pliki tłumaczeń
# 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.Konfiguracja MessageSource
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;
}
}Rozwiązywanie ustawień regionalnych
Skonfiguruj sposób określania przez Spring aktywnych ustawień regionalnych dla każdego żądania. CookieLocaleResolver zachowuje wybór użytkownika między sesjami. LocaleChangeInterceptor pozwala zmieniać język za pomocą parametru zapytania takiego jak ?lang=de.
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());
}
}Używaj tłumaczeń w kodzie
Uzyskuj dostęp do przetłumaczonych wiadomości w kontrolerach przez wstrzyknięcie MessageSource, w szablonach Thymeleaf za pomocą składni #{...} oraz w interfejsach REST przez automatycznie rozwiązany parametr Locale.
Kontroler z MessageSource
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";
}
}Szablony Thymeleaf
Wyrażenie #{...} Thymeleaf automatycznie rozwiązuje klucze wiadomości z plików .properties. Przekazuj parametry za pomocą składni #{key(arg0, arg1)}. Szablon używa ustawień regionalnych rozwiązanych przez LocaleResolver.
<!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>Lokalizacja interfejsu REST API
W interfejsach REST API Spring automatycznie rozwiązuje Locale z nagłówka Accept-Language. Wstrzyknij go jako parametr metody i przekaż do MessageSource. Klienci zmieniają język, wysyłając inne nagłówki Accept-Language.
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!"}
}Komunikaty walidacji beanów
Spring automatycznie rozwiązuje komunikaty ograniczeń walidacji z MessageSource. Używaj symboli zastępczych w nawiasach klamrowych, takich jak {validation.name.required}, w adnotacjach ograniczeń i definiuj tłumaczenia w plikach .properties.
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 einObsłuż liczbę mnogą i zmienne
Spring używa java.text.MessageFormat do interpolacji i liczby mnogiej. Wzorzec ChoiceFormat obsługuje podstawowe reguły liczby mnogiej, ale pełna obsługa ICU (6 form w arabskim i 3 w rosyjskim) wymaga dodania biblioteki ICU4J.
# 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}Zautomatyzuj tłumaczenia
Po skonfigurowaniu i18n tłumacz pliki .properties za pomocą AI. Poproś asystenta AI w środowisku programistycznym o przetłumaczenie pliku źródłowego albo użyj CLI i18n Agent w pipeline CI/CD.
# 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,esZautomatyzuj kontrolę jakości tłumaczeń
Konfiguracja bez kodu z spring-locale-chain
spring-locale-chain to starter Spring Boot o otwartym kodzie źródłowym, który za pomocą jednej zależności automatycznie konfiguruje LocaleResolver, LocaleChangeInterceptor i walidację obsługiwanych ustawień regionalnych. Zdefiniuj obsługiwane ustawienia w application.yml, a biblioteka zajmie się resztą.
<!-- 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>Zalecana struktura plików
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.gradleTypowe pułapki
Znaki spoza ASCII są wyświetlane nieprawidłowo
ChoiceFormat nie działa dla nieangielskich form liczby mnogiej
Zmiany tłumaczeń nie są widoczne
Nieoczekiwany powrót do ustawień regionalnych JVM
Przetłumacz również:
Wypróbuj i18n Agent
Upuść tutaj plik tłumaczenia
JSON, YAML, PO, XML, CSV, Markdown, Properties
lub kliknij, aby go wybrać
Języki docelowe
Rezerwowe ustawienia regionalne z spring-locale-chain
Gdy brakuje klucza tłumaczenia w regionalnym wariancie języka, takim jak pt-BR, Spring Boot przechodzi bezpośrednio do języka domyślnego, zamiast najpierw sprawdzić język nadrzędny pt.
<!-- Maven -->
<dependency>
<groupId>ai.i18nagent</groupId>
<artifactId>spring-locale-chain</artifactId>
</dependency># application.yml
locale-chain:
fallbacks:
pt-BR:
- pt
- en
zh-Hant-HK:
- zh-Hant
- zh
- enZobacz nasz przewodnik po rezerwowych ustawieniach regionalnych, aby poznać pełną listę obsługiwanych frameworków i 75 wbudowanych łańcuchów. Learn more →