共计 7091 个字符,预计需要花费 18 分钟才能阅读完成。
download:高级前端进阶必修:自主打造高扩大的业务组件库含源码 ppt
编程猿自学 it java python go c
基于 springboot 的国际化解决方案
1 底漆
1.1 国际化概述
作为一名服务器端开发人员,这里我想从本人的角度简略概述一下国际化 (国际化也叫 i18n,因为 I 和 N 之间有 18 个字母) 是做什么的:
在理论的国际化我的项目中,责任是让服务器依据客户指定的语言,返回相应语言的内容。
1.2 spring/spring boot 我的项目中国际化游戏性概述
在 spring/springboot 的世界中,国内游戏基于以下界面:
org . spring framework . context . message source .
该接口次要定义了以下三种办法:
公共接口音讯源 {
// 如果国际化配置文件中找不到 var1 对应的音讯,能够给它一个默认值 var3。
@Nullable
String getMessage(String var1,@Nullable Object[] var2,@Nullable String var3,Locale var 4);
// 如果在国际化配置文件中找不到 var1 对应的音讯,抛出异样
String getMessage(String var1,@Nullable Object[] var2,Locale var3) 抛出 NoSuchMessageException
// 这个办法临时没有做太多的钻研,所以本文也不会波及。
string getMessage(MessageSourceResolvable var 1,Locale var2)抛出 NoSuchMessageException
}
复制代码
该接口的三个重要实现类如下:
ResourceBundleMessageSource
ReloadableResourceBundleMessageSource
动态音讯源
2 如何播放 ResourceBundleMessage 源 (默认)
首先咱们来看看 springboot 我的项目中国际化最根本的玩法:
(1)构建 springboot 我的项目(至多增加 web 依赖)。
Messages.properties(默认配置)
用户名 = 溜溜球
复制代码
messages_en_US . 属性
user.name=yoyo-EN
user.name1=nrsc
user.name2=nrsc{0}-{1}
复制代码
音讯_zh_CN.properties
用户名 1 = 张耳
User.name2= 张耳{0}-{1}
复制代码
(3) 创立一个控制器测试类
@RestController
公共类 i18d 专制控制器 {
@主动连线
公有 MessageSource messageSource
@GetMapping(“/hello “)
公共字符串 hello() {
String defaultM = messageSource
。getMessage(“user.name “,null,locale context holder . getlocale());
字符串 message1 = 音讯源
。getMessage(“user.name1 “,null,locale context holder . getlocale());
字符串 message2 = messageSource
。getMessage(“user.name2 “,new String[]{“WW “,” MM”},locale context holder . getlocale());
字符串 message3 = messageSource
。getMessage(“user.nameXX “,null,” defaultName “,locale context holder . getlocale());
返回 default m+” “+message 1+”-“+message 2+” # # “+message 3;
}
}
复制代码
小贴士:
①之所以能够通过 @Autowired 间接从 spring 容器中获取 MessageSource,是因为如果没有配置这种类型的 bean,spring 容器会默认初始化一个这种类型的 bean——ResourceBundleMessage Source,并放入 spring 容器中。答案在上面的源文件中:
org . spring framework . boot . auto configure . context . messagesource auto configuration .
②LocaleContextHolder.getLocale()能够获取该申请头中 Accept-Language 对应的语言环境。
3 如何播放 ReloadableResourceBundleMessage 源码
ReloadableResourceBundleMessageSource 和 ResourceBundleMessageSource 之间最重要的区别在于
前者能够读取位于。属性和。xml,而后者只能读取。属性。
前者能够指定映射文件缓存在内存中的工夫,而后者不能。
ReloadableResourceBundleMessage 源码具体玩法如下:
(2)创立 ReloadableResourceBundleMessage 源,并将其注入到 spring 容器中。代码如下:
@ Bean(” reloadableResourceBundleMessageSource “)
public message source initReloadableResourceBundleMessageSource(){
ReloadableResourceBundleMessageSource message source = new ReloadableResourceBundleMessageSource();
// 指定读取国际化配置文件的根本名称。
message source . set basename(resource utils。class path URL PREFIX+” i18n/messages “);
// 指定代码
message source . setdefaultencoding(” UTF-8 “);
// 指定缓存工夫
message source . setcacheseconds(60);
返回 messageSource
}
复制代码
(3) 将注入的 MessageSource 指定为 ReloadableResourceBundleMessage Source。
@主动连线
@ Qualifier(” reloadableResourceBundleMessageSource “)
公有 MessageSource messageSource
复制代码
(4) 测试留给读者。
4 如何播放 staticmessagesource
ReloadableResourceBundleMessageSource 和 ResourceBundleMessageSource 基于本地文件,而 StaticMessageSource 绝对简略。它的根本玩法如下:
(1)创立一个 StaticMessageSource,同时指定国际化映射内容,而后放入 spring 容器。代码如下:
/**
* 代码和音讯能够来自数据库或任何其余文件系统。
- @返回
*/
@Bean(“staticMessageSource “)
public message source initStaticMessageSource(){
static message source message source = new static message source();
message source . add message(” user . name “,区域设置。美国,“yoyo-EN”);
message source . add message(” user . name 1 “,区域设置。美国,“nrsc”);
message source . add message(” user . name 2 “,区域设置。美国,“nrsc { 0}-{1}”;
message source . add message(” user . name “,locale.china,” 张耳 ”);
message source . add message(” user . name 1 “,locale.china,” Zhang er 1 “);
message source . add message(” user . name 2 “,locale.china,” 张耳 { 0}-{1} “);
返回 messageSource
}
复制代码
(2) 将注入的音讯源指定为 StaticMessageSource。
@主动连线
//@限定符(” reloadableResourceBundleMessageSource “)
@ Qualifier(” static message source “)
公有 MessageSource messageSource
复制代码
(3) 测试留给读者。
5 DIY
5.1 为什么要 DIY?
首先,假如你的我的项目国际化实施方案中有以下两个技术需要:
须要翻译的内容很多,须要结构化的存储和治理 (比方存储在 mysql 数据库中)。
心愿你能用 redis 做缓存。
这个时候你会发现 spring/springboot 提供的以上三款游戏如同都不行。这个时候,咱们就不得不思考 DIY 了。
5.2 从 StaticMessageSource 源代码中寻找 DIY 灵感
依据我的教训,做 DIY 最好的办法就是模拟和革新源代码。下面介绍的三种游戏中,StaticMessageSource 对应的游戏应该是最简略的,也是最好动手的。上面简略看一下它的源代码:
公共类 StaticMessageSource 扩大了 AbstractMessageSource {
/* 从“代码 + 区域设置”键映射到音讯字符串。/
// 保留密钥和音讯的映射 [密钥格局示例:user.name1_zh_CN]
公有最终映射音讯 = new HashMap();
// 保留键和音讯格局的映射
// 当音讯中有占位符时,将应用此映射(如下面示例中的 nrsc{0}-{1})
private final Map cachedMessageFormats = new HashMap();
// 给定代码和地区 (即地区),从音讯图中取出相应语言的音讯。
@笼罩
受爱护的字符串 resolveCodeWithoutArguments(字符串代码,区域设置区域设置){
返回 this . messages . get(code+’ _ ‘+locale . tostring());
}
// 给定的代码和区域设置 (即区域设置) 来自 cachedMessageFormats 映射
// 取出对应语言的 MessageFormat,父类会联合占位符等信息,解析失去具体的音讯。
@笼罩
@Nullable
受爱护的音讯格局 resolveCode(字符串代码,区域设置){
string key = code+’ _ ‘+locale . tostring();
string msg = this . messages . get(key);
if (msg == null) {
返回 null
}
// 这里采纳的是懒加载形式。首先,cachedMessageFormats 的映射是空的。
// 调用时,依据音讯和本地生成音讯格局
// 而后将生成的 MessageFormat 放入 cachedMessageFormats 的映射中。
synchronized(this . cachedmessageformats){
message format message format = this . cachedmessageformats . get(key);
if (messageFormat == null) {
message format = create message format(msg,locale);
this . cachedmessageformats . put(key,message format);
}
返回音讯格局;
}
}
/**
* 将给定的音讯与给定的代码相关联。
- @param code 查找代码
- @param locale 应该在其中找到音讯的区域设置
- @param msg 与此查找代码相关联的音讯
*/
// 增加音讯
public void addMessage(字符串代码、区域设置、字符串音讯){
Assert.notNull(code,“代码不得为空”);
Assert.notNull(区域设置,“区域设置不得为空”);
Assert.notNull(msg,“音讯不得为空”);
this . messages . put(code+’ _ ‘+locale . tostring()、msg);
if (logger.isDebugEnabled()) {
logger.debug(“ 为代码[” + code + “] 和区域设置 [” + locale + “]”) 增加了音讯[“+msg+”]”;
}
}
/**
* 将给定的音讯值与给定的密钥关联为代码。
- @param messages 要注册的音讯,带有音讯代码
* 作为键,音讯文本作为值 - @param locale 应该在其中找到音讯的区域设置
*/
// 批量增加音讯
public void addMessages(地图音讯,区域设置){
Assert.notNull(messages,“音讯映射不能为空”);
messages.forEach((code,msg) -> addMessage(code,locale,msg));
}
@笼罩
公共字符串 toString() {
返回 getClass()。getName()+”:”+this . messages;
}
}
复制代码
从下面的源代码来看,其实很简略。
5.3 用 redis 做 DIY 缓存
上面是一个简略的用 redis 进行缓存的 DIY 计划。
(1)在 redis 中存储数据
@主动连线
公有 RedisTemplate redisTemplate
@测试
public void initData() {
List messageInfos = Arrays.asList(
新的 MessageInfo(“user.name “,区域设置。US.toString(),” yoyo-EN “),
新的 MessageInfo(“user.name1 “,区域设置。US.toString(),” nrsc “),
新的 MessageInfo(“user.name2 “,区域设置。US.toString(),” nrsc{0}-{1} “,
音讯信息 (“ 用户名 ”,locale.china.tostring(),” 张耳 ”),
音讯信息 (” user.name1 “,locale.china.tostring(),” Zhanger1 “),
音讯信息 (” user.name2 “,locale.china.tostring(),” 张耳{0}-{1} “)
);
redisTemplate.opsForValue()。set(“userInfo “,message infos);
}
复制代码
(2) 模拟动态音讯源定制音讯源
@Component(“myMessageSource “)
公共类 MyMessageSource 扩大了 AbstractMessageSource {
private final Map cachedMessageFormats = new HashMap();
@主动连线
公有 RedisTemplate redisTemplate
@笼罩
受爱护的字符串 resolveCodeWithoutArguments(字符串代码,区域设置区域设置){
map map = getMessagesMap();
返回 map . get(code+’ _ ‘+locale . tostring());
}
公有地图 getMessagesMap() {
对象 userInfoList = redis template . ops for value()。get(” userInfo “);
List message infolist =(List)userInfoList;
map map = new HashMap();
for(音讯信息音讯信息: 音讯信息列表){
string key = message info . get code()+’ _ ‘+message info . get locale();
map . computeifaxine(key,k-> message info . getmessage());
}
返回地图;
}
@笼罩
@Nullable
受爱护的音讯格局 resolveCode(字符串代码,区域设置){
string key = code+’ _ ‘+locale . tostring();
String msg = getMessagesMap()。get(键);
if (msg == null) {
返回 null
}
synchronized(this . cachedmessageformats){
message format message format = this . cachedmessageformats . get(key);
if (messageFormat == null) {
message format = create message format(msg,locale);
this . cachedmessageformats . put(key,message format);
}
返回音讯格局;
}
}
}
复制代码
(3) 将注入的 MessageSource 指定为我的自定义 MyMessageSource。
@主动连线
//@限定符(” reloadableResourceBundleMessageSource “)
//@限定符(” staticMessageSource “)
@限定符(” myMessageSource “)
公有 MessageSource messageSource
复制代码
(4) 测试留给读者。