Implementing Internationalization (i18n)
1. Configuring Message Sources (MessageSource)
Example: Configure reloadable message source
@Bean
MessageSource messageSource() {
ReloadableResourceBundleMessageSource ms = new ReloadableResourceBundleMessageSource();
ms.setBasename("classpath:messages");
ms.setDefaultEncoding("UTF-8");
ms.setCacheSeconds(60);
ms.setFallbackToSystemLocale(false);
return ms;
}
2. Creating Message Properties Files
| File | Locale |
|---|---|
messages.properties |
Default fallback |
messages_en.properties |
English |
messages_fr.properties |
French |
messages_es_MX.properties |
Spanish (Mexico) |
Example: Message bundle with plural form
greeting=Hello, {0}!
items.count={0,choice,0#no items|1#one item|1<{0,number,integer} items}
3. Creating Locale-Specific Files
| Resolution Order | Example for fr_CA |
|---|---|
| 1 | messages_fr_CA.properties |
| 2 | messages_fr.properties |
| 3 | messages.properties |
4. Resolving Locale (LocaleResolver)
| Resolver | Source |
|---|---|
AcceptHeaderLocaleResolver (default) |
Accept-Language header |
SessionLocaleResolver |
HTTP session attribute |
CookieLocaleResolver |
Cookie |
FixedLocaleResolver |
Hard-coded |
5. Using Accept-Language Header
Example: Locale from Accept-Language header
@Bean
LocaleResolver localeResolver() {
AcceptHeaderLocaleResolver r = new AcceptHeaderLocaleResolver();
r.setSupportedLocales(List.of(Locale.ENGLISH, Locale.FRENCH, Locale.forLanguageTag("es")));
r.setDefaultLocale(Locale.ENGLISH);
return r;
}
6. Using Session/Cookie Locale Resolver
Example: Locale stored in cookie
@Bean
LocaleResolver localeResolver() {
CookieLocaleResolver r = new CookieLocaleResolver();
r.setCookieName("APP_LOCALE");
r.setCookieMaxAge(Duration.ofDays(365));
r.setDefaultLocale(Locale.ENGLISH);
return r;
}
7. Accessing Messages in Controllers
Example: Resolve i18n message in controller
@RestController
public class GreetController {
private final MessageSource ms;
public GreetController(MessageSource ms) { this.ms = ms; }
@GetMapping("/hello/{name}")
public String hello(@PathVariable String name, Locale locale) {
return ms.getMessage("greeting", new Object[]{name}, locale);
}
}
8. Using Message Keys in Templates
Example: Use message key in Thymeleaf template
<p th:text="#{greeting('World')}">Hello</p>
<label th:text="#{form.email}">Email</label>
9. Implementing Locale Change Interceptor
Example: Change locale via query parameter
@Override
public void addInterceptors(InterceptorRegistry r) {
LocaleChangeInterceptor lci = new LocaleChangeInterceptor();
lci.setParamName("lang"); // ?lang=fr
r.addInterceptor(lci);
}
10. Formatting Dates and Numbers by Locale
| API | Use |
|---|---|
NumberFormat.getInstance(locale) |
Numbers |
NumberFormat.getCurrencyInstance(locale) |
Currency |
DateTimeFormatter.ofLocalizedDate(...).withLocale(...) |
Dates |
spring.mvc.format.date |
Global date pattern |
spring.mvc.format.date-time |
Global datetime pattern |
11. Implementing URL-Based Locale Resolution
Example: Resolve locale from URL path segment
public class PathLocaleResolver implements LocaleResolver {
private final Pattern P = Pattern.compile("^/(\\w{2})/.*$");
@Override public Locale resolveLocale(HttpServletRequest req) {
Matcher m = P.matcher(req.getRequestURI());
return m.matches() ? Locale.forLanguageTag(m.group(1)) : Locale.ENGLISH;
}
@Override public void setLocale(HttpServletRequest r, HttpServletResponse s, Locale l) {}
}