Skip to main content

Spring Boot i18n: Uluslararasılaştırma kurulumu eğitimi

MessageSource'u yapılandırın, yerel ayara özgü properties dosyaları oluşturun, yerel ayarları çözümleyin ve çok dilli Thymeleaf şablonları oluşturun — ardından çevirileri yapay zeka ile otomatikleştirin.

1

Bağımlılıkları ekleyin

Spring Boot Starter Web, MessageSource otomatik yapılandırmasını hazır olarak içerir. Sunucu tarafında oluşturulan i18n şablonları için Thymeleaf'i ve yerelleştirilmiş hata iletileri için doğrulama starter'ını ekleyin.

Spring Boot, classpath üzerindeki messages.properties dosyasını okuyan bir MessageSource bean'ini otomatik olarak yapılandırır. Açık yapılandırmaya yalnızca temel adı, kodlamayı veya önbelleğe alma davranışını özelleştirmek istediğinizde ihtiyacınız olur.
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

MessageSource ve LocaleResolver'ı yapılandırın

Spring'in MessageSource bileşeni, çevirileri temel ad kuralını kullanarak .properties dosyalarından yükler: messages.properties (varsayılan), messages_de.properties (Almanca), messages_ja.properties (Japonca). Her istek için hangi yerel ayarın kullanılacağını belirlemek üzere bir LocaleResolver yapılandırın.

Çeviri dosyaları

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.

MessageSource yapılandırması

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;
    }
}
Çeviriler çevrilmiş metin yerine anahtar adını döndürüyorsa en yaygın neden yanlış temel addır. Varsayılan değer 'messages'dır ve classpath üzerindeki messages.properties ile eşleşir. Dosyalarınızın adı farklıysa veya dosyalar bir alt klasördeyse spring.messages.basename değerini açıkça ayarlayın.

Yerel ayar çözümlemesi

Spring'in her istek için etkin yerel ayarı nasıl belirleyeceğini yapılandırın. CookieLocaleResolver kullanıcının seçimini oturumlar arasında korur. LocaleChangeInterceptor, kullanıcıların ?lang=de gibi bir sorgu parametresi aracılığıyla yerel ayar değiştirmesini sağlar.

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

Çevirileri kodda kullanın

Çevrilmiş iletilere denetleyicilerde MessageSource ekleme yoluyla, Thymeleaf şablonlarında #{...} söz dizimiyle ve REST API'lerinde otomatik çözümlenen Locale parametresini kullanarak erişin.

MessageSource kullanan denetleyici

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 şablonları

Thymeleaf'in #{...} ifadesi, ileti anahtarlarını .properties dosyalarınızdan otomatik olarak çözümler. Parametreleri #{key(arg0, arg1)} söz dizimiyle aktarın. Şablon, LocaleResolver'ınız tarafından çözümlenen yerel ayarı kullanır.

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>
#{greeting('Dünya')} gibi Thymeleaf ifadeleri bağımsız değişkenleri MessageFormat'a aktarır. HTML etiketlerinin içindeki sabit metin, şablon Spring olmadan görüntülendiğinde geri dönüş işlevi görür; bu, doğrudan şablonlar üzerinde çalışan tasarımcılar için kullanışlıdır.

REST API yerelleştirmesi

Spring, REST API'lerinde Locale değerini Accept-Language üstbilgisinden otomatik olarak çözümler. Bunu bir yöntem parametresi olarak ekleyin ve MessageSource'a aktarın. İstemciler farklı Accept-Language üstbilgileri göndererek dil değiştirir.

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'leri genellikle AcceptHeaderLocaleResolver (üstbilgi tabanlı), web uygulamaları ise CookieLocaleResolver (çerez tabanlı) kullanır. Her ikisini de aynı uygulamadan sunuyorsanız önce çerezleri denetleyen, ardından Accept-Language üstbilgisine dönen özel bir LocaleResolver kullanmayı değerlendirin.

Bean doğrulama iletileri

Spring, doğrulama kısıtı iletilerini MessageSource'unuzdan otomatik olarak çözümler. Kısıt ek açıklamalarınızda {validation.name.required} gibi kaşlı ayraçlı yer tutucular kullanın ve çevirileri .properties dosyalarınızda tanımlayın.

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

Çoğulları ve değişkenleri yönetin

