当前位置:网站首页>國際化配置
國際化配置
2022-06-26 09:54:00 【馬可愛家的馬可愛】
1、編寫國際化資源文件
在 src/main/resources 下創建一個 i18n 的目錄,並在該目錄中按照國際化資源文件命名格式分別創建以下三個文件,
login.properties:無語言設置時生效
login_en_US.properties :英語時生效
login_zh_CN.properties:中文時生效
以上國際化資源文件創建完成後,IDEA 會自動識別它們,並轉換成如下的模式:
然後就可以使用Resource Bundle進行可視化配置,如果沒有這個插件,下載Resource Bundle插件
打開任意一個國際化資源文件,並切換為 Resource Bundle 模式,然後點擊“+”號,創建所需的國際化屬性,如下圖。
2. 使用 ResourceBundleMessageSource 管理國際化資源文件
Spring Boot 已經對 ResourceBundleMessageSource 提供了默認的自動配置。
Spring Boot 通過 MessageSourceAutoConfiguration 對 ResourceBundleMessageSource 提供了默認配置,其部分源碼如下。
/* * Copyright 2012-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */
package org.springframework.boot.autoconfigure.context;
import java.time.Duration;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.SearchStrategy;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration.ResourceBundleCondition;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.core.Ordered;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.util.ConcurrentReferenceHashMap;
import org.springframework.util.StringUtils;
/** * {@link EnableAutoConfiguration Auto-configuration} for {@link MessageSource}. * * @author Dave Syer * @author Phillip Webb * @author Eddú Meléndez * @since 1.5.0 */
@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 = {
};
@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())) {
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<>();
@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 以組件的形式添加到容器中,進而實現對國際化資源文件的管理。
查看 MessageSourceProperties 類,其代碼如下。
/* * Copyright 2012-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */
package org.springframework.boot.autoconfigure.context;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import org.springframework.boot.convert.DurationUnit;
/** * Configuration properties for Message Source. * * @author Stephane Nicoll * @author Kedar Joshi * @since 2.0.0 */
public class MessageSourceProperties {
/** * Comma-separated list of basenames (essentially a fully-qualified classpath * location), each following the ResourceBundle convention with relaxed support for * slash based locations. If it doesn't contain a package qualifier (such as * "org.mypackage"), it will be resolved from the classpath root. */
private String basename = "messages";
/** * Message bundles encoding. */
private Charset encoding = StandardCharsets.UTF_8;
/** * Loaded resource bundle files cache duration. When not set, bundles are cached * forever. If a duration suffix is not specified, seconds will be used. */
@DurationUnit(ChronoUnit.SECONDS)
private Duration cacheDuration;
/** * Whether to fall back to the system Locale if no files for a specific Locale have * been found. if this is turned off, the only fallback will be the default file (e.g. * "messages.properties" for basename "messages"). */
private boolean fallbackToSystemLocale = true;
/** * Whether to always apply the MessageFormat rules, parsing even messages without * arguments. */
private boolean alwaysUseMessageFormat = false;
/** * Whether to use the message code as the default message instead of throwing a * "NoSuchMessageException". Recommended during development only. */
private boolean useCodeAsDefaultMessage = false;
public String getBasename() {
return this.basename;
}
public void setBasename(String basename) {
this.basename = basename;
}
public Charset getEncoding() {
return this.encoding;
}
public void setEncoding(Charset encoding) {
this.encoding = encoding;
}
public Duration getCacheDuration() {
return this.cacheDuration;
}
public void setCacheDuration(Duration cacheDuration) {
this.cacheDuration = cacheDuration;
}
public boolean isFallbackToSystemLocale() {
return this.fallbackToSystemLocale;
}
public void setFallbackToSystemLocale(boolean fallbackToSystemLocale) {
this.fallbackToSystemLocale = fallbackToSystemLocale;
}
public boolean isAlwaysUseMessageFormat() {
return this.alwaysUseMessageFormat;
}
public void setAlwaysUseMessageFormat(boolean alwaysUseMessageFormat) {
this.alwaysUseMessageFormat = alwaysUseMessageFormat;
}
public boolean isUseCodeAsDefaultMessage() {
return this.useCodeAsDefaultMessage;
}
public void setUseCodeAsDefaultMessage(boolean useCodeAsDefaultMessage) {
this.useCodeAsDefaultMessage = useCodeAsDefaultMessage;
}
}
通過以上代碼,我們可以得到以下 3 點信息:
MessageSourceProperties 為 basename、encoding 等屬性提供了默認值;
basename 錶示國際化資源文件的基本名,其默認取值為“message”,即 Spring Boot 默認會獲取類路徑下的 message.properties 以及 message_XXX.properties 作為國際化資源文件;
在 application.porperties/yml 等配置文件中,使用配置參數“spring.messages.basename”即可重新指定國際化資源文件的基本名。
通過以上源碼分析可知,Spring Boot 已經對國際化資源文件的管理提供了默認自動配置,我們這裏只需要在 Spring Boot 全局配置文件中,使用配置參數“spring.messages.basename”指定我們自定義的國際資源文件的基本名即可,代碼如下(當指定多個資源文件時,用逗號分隔)。
3. 獲取國際化內容
由於頁面使用的是 Tymeleaf 模板引擎,因此我們可以通過錶達式 #{…} 獲取國際化內容。
但是這裏需要注意的是,訪問路徑中的login.html是否存在,我直接再將其放在了config下的webConfig文件中進行訪問,當然也可以隨便命名

區域信息解析器自動配置
Spring MVC 進行國際化時有 2 個十分重要的對象:
Locale:區域信息對象
LocaleResolver:區域信息解析器,容器中的組件,負責獲取區域信息對象
我們可以通過以上兩個對象對區域信息的切換,以達到切換語言的目的。
Spring Boot 在 WebMvcAutoConfiguration 中為區域信息解析器(LocaleResolver)進行了自動配置,源碼如下
@Override
@Bean
@ConditionalOnMissingBean(name = DispatcherServlet.LOCALE_RESOLVER_BEAN_NAME)
public LocaleResolver localeResolver() {
if (this.webProperties.getLocaleResolver() == WebProperties.LocaleResolver.FIXED) {
return new FixedLocaleResolver(this.webProperties.getLocale());
}
AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
localeResolver.setDefaultLocale(this.webProperties.getLocale());
return localeResolver;
}
從以上源碼可知:
該方法默認向容器中添加了一個區域信息解析器(LocaleResolver)組件,它會根據請求頭中攜帶的“Accept-Language”參數,獲取相應區域信息(Locale)對象。
該方法上使用了 @ConditionalOnMissingBean 注解,其參數 name 的取值為 localeResolver(與該方法注入到容器中的組件名稱一致),該注解的含義為:當容器中不存在名稱為 localResolver 組件時,該方法才會生效。換句話說,當我們手動向容器中添加一個名為“localeResolver”的組件時,Spring Boot 自動配置的區域信息解析器會失效,而我們定義的區域信息解析器則會生效
AcceptHeaderLocaleResolver中的源碼如下
/* * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */
package org.springframework.web.servlet.i18n;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Locale;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.LocaleResolver;
/** * {@link LocaleResolver} implementation that simply uses the primary locale * specified in the "accept-language" header of the HTTP request (that is, * the locale sent by the client browser, normally that of the client's OS). * * <p>Note: Does not support {@code setLocale}, since the accept header * can only be changed through changing the client's locale settings. * * @author Juergen Hoeller * @author Rossen Stoyanchev * @since 27.02.2003 * @see javax.servlet.http.HttpServletRequest#getLocale() */
public class AcceptHeaderLocaleResolver implements LocaleResolver {
private final List<Locale> supportedLocales = new ArrayList<>(4);
@Nullable
private Locale defaultLocale;
/** * Configure supported locales to check against the requested locales * determined via {@link HttpServletRequest#getLocales()}. If this is not * configured then {@link HttpServletRequest#getLocale()} is used instead. * @param locales the supported locales * @since 4.3 */
public void setSupportedLocales(List<Locale> locales) {
this.supportedLocales.clear();
this.supportedLocales.addAll(locales);
}
/** * Return the configured list of supported locales. * @since 4.3 */
public List<Locale> getSupportedLocales() {
return this.supportedLocales;
}
/** * Configure a fixed default locale to fall back on if the request does not * have an "Accept-Language" header. * <p>By default this is not set in which case when there is no "Accept-Language" * header, the default locale for the server is used as defined in * {@link HttpServletRequest#getLocale()}. * @param defaultLocale the default locale to use * @since 4.3 */
public void setDefaultLocale(@Nullable Locale defaultLocale) {
this.defaultLocale = defaultLocale;
}
/** * The configured default locale, if any. * <p>This method may be overridden in subclasses. * @since 4.3 */
@Nullable
public Locale getDefaultLocale() {
return this.defaultLocale;
}
@Override
public Locale resolveLocale(HttpServletRequest request) {
Locale defaultLocale = getDefaultLocale();
if (defaultLocale != null && request.getHeader("Accept-Language") == null) {
return defaultLocale;
}
Locale requestLocale = request.getLocale();
List<Locale> supportedLocales = getSupportedLocales();
if (supportedLocales.isEmpty() || supportedLocales.contains(requestLocale)) {
return requestLocale;
}
Locale supportedLocale = findSupportedLocale(request, supportedLocales);
if (supportedLocale != null) {
return supportedLocale;
}
return (defaultLocale != null ? defaultLocale : requestLocale);
}
@Nullable
private Locale findSupportedLocale(HttpServletRequest request, List<Locale> supportedLocales) {
Enumeration<Locale> requestLocales = request.getLocales();
Locale languageMatch = null;
while (requestLocales.hasMoreElements()) {
Locale locale = requestLocales.nextElement();
if (supportedLocales.contains(locale)) {
if (languageMatch == null || languageMatch.getLanguage().equals(locale.getLanguage())) {
// Full match: language + country, possibly narrowed from earlier language-only match
return locale;
}
}
else if (languageMatch == null) {
// Let's try to find a language-only match as a fallback
for (Locale candidate : supportedLocales) {
if (!StringUtils.hasLength(candidate.getCountry()) &&
candidate.getLanguage().equals(locale.getLanguage())) {
languageMatch = candidate;
break;
}
}
}
}
return languageMatch;
}
@Override
public void setLocale(HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable Locale locale) {
throw new UnsupportedOperationException(
"Cannot change HTTP accept header - use a different locale resolution strategy");
}
}
手動切換語言
(1)、修改 login.html 切換語言鏈接,在請求中攜帶國際化區域信息,代碼如下。
(2)、模仿AcceptHeaderLocaleResolve寫配置文件,命名為MyLocalResolver,實現接口LocaleResolver並重寫LocaleResolver中的方法
package com.ma.ml.config;
import org.springframework.web.servlet.LocaleResolver;
import org.thymeleaf.util.StringUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Locale;
public class MyLocalResolver implements LocaleResolver {
/** * Resolve the current locale via the given request. * Can return a default locale as fallback in any case. * * @param request the request to resolve the locale for * @return the current locale (never {@code null}) */
@Override
public Locale resolveLocale(HttpServletRequest request) {
String language = request.getParameter("l"); //獲得請求路徑中的參數
Locale locale = Locale.getDefault(); //獲取默認的請求,如果沒有我們自己配置的國際化設置,就使用默認spring默認的
if(!StringUtils.isEmpty(language)){
//若請求的鏈接中含有國家化的參數
//zh_CN
String [] strings = language.split("_");
locale = new Locale(strings[0],strings[1]); //重新修改默認值
}
return locale;
}
/** * Set the current locale to the given one. * * @param request the request to be used for locale modification * @param response the response to be used for locale modification * @param locale the new locale, or {@code null} to clear the locale * @throws UnsupportedOperationException if the LocaleResolver * implementation does not support dynamic changing of the locale */
@Override
public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
}
}
(3)、將其像下面一樣使用@Bean注册到spring Mvc中

