Master Spring i18n: A Complete Guide to Internationalization in Spring 5
This tutorial explains how Spring's internationalization (i18n) works, covering core APIs, bean initialization, configuration for both classic Spring and Spring Boot, message resource files, placeholder usage, and convenient accessor utilities, with full code examples for seamless multi‑language support.
1. Introduction
Spring Internationalization (i18n) provides a mechanism for multi‑language support in Spring applications. It extracts texts, labels, messages into resource files and switches language automatically based on user locale, simplifying maintenance and extension.
2. API Overview
ApplicationContext extends MessageSource, offering methods such as:
String getMessage(String code, Object[] args, String defaultMessage, Locale locale)
String getMessage(String code, Object[] args, Locale locale)
String getMessage(String code, Object[] args, Locale locale) (throws NoSuchMessageException if not found)
String getMessage(MessageSourceResolvable resolvable, Locale locale)
3. Internationalization Initialization
During ApplicationContext initialization Spring looks for a bean named messageSource . If found it is used; otherwise Spring creates an empty DelegatingMessageSource . Example implementation:
<code>public abstract class AbstractApplicationContext {
public void refresh() {
// Initialize message source
initMessageSource();
}
protected void initMessageSource() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) {
this.messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class);
if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) {
HierarchicalMessageSource hms = (HierarchicalMessageSource) this.messageSource;
if (hms.getParentMessageSource() == null) {
hms.setParentMessageSource(getInternalParentMessageSource());
}
}
} else {
// Use empty MessageSource to be able to accept getMessage calls.
DelegatingMessageSource dms = new DelegatingMessageSource();
dms.setParentMessageSource(getInternalParentMessageSource());
this.messageSource = dms;
beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource);
}
}
}
</code>4. Internationalization Configuration
In a plain Spring environment define a MessageSource bean:
<code>@Bean(AbstractApplicationContext.MESSAGE_SOURCE_BEAN_NAME)
public MessageSource messageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
// basename, not package
messageSource.addBasenames("classpath:com/pack/main/databinder/message");
return messageSource;
}
</code>Create message_zh_CN.properties and message_en_US.properties under com/pack/main/databinder with appropriate key‑value pairs.
Usage example:
<code>try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class)) {
// Locale.CHINA or Locale.US
System.out.println(context.getMessage("user.name.empty", null, Locale.CHINA));
}
</code>In Spring Boot, configure in application.yml :
<code>spring:
messages:
basename: message
</code>Provide a default message.properties file.
Controller example:
<code>@RestController
@RequestMapping("/i18n")
public class I18NController {
@Resource
private ApplicationContext context;
@GetMapping("/index")
public String index() {
return context.getMessage("user.name.empty", null, "默认消息", LocaleContextHolder.getLocale());
}
}
</code>Locale is obtained from the current thread context, initialized by DispatcherServlet . Clients set the language via the Accept-Language header.
5. Additional Configuration
Spring offers MessageSourceAccessor for convenient access:
<code>@Bean
public MessageSourceAccessor messageSourceAccessor(MessageSource messageSource) {
return new MessageSourceAccessor(messageSource);
}
</code>Inject and use:
<code>@Resource
private MessageSourceAccessor accessor;
@GetMapping("/index")
public String index() {
return accessor.getMessage("user.name.empty");
}
</code>For messages with placeholders:
<code># Age range example
user.age.range=年龄的取值范围从{0}~{1}
</code> <code>@GetMapping("/index")
public String index() {
return accessor.getMessage("user.age.range", new Object[]{1, 100});
}
</code>Spring also provides ReloadableResourceBundleMessageSource , which supports hot reloading and reading from any Spring resource location.
Done!
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.