Skip to main content

Spring Boot i18n: Návod na nastavení internacionalizace

Nakonfigurujte MessageSource, vytvořte locale-specifické properties soubory, vyřešte locale a vykreslujte vícejazyčné Thymeleaf šablony — poté automatizujte překlady pomocí AI.

1

Přidat závislosti

Spring Boot Starter Web obsahuje automatickou konfiguraci MessageSource. Přidejte Thymeleaf pro i18n šablony vykreslované na serveru a validation starter pro lokalizované chybové zprávy.

Spring Boot automaticky nakonfiguruje bean MessageSource, který čte z messages.properties na classpath. Explicitní konfiguraci potřebujete jen tehdy, pokud chcete přizpůsobit basename, kódování nebo chování cache.
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

Nakonfigurujte MessageSource a LocaleResolver

Spring MessageSource načítá překlady ze souborů .properties podle konvence basename: messages.properties (výchozí), messages_de.properties (němčina), messages_ja.properties (japonština). Nakonfigurujte LocaleResolver, aby pro každý request určil, kterou lokalitu použít.

Soubory s překlady

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.

Konfigurace 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;
    }
}
Pokud se místo přeloženého textu vrací název klíče, nejčastější příčinou je špatně nastavené basename. Výchozí hodnota je 'messages', což mapuje na messages.properties na classpath. Pokud jsou Vaše soubory pojmenované jinak nebo jsou v podadresáři, nastavte spring.messages.basename explicitně.

Určování lokality

Nakonfigurujte, jak Spring určí aktivní lokalitu pro každý request. CookieLocaleResolver zachovává volbu uživatele napříč relacemi. LocaleChangeInterceptor umožňuje uživatelům přepínat lokalitu přes query parametr, například ?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

Používejte překlady v kódu

K přeloženým zprávám přistupujte v controllerech přes injektovaný MessageSource, v šablonách Thymeleaf pomocí syntaxe #{...} a v REST API pomocí automaticky určeného parametru Locale.

Controller s 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";
    }
}

Thymeleaf šablony

Výraz Thymeleaf #{...} automaticky vyhodnotí klíče zpráv z vašich souborů .properties. Parametry předávejte syntaxí #{key(arg0, arg1)}. Šablona používá lokalitu určenou vaším 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>
Výrazy Thymeleaf jako #{greeting('World')} předávají argumenty do MessageFormat. Statický text uvnitř HTML tagů slouží jako fallback při zobrazení šablony bez Springu — užitečné pro designéry, kteří pracují přímo se šablonami.

Lokalizace REST API

U REST API Spring automaticky určí Locale z hlavičky Accept-Language. Vstříkněte jej jako parametr metody a předejte do MessageSource. Klienti mění jazyk odesláním jiné hlavičky Accept-Language.

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!"}
}
REST API typicky používají AcceptHeaderLocaleResolver (podle hlavičky), zatímco webové aplikace používají CookieLocaleResolver (podle cookie). Pokud obsluhujete obojí v jedné aplikaci, zvažte vlastní LocaleResolver, který nejprve zkontroluje cookies a pak použije fallback na hlavičku Accept-Language.

Zprávy pro Bean Validation

Spring automaticky vyhodnocuje zprávy validačních omezení z Vašeho MessageSource. V anotacích omezení používejte zástupné symboly ve složených závorkách jako {validation.name.required} a překlady definujte ve svých souborech .properties.

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

Zpracujte plurály a proměnné

