目录

  • 2.8、国际化配置
  • 2.8.1、配置文件
  • 2.8.2、管理国际化
  • 2.8.3、在HTML获取


2.8、国际化配置

2.8.1、配置文件

国际化为 Internationalization 简称 I18n ,国际化的配置需要在resource包内创建 i18n包,

  1. resource包内创建i18n包,在内部创建国际化资源文件(文件格式为:基本名_ 语言代码 _国家或地区代码),idea会自动识别,进行合并
  1. message.properties:默认的设置
  2. message_en_US.properties :英语时生效
  3. message_zh_CN.properties:中文时生效
  1. 创建完成后idea自动识别成如下情况
  2. 下载一个插件 Resource Bundle Editor

下载完此插件后,左下角会出现2个按钮

  1. 点击 Resource Bundle 切换到可视化操作
  2. 可视化页面添加完毕后,会在配置文件中以 键=值的形式自动生成
  3. 在配置文件中设定国际化文件的位置
spring:
  messages:
    basename: i18n/message
2.8.2、管理国际化

ResourceBundleMessageSource类用于管理国际化资源文件,Spring Boot 通过 MessageSourceAutoConfiguration 对 ResourceBundleMessageSource 提供了默认配置。

@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(name = AbstractApplicationContext.MESSAGE_SOURCE_BEAN_NAME, search = SearchStrategy.CURRENT)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Conditional(ResourceBundleCondition.class)
@EnableConfigurationProperties
public class MessageSourceAutoConfiguration {

    private static final Resource[] NO_RESOURCES = {};

    // Spring Boot 将 ResourceBundleMessageSource 以组件的形式添加到容器中,进而实现对国际化资源文件的管理。,(yml文件中配置的国际化文件的位置)
    @Bean
    @ConfigurationProperties(prefix = "spring.messages")
    public MessageSourceProperties messageSourceProperties() {
        return new MessageSourceProperties();
    }
 	//Spring Boot 从容器中获取 MessageSourceProperties 组件,并从中读取国际化资源文件的 basename(文件基本名)、encoding(编码)等信息,将它们封装到 ResourceBundleMessageSource 中;
    @Bean
    public MessageSource messageSource(MessageSourceProperties properties) {
        //资源包属性
        ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
         //读取国际化资源文件的 基本名,并封装到 ResourceBundleMessageSource 中
        if (StringUtils.hasText(properties.getBasename())) {
            messageSource.setBasenames(StringUtils
                                       .commaDelimitedListToStringArray(StringUtils.trimAllWhitespace(properties.getBasename())));
        }
        //读取国际化资源文件的 编码,封装到 ResourceBundleMessageSource 中
        if (properties.getEncoding() != null) {
            messageSource.setDefaultEncoding(properties.getEncoding().name());
        }
      	//如果没有找到特定语言环境的文件,回退到系统语言环境
        messageSource.setFallbackToSystemLocale(properties.isFallbackToSystemLocale());
        //缓存持续时间
        Duration cacheDuration = properties.getCacheDuration();
        if (cacheDuration != null) {
            messageSource.setCacheMillis(cacheDuration.toMillis());
        }
        //设置始终使用消息格式
        messageSource.setAlwaysUseMessageFormat(properties.isAlwaysUseMessageFormat());
        //设置使用代码作为默认消息
        messageSource.setUseCodeAsDefaultMessage(properties.isUseCodeAsDefaultMessage());
        return messageSource;
    }

    //资源束条件
    protected static class ResourceBundleCondition extends SpringBootCondition {

        private static ConcurrentReferenceHashMap<String, ConditionOutcome> cache = new ConcurrentReferenceHashMap<>();

        //获取配置的国际化
        @Override
        public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
            String basename = context.getEnvironment().getProperty("spring.messages.basename", "messages");
            ConditionOutcome outcome = cache.get(basename);
            if (outcome == null) {
                outcome = getMatchOutcomeForBasename(context, basename);
                cache.put(basename, outcome);
            }
            return outcome;
        }

