惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

L
Lohrmann on Cybersecurity
I
Intezer
M
MIT News - Artificial intelligence
博客园 - 【当耐特】
Martin Fowler
Martin Fowler
G
GRAHAM CLULEY
Jina AI
Jina AI
Engineering at Meta
Engineering at Meta
AI
AI
SecWiki News
SecWiki News
C
Cybersecurity and Infrastructure Security Agency CISA
IT之家
IT之家
D
Darknet – Hacking Tools, Hacker News & Cyber Security
T
The Blog of Author Tim Ferriss
P
Proofpoint News Feed
S
Security Affairs
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Microsoft Azure Blog
Microsoft Azure Blog
P
Proofpoint News Feed
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Google DeepMind News
Google DeepMind News
H
Hacker News: Front Page
爱范儿
爱范儿
F
Fortinet All Blogs
大猫的无限游戏
大猫的无限游戏
V
V2EX
H
Help Net Security
The Hacker News
The Hacker News
G
Google Developers Blog
Latest news
Latest news
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
T
Tor Project blog
宝玉的分享
宝玉的分享
博客园 - 司徒正美
H
Heimdal Security Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
MyScale Blog
MyScale Blog
S
SegmentFault 最新的问题
C
Cisco Blogs
Blog — PlanetScale
Blog — PlanetScale
S
Security @ Cisco Blogs
B
Blog RSS Feed
B
Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Google Online Security Blog
Google Online Security Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Cyberwarzone
Cyberwarzone
L
LINUX DO - 最新话题
K
Kaspersky official blog
MongoDB | Blog
MongoDB | Blog

Java技术经验分享