4、啟動項目即可正常切換訪問


边栏推荐
- 调用api接口生成不同颜色的微信小程序二维码
- The 100000 line transaction lock has opened your eyes.
- My creation anniversary
- 深度学习(初识tensorflow2.版本)之三好学生成绩问题(1)
- 测试实践——app 测试注意点
- From TF 1 X to TF 2.6 (update if you encounter it)
- 进入页面输入框自动获取焦点
- Go learning notes (83) - code specification and common development skills
- Teach you to use shell script to check whether the server program is running
- Logical English structure [key points]
猜你喜欢

2021年全国职业院校技能大赛(中职组)网络安全竞赛试题(1)详细解析教程

DAY 3 数组,前置后置,字符空间,关键词和地址指针

mysql学习总结

Redis novice introduction

2021-11-29 quintic polynomial of trajectory planning

install realsense2: The following packages have unmet dependencies: libgtk-3-dev

WIN10系统实现Redis主从复制

力扣------从数组中移除最大值和最小值

Go learning notes (83) - code specification and common development skills

Notes on sports planning on November 22, 2021
随机推荐
Redis notes (15) - Pipeline (the client packages and sends batch commands to save network overhead)
Opencv depthframe - > pointcloud causes segmentation fault!
Druid data source for background monitoring
国际化配置
Speed test of adding, deleting, modifying and querying 5million pieces of data in a single MySQL table
同花顺炒股软件安全性怎样?在同花顺怎么开户
测试须知——常见接口协议解析
cento7.7安装ELK简单记录
2021-11-22 运动规划杂记
Redis notes (13) - scan and keys search for specific prefix key fields (command format, usage examples, locating large keys)
c语言语法基础之——函数定义学习
2021年全国职业院校技能大赛(中职组)网络安全竞赛试题(2)详解
Js--- get the data with the same key value in the object array to get a new array
LeetCode 958. Completeness checking of binary tree
教你用shell脚本检测服务器程序是否在运行
Automated testing -- Introduction and example of pytest framework
install ompl. sh
Redis master-slave replication in win10 system
QPM performance monitoring components - General
2021 national vocational college skills competition (secondary vocational group) network security competition questions (1) detailed analysis tutorial