Implementing Internationalization (i18n)
1. Implementing Message Bundles
Example: messages_en.properties
# messages_en.properties
order.confirmation.subject=Order {0} confirmed
# messages_de.properties
order.confirmation.subject=Bestellung {0} bestätigt
2. Implementing Locale Detection
| Source | Detail |
|---|---|
| User preference | Stored on profile (highest priority) |
| Accept-Language header | Browser default |
| Cookie | Persisted last choice |
| URL prefix | /de/... |
| Fallback | App default (e.g., en-US) |
3. Implementing Translation Lookup
Example: Spring MessageSource
String subject = messages.getMessage(
"order.confirmation.subject",
new Object[]{ order.id() },
LocaleContextHolder.getLocale());
4. Handling Pluralization
| Tool | Detail |
|---|---|
| ICU MessageFormat | Plural / select / number rules |
| CLDR | Locale-aware plural categories |
| Library | icu4j |
5. Implementing Date/Time Localization
Example: java.time formatters
DateTimeFormatter f = DateTimeFormatter
.ofLocalizedDateTime(FormatStyle.MEDIUM)
.withLocale(locale)
.withZone(user.zoneId());
String s = f.format(Instant.now());
6. Implementing Number/Currency Localization
Example: Currency format
NumberFormat nf = NumberFormat.getCurrencyInstance(locale);
nf.setCurrency(Currency.getInstance("EUR"));
String price = nf.format(amount); // "1.234,56 €" in de-DE
7. Implementing Right-to-Left Support
| Concern | Detail |
|---|---|
| Text direction | HTML dir="rtl" |
| CSS | Logical properties (margin-inline-start) |
| Bidi algorithm | Unicode UAX #9 handles mixed text |
| Server | Send Content-Language + dir hint |
8. Implementing Locale-Specific Validation
| Field | Detail |
|---|---|
| Postal code | Format per country |
| Phone | libphonenumber for E.164 + national |
| VAT/Tax ID | Country-specific patterns |
| Address | Required fields vary |
9. Implementing Translation Caching
| Layer | Detail |
|---|---|
| In-process | MessageSource caches loaded bundles |
| Distributed | Redis for dynamic translations |
| Invalidation | On translation update |
| CDN | For client-side bundles |
10. Implementing Fallback Locale Logic
| Order | Lookup |
|---|---|
| 1 | Specific (de_AT) |
| 2 | Language only (de) |
| 3 | App default (en) |
| 4 | Key returned literally (with warning) |
11. Implementing Dynamic Language Switching
Example: LocaleChangeInterceptor
@Bean
LocaleChangeInterceptor localeChangeInterceptor() {
var i = new LocaleChangeInterceptor();
i.setParamName("lang"); // ?lang=de
return i;
}
@Bean LocaleResolver localeResolver() {
var r = new CookieLocaleResolver();
r.setDefaultLocale(Locale.ENGLISH);
return r;
}
12. Implementing Translation Management
| Practice | Detail |
|---|---|
| Source of truth | TMS (Crowdin, Phrase, Lokalise) |
| CI sync | Push keys, pull translations |
| Missing-key tracking | Log + alert |
| Translation memory | Reuse phrases |
| Context for translators | Screenshots, descriptions |