生产故障处理SOP分享 | Java技术经验分享 系统稳定性建设实践总结【转载】 | Java技术经验分享 valine访问leancloud国际版异常,评论失效修复 | Java技术经验分享 匆匆忙忙的2021 | Java技术经验分享 聊一下换工作 | Java技术经验分享 IoT系列(2):WIFI设备常见配网方案介绍 | Java技术经验分享 IoT系列(1):什么是物联网 | Java技术经验分享 Java8特性2 - StreamApi | Java技术经验分享 Java8特性1 - lambda表达式&函数式接口 | Java技术经验分享 设计模式(5)-适配器模式 | Java技术经验分享 设计模式(4)-建造者模式 | Java技术经验分享 设计模式(3)-原型模式与浅拷贝和深拷贝 | Java技术经验分享 设计模式(2)-工厂模式图文介绍 | Java技术经验分享 设计模式(1)-带你了解3类8种单例模式 | Java技术经验分享 Java时间处理5---Java8中时区相关类库介绍 | Java技术经验分享 Java时间处理4---Java8中LocalDate、LocalTime、LocalDateTime介绍 | Java技术经验分享 Java时间处理3---Java8中Instant、Duration、Period、Clock介绍 | Java技术经验分享 一些有意思的问答 | Java技术经验分享 Nacos系列博客说明 | Java技术经验分享 菜鸡程序员的2019年度总结 | Java技术经验分享 Java中“附近的人”实现方案讨论及代码实现 | Java技术经验分享 Java时间处理2----时区TimeZone类方法探究(Java8以前) | Java技术经验分享 Java时间处理1----Date和Calendar方法探究(Java8以前) | Java技术经验分享 FastJson中JSONString、JavaBean、JSONObject、JSONArray的转换关系及API示例 | Java技术经验分享 2019.11软考软件设计师归来心得体会及复习备考指南 | Java技术经验分享 你还没用过“约定式提交”吗?那你赶紧来补补知识吧 | Java技术经验分享 教你如何看懂UML中的类图及类图中的关系 | Java技术经验分享 设计模式总览 | Java技术经验分享 萌新入门Github请看这里,学不会远程教 | Java技术经验分享 Hexo的工作原理探究 | Java技术经验分享 Hexo-theme-butterfly修改调整记录教程 | Java技术经验分享 排序8:基数排序 | Java技术经验分享 排序7:归并排序 | Java技术经验分享 排序6:快速排序 | Java技术经验分享 排序5:冒泡排序 | Java技术经验分享 排序4:堆排序 | Java技术经验分享 排序3:选择排序 | Java技术经验分享 排序2:希尔排序 | Java技术经验分享 排序1:直接插入排序 | Java技术经验分享 推荐一款博客一文多发的良心工具OpenWrite | Java技术经验分享 近期学习计划 | Java技术经验分享 Nacos(九):Nacos集群部署和遇到的问题 | Java技术经验分享 Nacos(八):Nacos持久化 | Java技术经验分享 Nacos(七):Nacos共享配置 | Java技术经验分享 Nacos(六):多环境下如何“管理”及“隔离”配置和服务 | Java技术经验分享 Nacos(五):多环境下如何“读取”Nacos中相应的配置 | Java技术经验分享 Nacos(四):SpringCloud项目中接入Nacos作为配置中心 | Java技术经验分享 Nacos(三):Nacos与OpenFeign的对接使用 | Java技术经验分享 Nacos(二):SpringCloud项目中接入Nacos作为注册中心 | Java技术经验分享 Nacos(一):Nacos介绍 | Java技术经验分享 20190719小组分享 | Java技术经验分享 Java中equals和HashCode方法的分析 | Java技术经验分享 Java中==和equals方法的分析 | Java技术经验分享 Java中的自动拆装箱、装箱缓存 | Java技术经验分享 About-blog | Java技术经验分享 Java中的编译、反编译和反编译工具全家桶分享 | Java技术经验分享 finalize()的生命周期(执行过程) | Java技术经验分享 Java关键字之final、finally与finalize方法 | Java技术经验分享 Java中重写、重载 | Java技术经验分享 Java中面向对象的三大特征:继承、封装、多态 | Java技术经验分享 DockerFile介绍 | Java技术经验分享 Docker环境下安装Gitlab | Java技术经验分享 Docker中私有仓库的搭建流程 | Java技术经验分享 Centos7下两种方式安装Docker-CE | Java技术经验分享 Vert.x创建一个Http服务 | Java技术经验分享 Vert.x创建TCP服务端及客户端 | Java技术经验分享 Vert.x Core(二)- Event Bus(事件总线) | Java技术经验分享 Vert.x-Core(一)- 基础篇 | Java技术经验分享 Vert.x介绍 | Java技术经验分享 毕设选题项目本地运行环境搭建教程 | Java技术经验分享 Jupyter Notebooks的安装和使用介绍 | Java技术经验分享 算法笔试题:1元,5元,10元,20元,50元、100元面值人民币组合给定x元的问题 | Java技术经验分享 Quartz学习总结 | Java技术经验分享 SpringBoot2.x集成Redis | Java技术经验分享 SpringBoot2.x集成MongoDB | Java技术经验分享 [SpringCloud学习] - 浅谈微服务架构 | Java技术经验分享 基于hexo和coding免费搭建个人博客网站 | Java技术经验分享 Hello World | Java技术经验分享
SpringBoot项目中实现国际化 | Java技术经验分享
文章作者: LarsCheng · 2019-04-01 · via Java技术经验分享

前言

什么是国际化呢?国际惯例,来时来一段官方介绍:

国际化(internationalization)是设计和制造容易适应不同区域要求的产品的一种方式。它要求从产品中抽离所有地域语言,国家/地区和文化相关的元素。换言之,应用程序的功能和代码设计考虑在不同地区运行的需要,其代码简化了不同本地版本的生产。开发这样的程序的过程,就称为国际化。

在我们实际开发中,一个web应用可能要在多个地区使用,面对不同地区的不同语言,为了适应不同的用户,我们可以尝试在前端页面实现多语言的支持,那么同样对于后端返回的一些提示信息,异常信息等,我们后端也可以根据不同的语言环境来进行国际化处理,返回相应的信息。

开发工具

IDEA、Maven、SpringBoot2.0.5、Jdk1.8、google浏览器

SpringBoot中的国际化