        //获取 Basename 的匹配结果
        private ConditionOutcome getMatchOutcomeForBasename(ConditionContext context, String basename) {
            ConditionMessage.Builder message = ConditionMessage.forCondition("ResourceBundle");
            for (String name : StringUtils.commaDelimitedListToStringArray(StringUtils.trimAllWhitespace(basename))) {
                for (Resource resource : getResources(context.getClassLoader(), name)) {
                    if (resource.exists()) {
                        return ConditionOutcome.match(message.found("bundle").items(resource));
                    }
                }
            }
            return ConditionOutcome.noMatch(message.didNotFind("bundle with basename " + basename).atAll());
        }

        //获取资源
        private Resource[] getResources(ClassLoader classLoader, String name) {
            String target = name.replace('.', '/');
            try {
                return new PathMatchingResourcePatternResolver(classLoader)
                    .getResources("classpath*:" + target + ".properties");
            }
            catch (Exception ex) {
                return NO_RESOURCES;
            }
        }
    }
}
  • Spring Boot 创建MessageSourceProperties保存基本信息,并作为组件配置到容器中
  • MessageSourceProperties 的属性与配置文件中以“spring.messages”开头的配置进行了绑定;
    所以当我们自己编写了国际化配置文件时, 需要在springboot的默认配置文件中指定spring.messages.basename的值用于解析国际化配置文件, 如果值不包含包限定, 那么将直接从classpath类路径下进行解析
  • Spring Boot 从容器中获取 MessageSourceProperties 组件,并从中读取国际化资源文件的 basename(文件基本名)、encoding(编码)等信息,将它们封装到 ResourceBundleMessageSource 中;
  • Spring Boot 将 ResourceBundleMessageSource 以组件的形式添加到容器中,进而实现对国际化资源文件的管理。
public class MessageSourceProperties {

   /**逗号分隔的基本名称列表(本质上是完全限定的类路径位置)*/
   private String basename = "messages";

   /**消息包编码。*/
   private Charset encoding = StandardCharsets.UTF_8;

   /**加载的资源包文件缓存持续时间 */
   @DurationUnit(ChronoUnit.SECONDS)
   private Duration cacheDuration;

   /**如果没有找到特定语言环境的文件,是否回退到系统语言环境*/
   private boolean fallbackToSystemLocale = true;

   /**是否始终应用 MessageFormat 规则,甚至解析没有参数的消息。*/
   private boolean alwaysUseMessageFormat = false;

   /**是否使用消息代码作为默认消息而不是抛出“NoSuchMessageException”*/
   private boolean useCodeAsDefaultMessage = false;

 .....get、set等方法..........

}
2.8.3、在HTML获取
  1. 配置LocaleResolver(用于切换国际化的不同配置,WebMvcAutoConfiguration中的localeResolver为国际化解析器)。
@Configuration
public class MyMessageConfig implements LocaleResolver {
	//使用自定义LocaleResolver生效
    @Bean
    public LocaleResolver localeResolver(){
        return new MyMessageConfig();
    }
	//用于解析路径中的配置的(l='zh_CN')
    @Override
    public Locale resolveLocale(HttpServletRequest request) {
        String l = request.getParameter("l");
        Locale locale = Locale.getDefault();
        if (!StringUtils.isEmpty(l)){
            String[] s = l.split("_");
            locale = new Locale(s[0],s[1]);
        }
        return locale;
    }
    @Override
    public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) 	   {
    }
}
  1. HTML中获取国际化:
    在标签里面用:th:text="#{xxx.xxx}"在标签外面用:[[#{xxx.xxx}]]
  2. 切换中英文按钮
<a class="btn btn-sm" th:href="@{/跳转到此页面的路径(l='zh_CN')}">中文</a>|
<a class="btn btn-sm" th:href="@{/跳转到此页面的路径(l='en_US')}">English</a>