Skip to main content

Spring Boot i18n: guide till konfiguration av internationalisering

Konfigurera MessageSource, skapa språkspecifika properties-filer, fastställ språkvarianter och rendera flerspråkiga Thymeleaf-mallar – automatisera sedan översättningarna med AI.

1

Lägg till beroenden

Spring Boot Starter Web innehåller automatisk konfiguration av MessageSource direkt. Lägg till Thymeleaf för serverrenderade i18n-mallar och valideringsstartpaketet för lokaliserade felmeddelanden.

Spring Boot konfigurerar automatiskt en MessageSource-bean som läser messages.properties från classpath. Du behöver bara en uttrycklig konfiguration om du vill anpassa basnamn, kodning eller cachelagring.
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

Konfigurera MessageSource och LocaleResolver

Springs MessageSource läser in översättningar från .properties-filer enligt basnamnskonventionen: messages.properties (förvalt), messages_de.properties (tyska), messages_ja.properties (japanska). Konfigurera en LocaleResolver för att avgöra vilken språkvariant som ska användas för varje begäran.

Översättningsfiler

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.

Konfiguration av 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;
    }
}
Om en översättning returnerar nyckelnamnet i stället för den översatta texten är den vanligaste orsaken ett felaktigt basnamn. Standardvärdet är 'messages', vilket motsvarar messages.properties i classpath. Om filerna har andra namn eller ligger i en underkatalog måste du uttryckligen ange spring.messages.basename.

Fastställande av språkvariant

Konfigurera hur Spring fastställer den aktiva språkvarianten för varje begäran. CookieLocaleResolver sparar användarens val mellan sessioner. Med LocaleChangeInterceptor kan användare byta språk via en frågeparameter som ?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

Använd översättningar i koden

Hämta översatta meddelanden i styrenheter genom injicering av MessageSource, i Thymeleaf-mallar med syntaxen #{...} och i REST-API:er med den automatiskt fastställda parametern Locale.

Styrenhet med 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-mallar

Thymeleaf-uttrycket #{...} slår automatiskt upp meddelandenycklar i dina .properties-filer. Skicka parametrar med syntaxen #{key(arg0, arg1)}. Mallen använder den språkvariant som fastställts av din 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>
Thymeleaf-uttryck som #{greeting('World')} skickar argument till MessageFormat. Den statiska texten inuti HTML-taggar fungerar som reserv när mallen visas utan Spring, vilket är praktiskt för designers som arbetar direkt med mallarna.

Lokalisering av REST-API:er

För REST-API:er fastställer Spring automatiskt Locale från Accept-Language-rubriken. Injicera den som en metodparameter och skicka den till MessageSource. Klienter byter språk genom att skicka olika Accept-Language-rubriker.

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:er använder vanligtvis AcceptHeaderLocaleResolver (rubrikbaserad), medan webbappar använder CookieLocaleResolver (cookiebaserad). Om båda tillhandahålls från samma app kan du överväga en anpassad LocaleResolver som först kontrollerar cookies och sedan går vidare till Accept-Language-rubriken.

Valideringsmeddelanden för beans

Spring hämtar automatiskt meddelanden om valideringsbegränsningar från din MessageSource. Använd platshållare med klammerparenteser, som {validation.name.required}, i begränsningsannoteringarna och definiera översättningarna i .properties-filerna.

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

Hantera pluralformer och variabler

Spring använder java.text.MessageFormat för interpolering och pluralformer. ChoiceFormat-mönstret hanterar grundläggande pluralregler, men lägg till biblioteket ICU4J för fullständigt stöd för ICU-pluralformer (arabiskans 6 former och ryskans 3).

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 är inte samma sak som ICU:s pluralregler. Det använder numeriska intervall (0#, 1#, 1<) i stället för CLDR-kategorier (zero, one, two, few, many, other). För språk med komplexa pluralregler, som arabiska, polska eller ryska, räcker ChoiceFormat inte till – använd i stället ICU4J:s MessageFormat.

Automatisera översättningar

När i18n-konfigurationen är klar kan du översätta .properties-filerna med AI. Be AI-assistenten i utvecklingsmiljön att översätta källfilen eller använd i18n Agent CLI i CI/CD-pipelinen.

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
Översätt stegvis – när du lägger till nya nycklar i messages.properties översätter du bara de nya nycklarna i stället för att generera om alla språkfiler. Då bevaras översättningar som redan har granskats av människor.

Automatisera kvalitetskontrollen av översättningar

Upptäck saknade nycklar och trasiga platshållare före lansering med i18n-validate. Testa gränssnittet med pseudoöversättningar via i18n-pseudo innan de riktiga översättningarna är klara.

Konfiguration utan handpåläggning med spring-locale-chain

spring-locale-chain är ett Spring Boot-startpaket med öppen källkod som automatiskt konfigurerar LocaleResolver, LocaleChangeInterceptor och validering av de språkvarianter som stöds i ett enda beroende. Definiera språkvarianterna som stöds i application.yml så hanterar biblioteket resten.

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>

Rekommenderad filstruktur

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

Vanliga fallgropar

Icke-ASCII-tecken visas som skräptecken

Java-filer av typen .properties använder ISO-8859-1 som standardkodning, inte UTF-8. Tecken som omljud (ü) eller CJK-tecken visas som skräptecken. Lösning: ange spring.messages.encoding=UTF-8 i application.yml eller använd Unicode-escape-sekvenser som \u00FC i .properties-filerna. Spring Boots ReloadableResourceBundleMessageSource använder UTF-8 som standard, men ResourceBundleMessageSource gör det inte.

ChoiceFormat fungerar inte för icke-engelska pluralformer

Javas ChoiceFormat ({0,choice,0#|1#|1<}) stöder endast numeriska intervall – det kan inte uttrycka CLDR-pluralkategorier som 'few' eller 'many'. Språk som arabiska (6 former), polska (3 former) och ryska (3 former) behöver ICU4J för korrekt pluralisering. Utgå inte från att ChoiceFormat kan hantera alla språk.

Översättningsändringar visas inte

ResourceBundleMessageSource cachelagrar paket på obestämd tid som standard. Använd ReloadableResourceBundleMessageSource med cacheSeconds=0 under utvecklingen för att se ändringar utan omstart. Ange en rimlig cachetid i produktion (till exempel 3600 sekunder) för att balansera prestanda och uppdateringshastighet.

Oväntad reserv till JVM-språkvarianten

Som standard går Spring tillbaka till JVM:ens förvalda språkvariant (Locale.getDefault()), inte till filen messages.properties. Ange spring.messages.fallback-to-system-locale=false i application.yml för att alltid använda standardpaketet. Annars visar en server där JVM-språkvarianten är inställd på 'fr' franska i stället för engelska när en nyckel saknas i den begärda språkvarianten.

Prova i18n Agent nu

Släpp din översättningsfil här

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

eller klicka för att välja en fil

Målspråk

Ingen registrering krävsPrisuppskattning direkt

Reservspråk med spring-locale-chain

När en översättningsnyckel saknas i en regional variant som pt-BR går Spring Boot direkt till den förvalda språkvarianten i stället för att först kontrollera den överordnade språkvarianten 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

Se vår guide om reservspråk för en fullständig lista över de ramverk som stöds och 75 inbyggda kedjor. Learn more →

Vanliga frågor