Spring, ekleme ve çoğullar için java.text.MessageFormat kullanır. ChoiceFormat kalıbı temel çoğul kurallarını yönetir ancak tam ICU çoğul desteği (Arapçanın 6 biçimi, Rusçanın 3 biçimi) için ICU4J kitaplığını ekleyin.

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, ICU çoğul kurallarıyla aynı değildir. CLDR kategorileri (zero, one, two, few, many, other) yerine sayısal aralıklar (0#, 1#, 1<) kullanır. Arapça, Lehçe veya Rusça gibi karmaşık çoğul kuralları olan diller için ChoiceFormat yetersizdir — bunun yerine ICU4J'in MessageFormat sınıfını kullanın.

Çevirileri otomatikleştirin

i18n kurulumunuz tamamlandığında .properties dosyalarınızı yapay zeka kullanarak çevirin. IDE'nizde yapay zeka yardımcınızdan kaynak dosyanızı çevirmesini isteyin veya CI/CD işlem hattınızda i18n Agent CLI'ı kullanın.

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
Artımlı çeviri yapın — messages.properties dosyasına yeni anahtarlar eklediğinizde tüm yerel ayar dosyalarını yeniden oluşturmak yerine yalnızca yeni anahtarları çevirin. Böylece mevcut dosyalardaki insanlar tarafından incelenmiş çeviriler korunur.

Çeviri kalitesini otomatikleştirin

Eksik anahtarları ve bozuk yer tutucuları yayımlanmadan önce i18n-validate ile yakalayın. Gerçek çeviriler gelmeden önce kullanıcı arayüzünüzü i18n-pseudo ile sözde çeviriler kullanarak sınayın.

spring-locale-chain ile sıfır yapılandırma

spring-locale-chain; LocaleResolver'ı, LocaleChangeInterceptor'ı ve desteklenen yerel ayar doğrulamasını tek bir bağımlılıkla otomatik olarak yapılandıran açık kaynaklı bir Spring Boot starter'ıdır. Desteklediğiniz yerel ayarları application.yml içinde tanımlayın, geri kalanını kitaplık yönetir.

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>

Önerilen dosya yapısı

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

Yaygın hatalar

ASCII dışı karakterlerin bozuk görünmesi

Java .properties dosyalarının varsayılan kodlaması UTF-8 değil ISO-8859-1'dir. Çift noktalı harfler (ü) veya CJK karakterleri gibi karakterler bozuk görünür. Çözüm: application.yml içinde spring.messages.encoding=UTF-8 ayarını yapın veya .properties dosyalarınızda \u00FC gibi Unicode kaçış dizileri kullanın. Spring Boot'un ReloadableResourceBundleMessageSource sınıfı varsayılan olarak UTF-8 kullanır ancak ResourceBundleMessageSource kullanmaz.

ChoiceFormat'ın İngilizce dışındaki çoğullarda bozulması

Java'nın ChoiceFormat sınıfı ({0,choice,0#|1#|1<}) yalnızca sayısal aralıkları destekler — 'few' veya 'many' gibi CLDR çoğul kategorilerini ifade edemez. Arapça (6 biçim), Lehçe (3 biçim) ve Rusça (3 biçim) doğru çoğullaştırma için ICU4J gerektirir. ChoiceFormat'ın tüm dilleri yönettiğini varsaymayın.

Çeviri değişikliklerinin yansımaması

ResourceBundleMessageSource varsayılan olarak paketleri süresiz biçimde önbelleğe alır. Değişiklikleri yeniden başlatmadan görmek için geliştirme sırasında ReloadableResourceBundleMessageSource'u cacheSeconds=0 ile kullanın. Performans ve güncelleme hızı arasında denge kurmak için üretimde makul bir önbellek süresi (ör. 3600 saniye) ayarlayın.

Beklenmedik biçimde JVM yerel ayarına dönülmesi

Spring varsayılan olarak messages.properties dosyanıza değil JVM'nin varsayılan yerel ayarına (Locale.getDefault()) döner. Her zaman varsayılan paketi kullanmak için application.yml içinde spring.messages.fallback-to-system-locale=false ayarını yapın. Aksi takdirde JVM yerel ayarı 'fr' olan bir sunucu, istenen yerel ayarda bir anahtar eksik olduğunda İngilizce yerine Fransızca gösterir.

i18n Agent'ı şimdi deneyin

Çeviri dosyanızı buraya bırakın

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

veya göz atmak için tıklayın

Hedef diller

Kayıt gerekmezAnında fiyat tahmini

spring-locale-chain ile yerel ayar geri dönüşü

pt-BR gibi bölgesel bir yerel ayarda çeviri anahtarı eksik olduğunda Spring Boot, önce üst yerel ayar pt'yi denetlemek yerine doğrudan varsayılan yerel ayara geçer.

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

Desteklenen çerçevelerin tam listesi ve 75 yerleşik zincir için Yerel Ayar Geri Dönüşü Rehberimize bakın. Learn more →

Sık sorulan sorular