国际化(Internationalization 简称 I18n,其中“I”和“n”分别为首末字符,18 则为中间的字符数)是指软件开发时应具备多种语言和地区的功能。换句话说就是,开发的软件需要能同时应对不同国家和地区的用户访问,并根据用户地区和语言习惯,提供相应的、符合用户阅读习惯的页面和数据。
② 实现国际化Spring Boot实现国际化,准备工作如下:
1.IDEA设置编码问题要统一设置properites的编码问题,不然会出现乱码
打开setting-File Encoding
编写国际化配置文件之前,需要抽取需要显示的国际化页面信息,明确哪些内容需要编写国际化配置。
在Spring Boot项目resource目录下创建i18n目录,存放国际化配置文件。
建立一个login.properties文件,还有一个login_zh_CN.properties;然后通过以下方式再添加一个login_en_US.properties
安装Resource Bundle插件,在Setting->Plugins
login.properties:无语言设置时生效login_en_US.properties :英语时生效login_zh_CN.properties:中文时生效
打开任意一个国际化资源文件,并切换为 Resource Bundle 模式,然后点击“+”号,创建所需的国际化属性,如下图。
3.使用ResourceBundleMessageSource管理国际化资源文件Spring Boot 已经对 ResourceBundleMessageSource 提供了默认的自动配置 MessageSourceAutoConfiguration
找到 MessageSourceAutoConfiguration
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(name = AbstractApplicationContext.MESSAGE_SOURCE_BEAN_NAME, search = SearchStrategy.CURRENT)
//只有容器中没有messageSource这个Bean才生效
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Conditional(ResourceBundleCondition.class)
//满足ResourceBundleCondition类的条件才有效
@EnableConfigurationProperties
// 开启配置属性
public class MessageSourceAutoConfiguration {
// 保存的国际化资源配置文件
private static final Resource[] NO_RESOURCES = {};
// 将 MessageSourceProperties 以组件的形式添加到容器中
// MessageSourceProperties 下的每个属性都与以 spring.messages 下的所有属性一一对应,双向绑定
@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();
//读取国际化资源文件的 basename (基本名),并封装到 ResourceBundleMessageSource 中
if (StringUtils.hasText(properties.getbasename())) {
messageSource.setbasenames(StringUtils
.commaDelimitedListToStringArray(StringUtils.trimAllWhitespace(properties.getbasename())));
}
//读取国际化资源文件的 encoding (编码),并封装到 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 cache = new ConcurrentReferenceHashMap<>();
//获取匹配basename的结果
@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;
}
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”开头的配置进行了绑定;Spring Boot 从容器中获取 MessageSourceProperties 组件,并从中读取国际化资源文件的 basename(文件基本名)、encoding(编码)等信息,将它们封装到 ResourceBundleMessageSource 中;Spring Boot 将 ResourceBundleMessageSource 以组件的形式添加到容器中,进而实现对国际化资源文件的管理。只有满足了ResourceBundleCondition条件, MessageSourceAutoConfiguration才能生效,通过basename去获得国际化资源配置文件的位置
查看 MessageSourceProperties 类,其代码如下。
public class MessageSourceProperties {
private String basename = "messages";
private Charset encoding;
@DurationUnit(ChronoUnit.SECONDS)
private Duration cacheDuration;
private boolean fallbackToSystemLocale;
private boolean alwaysUseMessageFormat;
private boolean useCodeAsDefaultMessage;
public MessageSourceProperties() {
this.encoding = StandardCharsets.UTF_8;
this.fallbackToSystemLocale = true;
this.alwaysUseMessageFormat = false;
this.useCodeAsDefaultMessage = false;
}
...
}
解析以上代码,可以知道:
MessageSourceProperties 为 basename、encoding 等属性提供了默认值;basename 表示国际化资源文件的基本名,其默认取值为“message”,即 Spring Boot 默认会获取类路径下的 message.properties 以及 message_XXX.properties 作为国际化资源文件;在 application.porperties/yaml 等配置文件中,使用配置参数“spring.messages.basename”即可重新指定国际化资源文件的基本名,来获取国际化资源配置文件 4.在页面获取国际化内容
在页面使用Thymeleaf模板引擎,我们可以通过#{…}来获取国际化内容
注意:要在templates目录下,才有用,其他目录下国际化内容配置不生效
在templates目录下创建一个index.html,代码如下


