前言
什么是國際化呢?國際慣例,來時(shí)來一段官方介紹:
國際化(internationalization)是設(shè)計(jì)和制造容易適應(yīng)不同區(qū)域要求的產(chǎn)品的一種方式。
它要求從產(chǎn)品中抽離所有地域語言,國家/地區(qū)和文化相關(guān)的元素。
換言之,應(yīng)用程序的功能和代碼設(shè)計(jì)考慮在不同地區(qū)運(yùn)行的需要,其代碼簡(jiǎn)化了不同本地版本的生產(chǎn)。
開發(fā)這樣的程序的過程,就稱為國際化。

在我們實(shí)際開發(fā)中,一個(gè)web應(yīng)用可能要在多個(gè)地區(qū)使用,面對(duì)不同地區(qū)的不同語言,為了適應(yīng)不同的用戶,我們可以嘗試在前端頁面實(shí)現(xiàn)多語言的支持,那么同樣對(duì)于后端返回的一些提示信息,異常信息等,我們后端也可以根據(jù)不同的語言環(huán)境來進(jìn)行國際化處理,返回相應(yīng)的信息。
開發(fā)工具
IDEA、Maven、SpringBoot2.0.5、Jdk1.8、google瀏覽器
SpringBoot中的國際化
原理:
想要使應(yīng)用支持國際化,首先需要知道用戶的語言環(huán)境,即用戶想要看到的語言,我們?cè)O(shè)想在用戶每次請(qǐng)求時(shí)告訴服務(wù)器自己的語言環(huán)境,服務(wù)器收到請(qǐng)求后,根據(jù)不同的語言環(huán)境返回不同的信息來實(shí)現(xiàn)國際化。在spring應(yīng)用中,用戶的語言環(huán)境是通過區(qū)域解析器來識(shí)別的,而區(qū)域解析器有分為好幾種(后面詳細(xì)說),在我們不做配置修改時(shí),spring使用
AcceptHeaderLocaleResolver作為默認(rèn)的區(qū)域解析器,它是根據(jù)HTTP請(qǐng)求 Header中的Accept-language的值來解析,當(dāng)然區(qū)域解析器我們也可以自定義配置。
springboot默認(rèn)就支持國際化。我們只需要只需要作相應(yīng)的配置即可。
1.首先你需要一個(gè)springboot項(xiàng)目。IDEA中分分鐘創(chuàng)建好一個(gè)項(xiàng)目。
2.在resources下定義國際化配置文件,注意名稱必須以messages開始。(在springboot中,當(dāng)我們不修改配置時(shí)默認(rèn)去解析名稱以message開始的properties文件)
messages.properties (默認(rèn)環(huán)境,無法確定語言環(huán)境時(shí),解析該文件中的相應(yīng)信息)
messages_zh_CN.properties(中文語言環(huán)境時(shí),解析該文件中的相應(yīng)信息)
messages_en_US.properties(英文語言環(huán)境時(shí),解析該文件中的相應(yīng)信息)
在三個(gè)配置文件中分別以Key = Value形式存儲(chǔ)如下三條信息,如下:
welcome = 這是一個(gè)支持國際化的項(xiàng)目。
welcome = 這是一個(gè)支持國際化的項(xiàng)目。
welcome = This is a project supporting internationalization.

