DispatcherServlet使得开发人员可以通过客户端的Locale来自动转换消息进行国际化;在整个国际化的过程中,主要分成两步,一是解析客户端的Locale,一是查找国际化的消息;

1. 解析Locale

解析Locale是通过LocaleResolver来完成的。

当有请求时,DispatcherServlet查找LocaleResolver类型的Bean,如果找到,则使用它获取并设置Locale。在这之后,通过RequestContext.getLocale方法则可以获取到Locale。

Spring默认在i18n包中提供了几种实现,如通过Cookie、Header、Session等方式来获取Locale的实现等。

1.1 定义所使用的LocaleResolver:

Spring Boot中可以通过Bean注解来定义所使用的LocaleResolver

@Configuration
public class AdditionalBeanConfig {
    @Bean
    public LocaleResolver getLocalResolver() {
        return new CookieLocaleResolver();

默认情况下,SpringBoot中是没有包含LocaleResolver对象的。

1.2 获取Locale

如果需要在应用中获取当前客户端所使在的Locale,在Controller中通过以下方式查找Locale:

@RestController
@RequestMapping("/test")
public class TestController {
    @GetMapping("/locale")
    public String locale(HttpServletRequest request) {
        // 第一种方式,通过构建RequestContext来查找Locale
        RequestContext requestContext = new RequestContext(request);
        System.out.println(requestContext.getLocale().toLanguageTag());
        // 第二种方式, 通过使用LocaleContextHolder来获取Locale
        System.out.println(LocaleContextHolder.getLocale().toLanguageTag());
        // 第二种方式,通过RequestContextUtils来获取Locale
        return RequestContextUtils.getLocale(request).toLanguageTag();

打印及返回的结果为:zh-CN

RequestContext:用于存储请求相关的一些信息,如当前请求的Web上下文、Theme、Locale等;
RequestContextUtils: 使用RequestContext的一个辅助类;提供静态方法来使用RequestContext,这样就不需要手动创建RequestContext对象;

2. 查找消息

查找消息一般通过MessageSource接口进行,它可以根据Code查找消息,同时支持国际化及参数传递。

2.1 MessageSource接口

当不配置任何的MessageSource时,Spring默认使用DelegatingMessageSource,如以下控制器:

@GetMapping("/message")
public String message() {
    return messageSource.toString();

请求返回后是:org.springframework.context.support.DelegatingMessageSource@76b6471e

这个时候,我们使用MessageSource来获取Message:

@GetMapping("/message")
public String message(HttpServletRequest request) {
    Locale locale = RequestContextUtils.getLocale(request);
    return messageSource.getMessage("test", null, locale);

此时会报错,错误信息:

There was an unexpected error (type=Internal Server Error, status=500).
No message found under code 'test' for locale 'zh_CN'.

经过跟踪,这个异常在DelegatingMessageSource的getMessage方法中处理:

@Override
public String getMessage(String code, @Nullable Object[] args, Locale locale) throws NoSuchMessageException {
    if (this.parentMessageSource != null) {
        return this.parentMessageSource.getMessage(code, args, locale);
    else {
        throw new NoSuchMessageException(code, locale);

由于parentMessageSource为空,直接抛出异常,即访问接口时看到的消息。

2.2 指定MessageSource

MessageSource包含有以下几个实现类:

  • StaticMessageSource: 基于程序注册的方式来管理资源;一般用于测试;
  • ResourceBundleMessageSource: 基于JDK中的ResourceBundle实现;
  • ReloadableResourceBundleMessageSource:包含运行时动态加载机制的实现;

2.2.1 StaticMessageSource

先使用StaticMessageSource来进行实验:

@Bean
public MessageSource messageSource() {
    StaticMessageSource messageSource =  new StaticMessageSource();
    messageSource.addMessage("test", Locale.CHINA, "测试消息");
    return messageSource;

仍旧使用之前的Controller,这时不会报错直接会返回“测试消息”这个串了。

2.2.2 ResourceBundleMessageSource

上面已经说过,StaticMessageSource这种方式一般用于测试当中,在生产中一般使用ResourceBundleMessageSource。

我们将上面使用staticMessageSource的地方进行替换:

@Bean
public MessageSource messageSource() {
    ResourceBundleMessageSource messageSource =  new ResourceBundleMessageSource();
    // 通过setBasenames来设置国际化文件
    messageSource.setBasenames("messages");
    // 也可以指定多个,如:
    //        messageSource.setBasenames("messages", "errors");
    return messageSource;

它会去Classpath下的目录中查找名称为mesages开头的国际化文件并进行加载。

在resources目录下添加messages.properties及messages_zh_cn.properties两个文件,并在message_zh_cn.properties中添加test=测试消息 的内容。

执行前面的Controller,可以看到获取到的消息。也可以仅在messages.properties中添加;它会先去对应Locale的文件中查找,如果未找到则去默认的messages.properties中查找;如果仍旧未找到则抛出异常。

有时候resources目录东西过多需要分子目录,可以在定义MessageSource的时候进行指定,如下面在resources下增加子目录i18n:

 @Bean
public MessageSource messageSource() {
    ResourceBundleMessageSource messageSource =  new ResourceBundleMessageSource();
    // 通过setBasenames来设置国际化文件
    messageSource.setBasenames("i18n.messages");
    // 也可以指定多个,如:
    //        messageSource.setBasenames("messages", "errors");
    return messageSource;

3. 总结

综上,在项目国际化过程中,需要创建LocaleResolver及MessageSource对象并注入到容器中,然后定义相关的国际化属性文件;在文件中定义好相关信息即可在需要使用的地方通过Autowired注入MessageSource对象并使用它来解析国际化消息了。

注意到在每个使用的地方都要传入Locale到MessageSource的方法中,实际上,通过使用LocaleContextHolder可以进行进一步的简化:

@Component
public class MessageSourceProxy {
    @Autowired
    private MessageSource messageSource;
    public String getMessage(String code, Object[] params) {
        return messageSource.getMessage(code, params, LocaleContextHolder.getLocale());

然后改造前面的Controller:

@Autowired
private MessageSourceProxy messageSourceProxy;
@GetMapping("/message")
public String message(HttpServletRequest request) {
    //        Locale locale = RequestContextUtils.getLocale(request);
    //        return messageSource.getMessage("test", null, locale);
    return messageSourceProxy.getMessage("test", null);

这样简化后,使用者甚至都不用手动去获取Locale了。

本文主要分析国际化的配置过程是怎样完成的,关于MessageSource对象的使用,可以参考具体的API。

DispatcherServlet使得开发人员可以通过客户端的Locale来自动转换消息进行国际化;在整个国际化的过程中,主要分成两步,一是解析客户端的Locale,一是查找国际化的消息;1. 解析Locale解析Locale是通过LocaleResolver来完成的。当有请求时,DispatcherServlet查找LocaleResolver类型的Bean,如果找到,则使用它获... 找不到message文件,该文件的路径为src/main/resources/static/i18n/messages,因操作失误被删除了。 # Spring配置 spring: # 资源信息 messages: # 国际化资源文件路径 basename: static/i18n/messages 恢复messag
转自http://www.sandzhang.com/blog/2011/04/07/spring-study-notes-initialization-5/ refresh()方法中在上篇看完了对PostProcessors的处理,这篇继续往下看。 注:refresh()的代码就不再次列举了,请看spring源码中AbstractApplicationContext类。 一、initM
这个错误在网上找了好久,试过许多方法都不行,我的代码也检查了几遍,也和别人的对照过,愣是没有找到,后来脑壳突发奇想的想到一种可能,那就是自己的创建方式有问题,之后也解决了问题,俺在这里记录下。 俺的springmvc.xml相关配置: <!--国际化--> <bean id="messageSource" class="org.springframework.contex...
<!--国际化操作拦截器,如果采用基于Session/Cookie则必须配置--> <mvc:interceptors> <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"/> &lt...
Spring Boot 支持国际化,可以通过配置来实现。下面是一个简单的示例: 1. 在 src/main/resources 目录下创建 i18n 目录,在该目录下创建 messages.properties 文件,并填写需要国际化的文本,例如: hello=Hello 2. 在 messages.properties 文件所在目录下创建 messages_zh_CN.properties 文件,填写中文翻译,例如: hello=你好 3. 在 Spring Boot 配置文件 application.properties 中添加如下配置spring.mvc.locale=zh_CN spring.messages.basename=i18n/messages 其中,`spring.mvc.locale` 指定默认语言为中文,`spring.messages.basename` 指定国际化文件的前缀,Spring Boot 会自动加载 messages.properties 和 messages_zh_CN.properties 文件。 4. 在代码中使用国际化文本,例如: ```java @Controller public class HelloController { @Autowired private MessageSource messageSource; @RequestMapping("/hello") @ResponseBody public String hello(@RequestHeader("Accept-Language") String language) { Locale locale = LocaleContextHolder.getLocale(); return messageSource.getMessage("hello", null, locale); 在上面的例子中,我们使用 `MessageSource` 接口来获取国际化文本,`LocaleContextHolder.getLocale()` 方法获取当前的语言环境。 5. 启动应用程序,访问 /hello 接口,可以看到返回的文本为:"你好"。 以上就是 Spring Boot 国际化的简单实现方法。