当前位置:网站首页>Internationalization configuration
Internationalization configuration
2022-06-26 09:55:00 【Ma cute's Ma cute】
1、 Write international resource files
stay src/main/resources Next create a i18n The catalog of , And create the following three files in the directory according to the international resource file naming format ,
login.properties: Effective when there is no language setting
login_en_US.properties : Effective in English
login_zh_CN.properties: Effective in Chinese
After the above internationalization resource files are created ,IDEA Will automatically identify them , And convert to the following mode :
And then you can use it Resource Bundle Visual configuration , Without this plug-in , download Resource Bundle plug-in unit 
Open any internationalization resource file , And switch to Resource Bundle Pattern , And then click “+” Number , Create the required internationalization properties , Here's the picture .
2. Use ResourceBundleMessageSource Manage international resource files
Spring Boot Have been to ResourceBundleMessageSource Provides default auto configuration .
Spring Boot adopt MessageSourceAutoConfiguration Yes ResourceBundleMessageSource Provides default configuration , Part of the source code is as follows .
/* * 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;
}
}
}
}
From the above source code :
Spring Boot take MessageSourceProperties Add as a component to the container ;
MessageSourceProperties The properties of the configuration file are marked with “spring.messages” The configuration at the beginning is bound ;
Spring Boot Get from container MessageSourceProperties Components , And read the name of the internationalization resource file basename( File base name )、encoding( code ) Etc , Encapsulate them in ResourceBundleMessageSource in ;
Spring Boot take ResourceBundleMessageSource Add as a component to the container , Then realize the management of international resource files .
see MessageSourceProperties class , The code is as follows .
/* * 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;
}
}
Through the above code , We can get the following 3 Some information :
MessageSourceProperties by basename、encoding And other properties provide default values ;
basename Represents the base name of the internationalization resource file , The default value is “message”, namely Spring Boot By default, it will get the... Under the classpath message.properties as well as message_XXX.properties As an international resource file ;
stay application.porperties/yml Etc , Use configuration parameters “spring.messages.basename” You can reassign the basic name of the internationalization resource file .
Through the above source code analysis, we can see that ,Spring Boot Default auto configuration has been provided for the management of internationalization resource files , We just need to be here Spring Boot In the global configuration file , Use configuration parameters “spring.messages.basename” Specify the basic name of our customized international resource file , The code is as follows ( When multiple resource files are specified , Separate with commas ).
3. Get international content
Because the page uses Tymeleaf template engine , So we can use the expression #{…} Get international content .
But what we need to pay attention to here is , Access... In the path login.html Whether there is , I put it directly in config Under the webConfig File access , Of course, it can also be named casually 

The area information parser is automatically configured
Spring MVC When internationalizing, there are 2 A very important object :
Locale: Area information object
LocaleResolver: Area information parser , Components in container , Responsible for obtaining regional information objects
We can switch the area information through the above two objects , In order to achieve the purpose of switching languages .
Spring Boot stay WebMvcAutoConfiguration Area information parser in (LocaleResolver) Automatic configuration is performed , Source code is as follows
@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;
}
From the above source code :
This method adds a region information parser to the container by default (LocaleResolver) Components , It will be based on the... Carried in the request header “Accept-Language” Parameters , Get the corresponding area information (Locale) object .
This method uses @ConditionalOnMissingBean annotation , Its parameters name The values for localeResolver( It is consistent with the component name injected into the container by this method ), The meaning of this note is : When the container does not exist, the name is localResolver When the component , This method will take effect . let me put it another way , When we manually add a container named “localeResolver” Components of ,Spring Boot The automatically configured area information parser will fail , The area information parser we defined will take effect
AcceptHeaderLocaleResolver The source code in is as follows
/* * 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");
}
}
Manually switch languages
(1)、 modify login.html Switch language links , Carry the internationalization region information in the request , The code is as follows .
(2)、 imitation AcceptHeaderLocaleResolve Write profile , Name it MyLocalResolver, Implementation interface LocaleResolver And rewrite LocaleResolver The method in 
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"); // Get the parameters in the request path
Locale locale = Locale.getDefault(); // Get the default request , If we don't have our own configured internationalization settings , Use the default spring default
if(!StringUtils.isEmpty(language)){
// If the requested link contains nationalized parameters
//zh_CN
String [] strings = language.split("_");
locale = new Locale(strings[0],strings[1]); // Modify defaults again
}
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)、 Use it as follows @Bean Sign up to spring Mvc in 

4、 Start the project to switch access normally


边栏推荐
- 同花顺炒股软件安全性怎样?在同花顺怎么开户
- npm WARN config global `--global`, `--local` are deprecated. Use `--location=global` instead.npm ER
- mysql 数据库字段查询区分大小写设置
- c语言语法基础之——函数 小程序 求阶乘
- WGCLOUD的web ssh服务端口是多少
- 使用递归或while循环获取父/子层级结构的名称
- Code statistics tools cloc and SCC
- 2021-11-12 vrep视觉传感器配置
- 安装 新版本cmake & swig & tinyspline
- Basic grammar of C language -- pointer (character, one-dimensional array) learning
猜你喜欢

测试须知——常见接口协议解析

Cloud native essay using Hana expression database service on Google kubernetes cluster

软件测试---如何选择合适的正交表

install opencv-contrib-dev to use aruco code

Specific implementation comparison between different programming languages

Single sign on logic

调用api接口生成不同颜色的微信小程序二维码
QPM performance monitoring components - General

The basis of C language grammar -- learning of local variables and storage categories, global variables and storage categories, and macro definitions

Champions League data set (Messi doesn't cry - leaving Barcelona may reach another peak)
随机推荐
The basis of C language grammar -- learning of local variables and storage categories, global variables and storage categories, and macro definitions
npm WARN config global `--global`, `--local` are deprecated. Use `--location=global` instead.npm ER
jz2440---使用uboot燒錄程序
Cloud native essay using Hana expression database service on Google kubernetes cluster
online trajectory generation
install ompl. sh
c语言语法基础之——局部变量及存储类别、全局变量及存储类别、宏定义 学习
SQL query duplicate record
QPM performance monitoring components - General
Summary of common commands of vim
Do you know the //go: instructions in the go source code, go:linkname?
How to create an IE tab in edge browser
2021-11-29 轨迹规划五次多项式
Threadmode interpretation of eventbus
How about the security of flush stock trading software? How to open an account in flush
Champions League data set (Messi doesn't cry - leaving Barcelona may reach another peak)
Industrial and enterprise patent matching data (hundreds of thousands of data) 1998-2014
What is the web SSH service port of wgcloud
2021 national vocational college skills competition (secondary vocational group) network security competition questions (1) detailed analysis tutorial
My creation anniversary