原理
想要使应用支持国际化,首先需要知道用户的语言环境,即用户想要看到的语言,我们设想在用户每次请求时告诉服务器自己的语言环境,服务器收到请求后,根据不同的语言环境返回不同的信息来实现国际化。在spring应用中,用户的语言环境是通过区域解析器来识别的,而区域解析器有分为好几种(后面详细说),在我们不做配置修改时,spring使用AcceptHeaderLocaleResolver作为默认的区域解析器,它是根据HTTP请求 Header中的Accept-language的值来解析,当然区域解析器我们也可以自定义配置。

springboot默认就支持国际化。我们只需要只需要作相应的配置即可。

1.首先你需要一个springboot项目。IDEA中分分钟创建好一个项目。

2.在resources下定义国际化配置文件,注意名称必须以messages开始。(在springboot中,当我们不修改配置时默认去解析名称以message开始的properties文件

  • messages.properties (默认环境,无法确定语言环境时,解析该文件中的相应信息

  • messages_zh_CN.properties(中文语言环境时,解析该文件中的相应信息

  • messages_en_US.properties(英文语言环境时,解析该文件中的相应信息

在三个配置文件中分别以Key = Value形式存储如下三条信息,如下:

1
welcome = 这是一个支持国际化的项目。
1
welcome = 这是一个支持国际化的项目。
1
welcome = This is a project supporting internationalization.

3.创建thymeleaf页面
加入thymeleaf依赖

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

resources/templates目录下创建hello.html页面:
尝试在不同的语言环境下,通过#{welcome}获取信息

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title>demo</title>
</head>
<body>
<p><label th:text="#{welcome}"></label></p>
</body>
</html>

4.创建访问页面的controller
注意这里controller的注解时@Controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;








@Controller
public class BaseController {
@RequestMapping("/hi")
public String hello() {
return "/hello";
}
}

测试国际化效果

这里使用google浏览器进行测试,测试之前需要安装插件Language Switcher
Language Switcher : 可以改变当前请求的语言环境(根据自己的选择)

启动我们的springboot项目,google浏览器访问 http://127.0.0.1:8080/hi ,可以看到如下页面:

通过Language Switcher切换语言环境为English - United States,重新访问 http://127.0.0.1:8080/hi ,可以看到如下页面:

ok,大功告成,到此一个简单的国际化项目就完成了。

扩展国际化

通过上面的小栗子,我们可以看到一个简单的国际化使用,但是在开发中中还需要我们进行一定的配置,来满足我们不同情况下的使用。

在返回结果中获取国际化信息

很多时候,后端接收到一个请求后,需要返回一个提示信息,而此时我们可以使这个返回信息支持国际化

这里就用到了org.springframework.context.MessageSource接口,MessageSource提供了三个方法

@Nullable//参数字段可为空
String getMessage(String var1, @Nullable Object[] var2, @Nullable String var3, Locale var4);

String getMessage(String var1, @Nullable Object[] var2, Locale var3) throws NoSuchMessageException;

String getMessage(MessageSourceResolvable var1, Locale var2) throws NoSuchMessageException;
  • String getMessage(String var1, @Nullable Object[] var2, @Nullable String var3, Locale var4):用来从MessageSource获取消息的基本方法。如果在指定的locale中没有找到消息,则使用默认的消息。var2中的参数将使用标准类库中的MessageFormat来作消息中替换值。

  • String getMessage(String code, Object[] args, Locale loc):本质上和上一个方法相同,其区别在:没有指定默认值,如果没找到消息,会抛出一个NoSuchMessageException异常。

  • String getMessage(MessageSourceResolvable resolvable, Locale locale):上面方法中所使用的属性都封装到一个MessageSourceResolvable实现中,而本方法可以指定MessageSourceResolvable实现。

下面我们实践一下:

1.创建一个以json返回格式的controller,注入MessageSource,注意controller的注解为@RestController

在这里首先我们需要获取到当前请求的Locale,有两种方法:

1
2
Locale locale = LocaleContextHolder.getLocale();
Locale locale = RequestContextUtils.getLocale(request);

两种方式根据情况选择使用,下面是controller代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.example.i18n.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;







@RestController
public class JsonController {

@Autowired
private MessageSource messageSource;

@RequestMapping("/ha")
public String ha() {
return messageSource.getMessage("welcome", null, LocaleContextHolder.getLocale());
}
}

2.启动项目访问 http://127.0.0.1:8080/ha ,可以看到相应语言环境的返回信息

通过Language Switcher切换语言环境为English - United States,重新访问http://127.0.0.1:8080/ha,可以看到如下页面:

支持占位符国际化信息返回

我们经常会遇到这样一个场景,登录账号需要验证码,填写完手机号获取验证码后会收到一条类似于尊敬的用户13099999999您好,您的验证码是6666,这种信息,其实就是一个模板,通过改变参数,重复使用。我们通过国际化资源文件中的占位符,配合MessageSource提供的api也可以实现。
资源文件中加入如下属性:

messages.properties,messages_zh_CN.properties

1
hello=你好:{0} , 你的验证码为 :{1}

messages_en_US.properties

1
hello=Hello: {0}, your verification code is: {1}

我们在JsonController中,创建一个测试接口

1
2
3
4
@RequestMapping("/haha")
public String haha() {
return messageSource.getMessage("hello", new Object[]{"zhangsan","123456"}, LocaleContextHolder.getLocale());
}

启动项目直接,访问 http://127.0.0.1:8080/haha 可以看到相应语言环境的返回信息

通过Language Switcher切换语言环境为English - United States,重新访问 http://127.0.0.1:8080/haha ,可以看到如下页面:

支持国际化的枚举类

既然返回信息可以实现国际化,那我们的枚举类同样也可以实现国际化咯

创建一个枚举类EnumSuccessOrError.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40








public enum EnumSuccessOrError {
SUCCESS(0, "操作成功"),
ERROR(1, "操作失败");




private int statusCode;



private String statusMsg;

EnumSuccessOrError(int statusCode, String statusMsg) {
this.statusCode = statusCode;
this.statusMsg = statusMsg;
}




public int getStatusCode() {
return statusCode;
}




public String getStatusMsg() {
return statusMsg;
}
}

如上,刚刚创建的枚举类是不支持国际化的,我们呢需要改造他,当调用getStatusMsg方法时根据语言环境返回相应的国际化字符串。可以从如下两个点着手:

  • getStatusMsg方法改造
  • 资源文件中添加不同语言环境对应的返回值

先在三个资源文件中加入不同环境的返回值:

messages.properties,messages_zh_CN.properties

1
2
SUCCESS = 操作成功
ERROR = 操作失败

messages_en_US.properties

1
2
SUCCESS=success
ERROR=error

改造后的枚举如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
public enum  EnumSuccessOrError {
SUCCESS(0, "SUCCESS"),
ERROR(1, "ERROR");




private int statusCode;



private String statusMsg;

EnumSuccessOrError(int statusCode, String statusMsg) {
this.statusCode = statusCode;
this.statusMsg = statusMsg;
}

private MessageSource messageSource;

public EnumSuccessOrError setMessageSource(MessageSource messageSource) {
this.messageSource = messageSource;
return this;
}


@Component
public static class ReportTypeServiceInjector {

@Autowired
private MessageSource messageSource;

@PostConstruct
public void postConstruct() {
for (EnumSuccessOrError rt : EnumSet.allOf(EnumSuccessOrError.class))
rt.setMessageSource(messageSource);
}
}




public int getStatusCode() {
return statusCode;
}




public String getStatusMsg() {
return messageSource.getMessage(statusMsg,null,statusMsg, LocaleContextHolder.getLocale());
}

此时我们在JsonController中,再创建一个测试接口

1
2
3
4
@RequestMapping("/enumDemo")
public String enumDemo() {
return EnumSuccessOrError.SUCCESS.getStatusMsg();
}

启动项目直接,访问 http://127.0.0.1:8080/enumDemo 可以看到相应语言环境的返回信息

通过Language Switcher切换语言环境为English - United States,重新访问 http://127.0.0.1:8080/enumDemo ,可以看到如下页面:

参考和感谢

https://blog.csdn.net/linxingliang/article/details/52350238
https://blog.csdn.net/flowingflying/article/details/76358970