Uluslararasılaştırma (i18n)
Uygulaman birden çok dilde kullanıcıya hitap ediyorsa, metinleri koda gömmek yerine dile göre dışarıdan sağlamalısın. i18n (internationalization — i + 18 harf + n), uygulamayı farklı dil ve bölgelere uyarlayabilir hal…
Uluslararasılaştırma (i18n)
Uygulaman birden çok dilde kullanıcıya hitap ediyorsa, metinleri koda gömmek yerine dile göre
dışarıdan sağlamalısın. i18n (internationalization — i + 18 harf + n), uygulamayı farklı dil
ve bölgelere uyarlayabilir hale getirme işidir. Spring Boot bunu MessageSource ve dosya tabanlı
mesaj paketleriyle (messages_xx.properties) destekler.
MessageSource ve mesaj paketleri
Metinleri anahtar → dile göre değer olarak tutarsın. Gerçek projede bunlar dosyalardadır:
# messages.properties (varsayılan) selam=Hello, {0}! # messages_tr.properties selam=Merhaba, {0}! # messages_en.properties selam=Hello, {0}!
{0}, {1} yer tutucuları çalışma anında doldurulur. Koddan:
messageSource.getMessage("selam", new Object[]{ad}, locale);
Not: Bu portal tek dosya çalıştırır (
.propertiesdosyaları yok); bu yüzden örnek, mesajlarıStaticMessageSourceile programatik ekler. Gerçek projedemessages_tr.propertiesgibi dosyalar veResourceBundleMessageSourcekullanılır (Spring Boot bunu otomatik yapılandırır).
İsteğin dilini belirlemek: Locale
Hangi dilin kullanılacağını Locale belirler. Spring Boot varsayılan olarak
AcceptHeaderLocaleResolver kullanır: isteğin Accept-Language başlığına bakar. Bir
controller metoduna Locale parametresi eklersen, Spring onu otomatik enjekte eder:
@GetMapping("/selam") String selam(@RequestParam String ad, Locale locale) { return messageSource.getMessage("selam", new Object[]{ad}, locale); }
Örnek 1 (./Ornek1.java) aynı ucun Accept-Language: tr ile "Merhaba, Ada!", Accept-Language: en ile "Hello, Ada!" döndürdüğünü gösterir.
Locale çözümleme stratejileri
AcceptHeaderLocaleResolver dışında:
SessionLocaleResolver: Dili oturumda saklar (kullanıcı seçer).CookieLocaleResolver: Dili çerezde saklar.LocaleChangeInterceptor:?lang=trparametresiyle dili değiştirmeye izin verir.
@Bean LocaleResolver localeResolver() { var r = new SessionLocaleResolver(); r.setDefaultLocale(Locale.of("tr")); return r; }
Nerede kullanılır?
- Web UI metinleri (Thymeleaf şablonlarında
#{anahtar}). - Hata/doğrulama mesajları: Bean Validation mesajları i18n'lenebilir (
{javax...}). - REST API yanıtları: Hata mesajlarını istemcinin diline göre döndürme.
- Tarih/sayı/para biçimleri:
Locale'e göre biçimlendirme (topic 88'dekiDateTimeFormatter,NumberFormat).
İyi uygulamalar
- Metinleri asla koda gömme; hepsi mesaj paketlerinde olsun (çevirmen koda dokunmasın).
- Varsayılan bir dil/paket (
messages.properties) bulundur (eksik çeviri için yedek). - Yer tutucu sırasına dikkat (
{0}farklı dillerde farklı yerde olabilir). - UTF-8 kullan (Türkçe/özel karakterler — topic 87).
Özet
i18n ile uygulamayı çok dilli yapmayı öğrendik: MessageSource ve mesaj paketleri (anahtar→dile
göre değer, {0} yer tutucular); isteğin dilini Accept-Language ile çözen Locale ve
controller'a otomatik enjeksiyonu (Örnek 1); Locale çözümleme stratejileri (session/cookie/param)
ve iyi uygulamalar. Bununla yapılandırma & temel web batch'i tamamlandı. Sırada — sonraki turda —
test/güvenlik/DB derinleştirme ve mikroservis/cloud konuları.
▶ Kod Örnekleri(1)
Ornek1
ortam gerekir1// Ornek1: Uluslararasılaştırma (i18n) — MessageSource ile dile göre mesaj.
2// İstek dilini Accept-Language başlığı belirler (varsayılan AcceptHeaderLocaleResolver).
3// Çalıştırma: portal gömülü Tomcat ile başlatır, self-test çıktısını alır.
4package com.egitim.springboot.i18n;
5
6import org.springframework.boot.CommandLineRunner;
7import org.springframework.boot.SpringApplication;
8import org.springframework.boot.autoconfigure.SpringBootApplication;
9import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
10import org.springframework.context.MessageSource;
11import org.springframework.context.annotation.Bean;
12import org.springframework.context.support.StaticMessageSource;
13import org.springframework.web.bind.annotation.*;
14import org.springframework.web.client.RestClient;
15
16import java.util.Locale;
17
18@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
19public class Ornek1 {
20
21 public static void main(String[] args) { SpringApplication.run(Ornek1.class, args); }
22
23 // Mesaj kaynağı: anahtar + dile göre metin. Gerçekte messages_tr.properties / messages_en.properties dosyaları.
24 @Bean
25 MessageSource messageSource() {
26 StaticMessageSource ms = new StaticMessageSource();
27 ms.addMessage("selam", Locale.of("tr"), "Merhaba, {0}!");
28 ms.addMessage("selam", Locale.ENGLISH, "Hello, {0}!");
29 ms.addMessage("sepet", Locale.of("tr"), "Sepetinizde {0} ürün var.");
30 ms.addMessage("sepet", Locale.ENGLISH, "You have {0} items in your cart.");
31 ms.setUseCodeAsDefaultMessage(true); // anahtar bulunamazsa anahtarı döndür
32 return ms;
33 }
34
35 @RestController
36 static class SelamController {
37 private final MessageSource mesajlar;
38 SelamController(MessageSource mesajlar) { this.mesajlar = mesajlar; }
39
40 // 'Locale locale' parametresi, isteğin Accept-Language başlığından OTOMATİK gelir.
41 @GetMapping("/selam")
42 String selam(@RequestParam String ad, Locale locale) {
43 return mesajlar.getMessage("selam", new Object[]{ad}, locale);
44 }
45 @GetMapping("/sepet")
46 String sepet(@RequestParam int adet, Locale locale) {
47 return mesajlar.getMessage("sepet", new Object[]{adet}, locale);
48 }
49 }
50
51 @Bean
52 CommandLineRunner selfTest() {
53 return args -> {
54 RestClient c = RestClient.create("http://localhost:8080");
55 System.out.println("\n========= i18n SELF-TEST =========");
56 // Accept-Language başlığına göre AYNI uç farklı dilde yanıt verir.
57 System.out.println("Accept-Language: tr -> "
58 + c.get().uri("/selam?ad=Ada").header("Accept-Language", "tr").retrieve().body(String.class));
59 System.out.println("Accept-Language: en -> "
60 + c.get().uri("/selam?ad=Ada").header("Accept-Language", "en").retrieve().body(String.class));
61 System.out.println("sepet (tr) -> "
62 + c.get().uri("/sepet?adet=3").header("Accept-Language", "tr").retrieve().body(String.class));
63 System.out.println("sepet (en) -> "
64 + c.get().uri("/sepet?adet=3").header("Accept-Language", "en").retrieve().body(String.class));
65 System.out.println("==================================");
66 };
67 }
68}Bu örnek Spring / Spring Boot (Gradle) ortamı gerektirir; tek dosya olarak java Ornek1.java ile çalışmaz. Orijinal portal bunu gömülü Tomcat / Spring context ile koşuyordu. Beklenen davranış yukarıdaki anlatım ve kodda açıklanmıştır.