SpringBoot项目中实现返回结果和枚举类的国际化

前言

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

国际化(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形式存储如下三条信息,如下:

welcome = 这是一个支持国际化的项目。

welcome = 这是一个支持国际化的项目。

welcome = This is a project supporting internationalization.

3.创建thymeleaf页面

加入thymeleaf依赖

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-thymeleaf</artifactId>

</dependency>

在resources/templates目录下创建hello.html页面:

尝试在不同的语言环境下,通过#{welcome}获取信息

<!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

import org.springframework.stereotype.Controller;

import org.springframework.web.bind.annotation.RequestMapping;

/**

* 描述:

*

* @author zhengql

* @date 2018/9/25 19:28

*/

@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,有两种方法:

Locale locale = LocaleContextHolder.getLocale();

Locale locale = RequestContextUtils.getLocale(request);

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

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;

/**

* 描述:

*

* @author zhengql

* @date 2018/9/21 10:54

*/

@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

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

messages_en_US.properties

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

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

@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

/**

* 描述:枚举类举例

*

* @author zhengql

* @date 2018/9/26 20:52

*/

public enum EnumSuccessOrError {

SUCCESS(0, "操作成功"),

ERROR(1, "操作失败");

/**

* 返回状态码

*/

private int statusCode;

/**

* 返回状态信息

*/

private String statusMsg;

EnumSuccessOrError(int statusCode, String statusMsg) {

this.statusCode = statusCode;

this.statusMsg = statusMsg;

}

/**

* @return the statusCode

*/

public int getStatusCode() {

return statusCode;

}

/**

* @return the statusMsg

*/

public String getStatusMsg() {

return statusMsg;

}

}

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

getStatusMsg方法改造

资源文件中添加不同语言环境对应的返回值

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

messages.properties,messages_zh_CN.properties

SUCCESS = 操作成功

ERROR = 操作失败

messages_en_US.properties

SUCCESS=success

ERROR=error

改造后的枚举如下:

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;

}

//通过静态内部类的方式注入bean,并赋值到枚举中

@Component

public static class ReportTypeServiceInjector {

@Autowired

private MessageSource messageSource;

@PostConstruct

public void postConstruct() {

for (EnumSuccessOrError rt : EnumSet.allOf(EnumSuccessOrError.class))

rt.setMessageSource(messageSource);

}

}

/**

* @return the statusCode

*/

public int getStatusCode() {

return statusCode;

}

/**

* @return the statusMsg,根据语言环境返回国际化字符串

*/

public String getStatusMsg() {

return messageSource.getMessage(statusMsg,null,statusMsg, LocaleContextHolder.getLocale());

}


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

@RequestMapping("/enumDemo")

public String enumDemo() {

return EnumSuccessOrError.SUCCESS.getStatusMsg();

}

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,080评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,422评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,630评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,554评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,662评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,856评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,014评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,752评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,212评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,541评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,687评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,347评论 4 331
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,973评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,777评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,006评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,406评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,576评论 2 349

推荐阅读更多精彩内容