Spring používá java.text.MessageFormat pro interpolaci a plurály. Vzor ChoiceFormat řeší základní pravidla množného čísla, ale pro plnou podporu ICU plurálů (6 forem v arabštině, 3 v ruštině) přidejte knihovnu ICU4J.

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}
ChoiceFormat není totéž co ICU pravidla množného čísla. Používá číselné rozsahy (0#, 1#, 1<) namísto kategorií CLDR (zero, one, two, few, many, other). Pro jazyky se složitými pravidly množného čísla, jako je arabština, polština nebo ruština, ChoiceFormat nestačí — použijte místo toho MessageFormat z ICU4J.

Automatizujte překlady

Jakmile máte i18n nastavení hotové, přeložte své soubory .properties pomocí AI. V IDE požádejte svého AI asistenta o překlad zdrojového souboru, nebo ve Vaší CI/CD pipeline použijte i18n Agent CLI.

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
Překládejte inkrementálně — když přidáte nové klíče do messages.properties, přeložte jen nové klíče místo opětovného generování všech souborů pro jednotlivé lokality. Zachováte tak překlady, které už byly ručně zkontrolované v existujících souborech.

Automatizujte kvalitu překladu

Zachyťte chybějící klíče a rozbité zástupné symboly dříve, než se dostanou do produkce, pomocí i18n-validate. Otestujte uživatelské rozhraní s pseudo-překlady pomocí i18n-pseudo ještě předtím, než dorazí skutečné překlady.

Nulová konfigurace se spring-locale-chain

spring-locale-chain je open-source Spring Boot starter, který jednou závislostí automaticky nakonfiguruje LocaleResolver, LocaleChangeInterceptor a validaci podporovaných lokalit. Definujte podporované lokality v application.yml a knihovna se postará o zbytek.

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>

Doporučená struktura souborů

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

Běžné chyby

Znaky mimo ASCII se zobrazují nesmyslně

Soubory Java .properties mají ve výchozím nastavení kódování ISO-8859-1, nikoli UTF-8. Znaky jako přehlásky (ü) nebo znaky CJK se pak zobrazují nesmyslně. Řešení: nastavte spring.messages.encoding=UTF-8 v application.yml, nebo v souborech .properties používejte Unicode escape sekvence jako \u00FC. ReloadableResourceBundleMessageSource ve Spring Bootu má výchozí kódování UTF-8, ale ResourceBundleMessageSource nikoli.

ChoiceFormat selhává pro množné číslo mimo angličtinu

Java's ChoiceFormat ({0,choice,0#|1#|1<}) podporuje pouze číselné rozsahy — neumí vyjádřit kategorie množného čísla podle CLDR jako 'few' nebo 'many'. Jazyky jako arabština (6 tvarů), polština (3 tvary) a ruština (3 tvary) potřebují pro správné skloňování ICU4J. Nepředpokládejte, že ChoiceFormat zvládne všechny jazyky.

Změny překladů se neprojevují

ResourceBundleMessageSource ve výchozím nastavení cachuje bundly neomezeně dlouho. Během vývoje použijte ReloadableResourceBundleMessageSource s cacheSeconds=0, abyste změny viděli bez restartu. V produkci nastavte rozumnou dobu cache (např. 3600 sekund), abyste vyvážili výkon a rychlost aktualizací.

Neočekávaný fallback na lokalitu JVM

Ve výchozím nastavení Spring používá fallback na výchozí lokalitu JVM (Locale.getDefault()), nikoli na Váš soubor messages.properties. Nastavte spring.messages.fallback-to-system-locale=false v application.yml, abyste vždy používali výchozí bundle. Jinak server s lokalitou JVM nastavenou na 'fr' zobrazí francouzštinu místo angličtiny, když v požadované lokalitě klíč chybí.

Vyzkoušejte i18n Agent nyní

Sem přetáhněte svůj překladový soubor

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

nebo klikněte a vyberte soubor

Cílové jazyky

Bez registraceOkamžitý odhad

Fallback lokality se spring-locale-chain

Když v regionální lokalitě jako pt-BR chybí překladový klíč, Spring Boot přeskočí rovnou na výchozí lokalitu místo toho, aby nejprve zkontroloval rodičovskou lokalitu 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

Podívejte se na naši příručku k fallbacku lokalit, kde najdete úplný seznam podporovaných frameworků a 75 vestavěných řetězců. Learn more →

Často kladené otázky