12. SpringBoot国际化
1)、编写国际化配置文件;
2)、使用ResourceBundleMessageSource管理国际化资源文件
3)、在页面使用fmt:message取出国际化内容
步骤:
1)、编写国际化配置文件,抽取页面需要显示的国际化消息
国际化文件是peroperties文件
自动识别国际化视图
通过国际化视图创建国际化配置文件
同时对比编辑多个配置文件
配置国际化文件路径:
spring.messages.basename=static.i18n.login
@Configuration @ConditionalOnMissingBean( value = {MessageSource.class}, search = SearchStrategy.CURRENT ) @AutoConfigureOrder(-2147483648) @Conditional({MessageSourceAutoConfiguration.ResourceBundleCondition.class}) @EnableConfigurationProperties public class MessageSourceAutoConfiguration { private static final Resource[] NO_RESOURCES = new Resource[0]; public MessageSourceAutoConfiguration() { } @Bean @ConfigurationProperties( prefix = "spring.messages" ) public MessageSourceProperties messageSourceProperties() { return new MessageSourceProperties(); } @Bean public MessageSource messageSource(MessageSourceProperties properties) { ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource(); if (StringUtils.hasText(properties.getBasename())) {
//设置国际化文件的基础名称(去掉语言_国家代{zh_CN})的):即默认是message.properties,上图中我们起的名称是login.properties messageSource.setBasenames(StringUtils.commaDelimitedListToStringArray(StringUtils.trimAllWhitespace(properties.getBasename()))); } 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(); protected ResourceBundleCondition() { } public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
//自定义的话就用此属性:spring.messages.basename来指定,默认叫message.propertie
//我们上图的需要指定为:spring.messages.basename=login String basename = context.getEnvironment().getProperty("spring.messages.basename", "messages"); ConditionOutcome outcome = (ConditionOutcome)cache.get(basename); if (outcome == null) { outcome = this.getMatchOutcomeForBasename(context, basename); cache.put(basename, outcome); } return outcome; } private ConditionOutcome getMatchOutcomeForBasename(ConditionContext context, String basename) { Builder message = ConditionMessage.forCondition("ResourceBundle", new Object[0]); String[] var4 = StringUtils.commaDelimitedListToStringArray(StringUtils.trimAllWhitespace(basename)); int var5 = var4.length; for(int var6 = 0; var6 < var5; ++var6) { String name = var4[var6]; Resource[] var8 = this.getResources(context.getClassLoader(), name);//类路径 int var9 = var8.length; for(int var10 = 0; var10 < var9; ++var10) { Resource resource = var8[var10]; if (resource.exists()) { return ConditionOutcome.match(message.found("bundle").items(new Object[]{resource})); } } } return ConditionOutcome.noMatch(message.didNotFind("bundle with basename " + basename).atAll()); } private Resource[] getResources(ClassLoader classLoader, String name) { String target = name.replace('.', '/'); try {
//类路径下的 message.properties 和 messages_zh_CN.properties 或 自己指定的xxxx.properties 、xxxx.preperties return (new PathMatchingResourcePatternResolver(classLoader)).getResources("classpath*:" + target + ".properties"); } catch (Exception var5) { return MessageSourceAutoConfiguration.NO_RESOURCES; } } } }
public class MessageSourceProperties { private String basename = "messages";//我们的配置文件可以直接放在类路径下,默认名称叫message.properties 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; } public String getBasename() { return this.basename; } public void setBasename(String basename) { this.basename = basename; }
......
}
<!-- 中英文切换链接 -->
<a class="btn btn-sm" th:href="@{/index.html(l='zh_CN'}">中文</a> <a class="btn btn-sm" th:href="@{/index.html(l='en_US')}">English</a>
是根据浏览器的默认语言识别的
不过取出来的中文有乱码
IDEA中将UTF-8自动组转为ASSIC码
File ——》 settings 里的设置只是针对当前项目的设置,要想设置全局的
Settins ——》Other Settings ——》Default Settings 设置全局的默认设置
然后重新编辑有乱码的properties文件
根据浏览器的默认语言设置自动区分国际化字段的显示
默认请求头里会有语言的信息
切换默认的语言,请求头中排在第一位的也就变了
实现原理:
国际化Locale(区域信息对象);LocaleResolver(获取区域信息对象)
public class WebMvcAutoConfiguration { public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer, ResourceLoaderAware { @Bean @ConditionalOnMissingBean //当项目中没有区域解析器的时候,才执行这个SprigBoot默认的配置,所以可以自定义 @ConditionalOnProperty( prefix = "spring.mvc", name = {"locale"} ) // public LocaleResolver localeResolver() { //默认从配置文件中获取 if (this.mvcProperties.getLocaleResolver() == org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties.LocaleResolver.FIXED) { return new FixedLocaleResolver(this.mvcProperties.getLocale()); } else {//如果配置文件中没有,则从HttpRequest的header中获取 AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver(); localeResolver.setDefaultLocale(this.mvcProperties.getLocale()); return localeResolver; } } } public class AcceptHeaderLocaleResolver implements LocaleResolver { public Locale resolveLocale(HttpServletRequest request) { Locale defaultLocale = this.getDefaultLocale(); if (defaultLocale != null && request.getHeader("Accept-Language") == null) { return defaultLocale; } else { Locale requestLocale = request.getLocale(); List<Locale> supportedLocales = this.getSupportedLocales(); if (!supportedLocales.isEmpty() && !supportedLocales.contains(requestLocale)) { Locale supportedLocale = this.findSupportedLocale(request, supportedLocales); if (supportedLocale != null) { return supportedLocale; } else {//如果默认的区域解析器为空,则从requset请求头中获取 return defaultLocale != null ? defaultLocale : requestLocale; } } else { return requestLocale; } } } }
自定义区域解析器,点击链接切换国际化
public class MyLocaleResolver implements LocaleResolver { @Override public Locale resolveLocale(HttpServletRequest request) { String l = request.getParameter("l"); Locale locale = Locale.getDefault(); if (!StringUtils.isEmpty(l)) { String[] split = l.split("_"); locale = new Locale(split[0], split[1]); } return locale; } @Override public void setLocale(HttpServletRequest httpServletRequest, @Nullable HttpServletResponse httpServletResponse, @Nullable Locale locale) { } }
//将自定义的区域解析器加入到容器中来 @Bean public LocaleResolver localeResolver(){ return new MyLocaleResolver(); }