3.創(chuàng)建thymeleaf頁面
加入thymeleaf依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
在resources/templates目錄下創(chuàng)建hello.html頁面:
嘗試在不同的語言環(huán)境下,通過#{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.創(chuàng)建訪問頁面的controller
注意這里controller的注解時(shí)@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";
}
}
測(cè)試國際化效果
這里使用google瀏覽器進(jìn)行測(cè)試,測(cè)試之前需要安裝插件Language Switcher
Language Switcher : 可以改變當(dāng)前請(qǐng)求的語言環(huán)境(根據(jù)自己的選擇)
啟動(dòng)我們的springboot項(xiàng)目,google瀏覽器訪問 http://127.0.0.1:8080/hi ,可以看到如下頁面:
通過Language Switcher切換語言環(huán)境為English - United States,重新訪問 http://127.0.0.1:8080/hi ,可以看到如下頁面:
ok,大功告成,到此一個(gè)簡(jiǎn)單的國際化項(xiàng)目就完成了。
擴(kuò)展國際化
通過上面的小栗子,我們可以看到一個(gè)簡(jiǎn)單的國際化使用,但是在開發(fā)中中還需要我們進(jìn)行一定的配置,來滿足我們不同情況下的使用。
在返回結(jié)果中獲取國際化信息
很多時(shí)候,后端接收到一個(gè)請(qǐng)求后,需要返回一個(gè)提示信息,而此時(shí)我們可以使這個(gè)返回信息支持國際化
這里就用到了
org.springframework.context.MessageSource接口,MessageSource提供了三個(gè)方法
@Nullable//參數(shù)字段可為空
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中沒有找到消息,則使用默認(rèn)的消息。var2中的參數(shù)將使用標(biāo)準(zhǔn)類庫中的MessageFormat來作消息中替換值。
String getMessage(String code, Object[] args, Locale loc):本質(zhì)上和上一個(gè)方法相同,其區(qū)別在:沒有指定默認(rèn)值,如果沒找到消息,會(huì)拋出一個(gè)NoSuchMessageException異常。
String getMessage(MessageSourceResolvable resolvable, Locale locale):上面方法中所使用的屬性都封裝到一個(gè)MessageSourceResolvable實(shí)現(xiàn)中,而本方法可以指定MessageSourceResolvable實(shí)現(xiàn)。
下面我們實(shí)踐一下:
1.創(chuàng)建一個(gè)以json返回格式的controller,注入MessageSource,注意controller的注解為@RestController
在這里首先我們需要獲取到當(dāng)前請(qǐng)求的Locale,有兩種方法:
Locale locale = LocaleContextHolder.getLocale();
Locale locale = RequestContextUtils.getLocale(request);
兩種方式根據(jù)情況選擇使用,下面是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.啟動(dòng)項(xiàng)目訪問 http://127.0.0.1:8080/ha ,可以看到相應(yīng)語言環(huán)境的返回信息
通過Language Switcher切換語言環(huán)境為English - United States,重新訪問http://127.0.0.1:8080/ha,可以看到如下頁面:
支持占位符國際化信息返回
我們經(jīng)常會(huì)遇到這樣一個(gè)場(chǎng)景,登錄賬號(hào)需要驗(yàn)證碼,填寫完手機(jī)號(hào)獲取驗(yàn)證碼后會(huì)收到一條類似于尊敬的用戶13099999999您好,您的驗(yàn)證碼是6666,這種信息,其實(shí)就是一個(gè)模板,通過改變參數(shù),重復(fù)使用。我們通過國際化資源文件中的占位符,配合MessageSource提供的api也可以實(shí)現(xiàn)。
資源文件中加入如下屬性:
messages.properties,messages_zh_CN.properties
hello=你好:{0} , 你的驗(yàn)證碼為 :{1}
messages_en_US.properties
hello=Hello: {0}, your verification code is: {1}
我們?cè)贘sonController中,創(chuàng)建一個(gè)測(cè)試接口
@RequestMapping("/haha")
public String haha() {
return messageSource.getMessage("hello", new Object[]{"zhangsan","123456"}, LocaleContextHolder.getLocale());
}
啟動(dòng)項(xiàng)目直接,訪問?
http://127.0.0.1:8080/haha 可以看到相應(yīng)語言環(huán)境的返回信息
通過Language Switcher切換語言環(huán)境為English - United States,重新訪問?
http://127.0.0.1:8080/haha ,可以看到如下頁面:
支持國際化的枚舉類
既然返回信息可以實(shí)現(xiàn)國際化,那我們的枚舉類同樣也可以實(shí)現(xiàn)國際化咯
創(chuàng)建一個(gè)枚舉類EnumSuccessOrError.java
/**
* 描述:枚舉類舉例
*
* @author zhengql
* @date 2018/9/26 20:52
*/
public enum EnumSuccessOrError {
SUCCESS(0, "操作成功"),
ERROR(1, "操作失敗");
/**
* 返回狀態(tài)碼
*/
private int statusCode;
/**
* 返回狀態(tài)信息
*/
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;
}
}
如上,剛剛創(chuàng)建的枚舉類是不支持國際化的,我們呢需要改造他,當(dāng)調(diào)用getStatusMsg方法時(shí)根據(jù)語言環(huán)境返回相應(yīng)的國際化字符串??梢詮娜缦聝蓚€(gè)點(diǎn)著手:
getStatusMsg方法改造
資源文件中添加不同語言環(huán)境對(duì)應(yīng)的返回值
先在三個(gè)資源文件中加入不同環(huán)境的返回值:
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");
/**
* 返回狀態(tài)碼
*/
private int statusCode;
/**
* 返回狀態(tài)信息
*/
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;
}
//通過靜態(tài)內(nèi)部類的方式注入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,根據(jù)語言環(huán)境返回國際化字符串
*/
public String getStatusMsg() {
return messageSource.getMessage(statusMsg,null,statusMsg, LocaleContextHolder.getLocale());
}

此時(shí)我們?cè)贘sonController中,再創(chuàng)建一個(gè)測(cè)試接口
@RequestMapping("/enumDemo")
public String enumDemo() {
return EnumSuccessOrError.SUCCESS.getStatusMsg();
}
啟動(dòng)項(xiàng)目直接,訪問 http://127.0.0.1:8080/enumDemo 可以看到相應(yīng)語言環(huán)境的返回信息。