一、初探
單體應用完成國際化還是比較簡單的,可以看下面的示例代碼。
引入必要的依賴
<!-- SpringBoot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Validator -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!-- i18n -->
<dependency>
<groupId>org.webjars.bower</groupId>
<artifactId>jquery-i18n-properties</artifactId>
<version>1.2.7</version>
</dependency>
創(chuàng)建一個攔截器
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
import org.springframework.web.servlet.support.RequestContextUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class LocaleInterceptor extends LocaleChangeInterceptor {
private static final String LOCALE = "Accept-Language";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String newLocale = request.getHeader(LOCALE);
if (newLocale != null) {
LocaleResolver localeResolver = RequestContextUtils.getLocaleResolver(request);
if (localeResolver == null) {
throw new IllegalStateException("No LocaleResolver found: not in a DispatcherServlet request?");
}
try {
localeResolver.setLocale(request, response, parseLocaleValue(newLocale));
} catch (IllegalArgumentException ignore) {
}
}
return true;
}
}
創(chuàng)建一個配置類
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.validation.Validator;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.i18n.SessionLocaleResolver;
import java.nio.charset.StandardCharsets;
import java.util.Locale;
@Configuration
public class LocaleConfig implements WebMvcConfigurer{
/**
* 默認解析器 其中l(wèi)ocale表示默認語言,當請求中未包含語種信息,則設置默認語種
* 當前默認為簡體中文,zh_CN
*/
@Bean
public SessionLocaleResolver localeResolver() {
SessionLocaleResolver localeResolver = new SessionLocaleResolver();
localeResolver.setDefaultLocale(Locale.SIMPLIFIED_CHINESE);
return localeResolver;
}
/**
* 默認攔截器
* 攔截請求,獲取請求頭中包含的語種信息并重新注冊語種信息
*/
@Bean
public WebMvcConfigurer localeInterceptor() {
return new WebMvcConfigurer() {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LocaleInterceptor());
}
};
}
@Bean
public LocalValidatorFactoryBean localValidatorFactoryBean() {
LocalValidatorFactoryBean bean = new LocalValidatorFactoryBean();
// 設置消息源
bean.setValidationMessageSource(resourceBundleMessageSource());
return bean;
}
@Bean
public MessageSource resourceBundleMessageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setDefaultEncoding(StandardCharsets.UTF_8.toString());
// 多語言文件地址
messageSource.addBasenames("i18n/message");
return messageSource;
}
@Bean
public MethodValidationPostProcessor validationPostProcessor() {
MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
processor.setValidator(localValidatorFactoryBean().getValidator());
return processor;
}
@Override
public Validator getValidator() {
return localValidatorFactoryBean();
}
}
然后在resource
下創(chuàng)建i18n
目錄,選中右鍵 New
=>Resource Bundle
填入base name
,選擇Project locales
,再Add All
,確定即可。
打開配置文件,填寫對應的中英文數(shù)據
配置一下application.yml
spring:
messages:
basename: i18n.message
cache-duration: 3600
encoding: UTF-8
這樣基本上就好了,使用也很簡單,看下圖
二、深入
對于微服務來講,每個模塊單獨配置國際化還是很繁瑣的事情。所以一般是將國際化存入數(shù)據庫進行統(tǒng)一管理。而本地緩存使用Redis
替換,從而更新國際化之后,相應的模塊能同步。
先把原來的LocaleConfig
和LocaleInterceptor
抽離到公共服務,同時增加一個自定義MessageSource
import com.xxx.common.core.domain.RpcResult;
import com.xxx.common.redis.service.RedisService;
import com.xxx.system.api.RemoteLocaleMessageService;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.context.support.AbstractMessageSource;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import javax.annotation.PostConstruct;
import java.text.MessageFormat;
import java.util.Locale;
import java.util.Map;
@Slf4j
@Component("messageSource")
public class CustomMessageSource extends AbstractMessageSource {
public static final String REDIS_LOCALE_MESSAGE_KEY = "i18n_message";
@Value("${spring.application.name}")
private String appName;
/**
* 這里使用的是dubbo,也可以用open feign
* 需要引入dubbo的依賴包 spring-cloud-starter-dubbo
*/
@DubboReference(version = "1.0.0")
private RemoteLocaleMessageService i18nMessageMapper;
@Autowired
private RedisService redisService;
@PostConstruct
public void init() {
log.info("init i18n message...");
redisService.deleteObject(REDIS_LOCALE_MESSAGE_KEY + ":" + appName);
this.reload();
}
/**
* 重新加載消息到該類的Map緩存中
*/
public Map<String, Map<String, String>> reload() {
Map<String, Map<String, String>> localeMsgMap = redisService.getCacheMap(REDIS_LOCALE_MESSAGE_KEY + ":" + appName);
if (localeMsgMap == null || localeMsgMap.isEmpty()) {
// 加載所有的國際化資源
localeMsgMap = this.loadAllMessageResources();
// 緩存到redis
if (localeMsgMap != null && !localeMsgMap.isEmpty()) {
redisService.setCacheMap(REDIS_LOCALE_MESSAGE_KEY + ":" + appName, localeMsgMap);
}
}
return localeMsgMap;
}
@Override
protected MessageFormat resolveCode(String code, Locale locale) {
String msg = this.getSourceFromCacheMap(code, locale);
return new MessageFormat(msg, locale);
}
@Override
protected String resolveCodeWithoutArguments(String code, Locale locale) {
return this.getSourceFromCacheMap(code, locale);
}
/**
* 加載所有的國際化消息資源
*
* @return
*/
private Map<String, Map<String, String>> loadAllMessageResources() {
// 從數(shù)據庫中查詢所有的國際化資源
RpcResult<Map<String, Map<String, String>>> rpcResult = i18nMessageMapper.getAllLocaleMessage(appName);
return rpcResult.getCode() == 200 ? rpcResult.getData() : null;
}
/**
* 緩存Map中加載國際化資源
*
* @param code
* @param locale
* @return
*/
private String getSourceFromCacheMap(String code, Locale locale) {
// 判斷如果沒有值則會去重新加載數(shù)據
Map<String, Map<String, String>> localeMsgMap = this.reload();
String language = ObjectUtils.isEmpty(locale) ? LocaleContextHolder.getLocale().getLanguage() : locale.getLanguage();
// 獲取緩存中對應語言的所有數(shù)據項
Map<String, String> propMap = localeMsgMap.get(language);
if (!ObjectUtils.isEmpty(propMap) && propMap.containsKey(code)) {
// 如果對應語言中能匹配到數(shù)據項,那么直接返回
return propMap.get(code);
}
// 如果找不到國際化消息,就直接返回code
return code;
}
}
并對LocaleConfig
進行改造
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.validation.MessageInterpolatorFactory;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.validation.Validator;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.i18n.SessionLocaleResolver;
import java.nio.charset.StandardCharsets;
import java.util.Locale;
@Configuration
public class LocaleConfig implements WebMvcConfigurer {
@Autowired
private CustomMessageSource customMessageSource;
/**
* 默認解析器 其中l(wèi)ocale表示默認語言,當請求中未包含語種信息,則設置默認語種
* 當前默認為SIMPLIFIED_CHINESE,zh_CN
*/
@Bean
public SessionLocaleResolver localeResolver() {
SessionLocaleResolver localeResolver = new SessionLocaleResolver();
localeResolver.setDefaultLocale(Locale.SIMPLIFIED_CHINESE);
return localeResolver;
}
/**
* 默認攔截器
* 攔截請求,獲取請求頭中包含的語種信息并重新注冊語種信息
*/
@Bean
public WebMvcConfigurer localeInterceptor() {
return new WebMvcConfigurer() {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LocaleInterceptor());
}
};
}
@Bean
public LocalValidatorFactoryBean localValidatorFactoryBean() {
LocalValidatorFactoryBean bean = new LocalValidatorFactoryBean();
MessageInterpolatorFactory interpolatorFactory = new MessageInterpolatorFactory();
bean.setMessageInterpolator(interpolatorFactory.getObject());
// 設置消息源
bean.setValidationMessageSource(resourceBundleMessageSource());
return bean;
}
@Bean
public MessageSource resourceBundleMessageSource() {
return customMessageSource;
}
@Bean
public MethodValidationPostProcessor validationPostProcessor() {
MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
processor.setValidator(localValidatorFactoryBean().getValidator());
return processor;
}
@Override
public Validator getValidator() {
return localValidatorFactoryBean();
}
}
在需要的模塊中引入即可。
Dubbo provider
的實現(xiàn)
import com.xxx.common.core.domain.RpcResult;
import com.xxx.system.api.RemoteLocaleMessageService;
import com.xxx.system.entity.SysLocaleMessage;
import com.xxx.system.service.ISysLocaleMessageService;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@DubboService(version = "1.0.0")
public class DubboLocaleMessageService implements RemoteLocaleMessageService {
@Autowired
private ISysLocaleMessageService localeMessageService;
private final String COMMON_CONFIG = "common";
// 這里不能返回List,因為無法序列化,會導致Dubbo異常,所以使用Map
@Override
public RpcResult<Map<String, Map<String, String>>> getAllLocaleMessage(String module) {
// 每個module對應的配置
SysLocaleMessage localeMessage = new SysLocaleMessage();
localeMessage.setModule(module);
List<SysLocaleMessage> list = localeMessageService.queryByParam(localeMessage);
if (list == null) {
list = new ArrayList<>();
}
// 公共配置
localeMessage = new SysLocaleMessage();
localeMessage.setModule(COMMON_CONFIG);
list.addAll(localeMessageService.queryByParam(localeMessage));
if (CollectionUtils.isEmpty(list)) {
return RpcResult.fail("no data!");
}
Map<String, Map<String, String>> localeMsgMap = list.stream().collect(Collectors.groupingBy(
// 根據國家地區(qū)分組
SysLocaleMessage::getLocale,
// 收集為Map,key為code,msg為信息
Collectors.toMap(SysLocaleMessage::getCode, SysLocaleMessage::getMsg)
));
return RpcResult.success(localeMsgMap);
}
}
將國際化的增刪改查功能集成到系統(tǒng)管理,就可以通過數(shù)據庫進行管理了。
對國際化進行增刪改后,需要對Redis緩存進行更新文章來源:http://www.zghlxwxcb.cn/news/detail-405812.html
/**
* 清理Redis所有前綴匹配的緩存
*/
private void clearCache() {
Collection<String> keys = redisService.keys(CustomMessageSource.REDIS_LOCALE_MESSAGE_KEY + "*");
keys.stream().forEach(key -> {
redisService.deleteObject(key);
});
}
/**
* 按key清理Redis緩存
*/
private void clearCache(String key) {
redisService.deleteObject(CustomMessageSource.REDIS_LOCALE_MESSAGE_KEY + ":" + key);
}
之前創(chuàng)建的resouce/i18n
目錄則可以刪除。使用也是和單體應用一樣的。文章來源地址http://www.zghlxwxcb.cn/news/detail-405812.html
到了這里,關于springcloud微服務國際化的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!