作為一款在 Java 開發社區中廣受歡迎的技術框架,SpringBoot 在開發者和企業的具體實踐中應用廣泛。具體來說,它是一個用于構建基于 Java 的 Web 應用程序和微服務的框架,通過簡化開發流程、提供約定大于配置的原則以及集成大量常用庫和組件,SpringBoot 能夠幫助開發者更快速、更高效地構建應用程序。
為了幫助開發者更好地進行 SpringBoot 的開發,避免開發盲點,我們將 TDengine 資深研發所做的內部分享——《SpringBoot 多語言支持方案》進行了相關整理,給到有需要的開發者參考。
添加依賴
首先,SpringBoot 作為一個強大的 Java 開發腳手架工具框架,已經提供了多語言定義、解析底層工具,我們只需要在項目依賴中引入 spring-boot-starter 和 spring-boot-autoconfigure 兩個包。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
分析 spring-boot-autoconfigure 的源碼我們可以看到,在 MessageSourceAutoConfiguration 類中,默認已經自動裝配了 MessageSource 對象。

添加多語言 message 配置文件
在 IDEA 中我們只需要在 resources 資源包上右鍵:新建–>資源包,在彈出窗口填寫資源包名如:messages 選擇區域設置,默認的有 en、zh_CN、zh_TC 選項。

添加完即可在 resources 包內看到綁定的多語言文件。

注意:在配置文件里查看編輯中文,需要在 IDEA 中修改 message 配置文件。

在配置文件中我們添加 message ,格式為:{code}={message}
METHOD_NOT_ALLOWED=Http method is not supported!
INTERFACE_NOT_FOUND=Interface does not exist!
UNSUPPORTED_MEDIA_TYPE=Not supported MediaType!
ILLEGAL_REQUEST=Illegal request!
SERVICE_UNAVAILABLE=Server resources are unavailable!
SERVER_ERROR=Sorry, an internal server error occurred, please try again later.
INTERNAL_SERVER_ERROR=Internal Server Error.
field.validity.check.failed=Field validity check failed!
bill.account.not-found=bill account not found!
grant.role-group.failed=grant role to group failed!
grant.role-user.failed=grant role to user failed!
add.user-group.failed=add user to group failed!
del.user-group.failed=delete user from group failed!
create.org.failed=create organization failed!
cannot.visit.org=you cannot visit this organization!
wrong.value.parameter=wrong value for parameter!
role.not-found=role not found!
role.update.failed=update role failed!
role.delete.failed=can not delete role!
account.in.arrears=The account is in arrears. Please recharge and try again!
如何使用公共 jar 包內 i18n 資源文件
- 創建公共資源包 i18n 目錄:在 commons 包里添加一個文件夾 i18n-base,這里可以通過一個文件夾避免資源包的覆蓋。

- 依賴 commons 包的模塊,在 yaml 配置文件中添加路徑。
spring:
messages:
basename: i18n-base/messages,messages
以上,我們的多語言框架支持配置、初始化已經完成,接下來就是如何在業務中使用了。
在模塊中使用多語言消息
首先我們來看一個典型的 API 服務請求響應流程。客戶端發出一個接口請求,會經過多個過濾器進行身份認證、API 接口鑒權認證、權限識別,驗證通過后即可進入業務邏輯,最后通過接口返回。返回結果有兩種:
- 過濾器認證失敗直接返回包裝結果 BaseApiResponse
- 認證通過進入業務邏輯,這里又包含兩種情況:
- 業務異常,統一通過 GlobalExceptionHandler 攔截,最后由 ResponseAdvice 處理最終返回結果;
- 無異常,返回業務數據由 ResponseAdvice 處理最終返回結果。
一般來說,外層可以通過 ErrorHandler 捕獲整個流程的異常,包括攔截器、框架層的調用出現的異常,最終由 ResponseAdvice 統一處理并最終返回結果。
整個流程如下圖:

基于這個業務處理流程我們來封裝異常信息國際化的邏輯,如下:
定義多語言 message 獲取 LocaleMessageProvider
- 定義接口
public interface LocaleMessageProvider {
String get(String msgCode, Object... args);
}
- 配置實現類
@Bean
public LocaleMessageProvider localeMessageProvider(MessageSource messageSource){
return (msgCode, args) -> {
Locale locale = LocaleContextHolder.getLocale();
return messageSource.getMessage(msgCode,args,locale);
};
}
- 在返回結構體中使用 LocaleMessageProvider 獲取 message;在 ResponseBodyAdvice 可以為每個 Response 對象設置 messageProvider。
BaseApiResponse.class
private LocaleMessageProvider messageProvider;
public String getmsg() {
String localeMsg = msg;
if (messageProvider != null){
if (StringUtils.hasText(this.msgCode)){
try {
localeMsg = messageProvider.get(this.msgCode, getArgs());
} catch (Exception e) {
if (!StringUtils.hasText(localeMsg)){
localeMsg = this.msgCode;
}
}
if (!StringUtils.hasText(localeMsg)){
localeMsg = StringUtils.hasText(this.msgCode) ? this.msgCode : localeMsg;
}
return localeMsg;
}
public class ResponseAdvice implements ResponseBodyAdvice{
@Override
public Object beforeBodyWrite(Object body, @NotNull MethodParameter returnType,
@NotNull MediaType selectedContentType, @NotNull Class selectedConverterType,
@NotNull ServerHttpRequest request,
@NotNull ServerHttpResponse response) {
int code = ServiceInfoEnum.valueOf(key).getServiceCode() * 1000 + 200;
if (body instanceof BaseApiResponse) {
BaseApiResponse res = (BaseApiResponse) body;
res.setMessageProvider(messageProvider);
}
}
}
在這里提出一個問題,SpringBoot 框架是如何處理語言設置的?在我們定義的 LocaleMessageProvider 里可以使用 LocaleContextHolder.getLocale() 來獲取 Locale。
接下來我們繼續遵循 LocaleContextHolder 的方法,可以先嘗試從內部 localeContext 對象進行獲取,獲取不到的話則取 Locale 的缺省值。
org.springframework.context.i18n.LocaleContextHolder.java
public static Locale getLocale() {
return getLocale(getLocaleContext());
}
public static Locale getLocale(@Nullable LocaleContext localeContext) {
if (localeContext != null) {
Locale locale = localeContext.getLocale();
if (locale != null) {
return locale;
}
}
return (defaultLocale != null ? defaultLocale : Locale.getDefault());
}
在 Locale 類中,我們看到缺省的 locale 最終從系統變量 user.language 獲取,缺省是 en。
java.util.Locale.java
private static volatile Locale defaultLocale = initDefault();
private static Locale initDefault() {
String language, region, script, country, variant;
Properties props = GetPropertyAction.privilegedGetProperties();
language = props.getProperty("user.language", "en");
......//省略代碼
}
接下來我們看下 LocaleContextHolder 中的 Locale 是何時設置的,實際就是在 request 請求過濾器基類 RequestContextFilter 里,通過 request.getLocale() 獲取到 request 的 locale,然后使用 LocaleContextHolder 設置到 LocaleContext 中。
RequestContextFilter.java
protected void doFilterInternal(
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
ServletRequestAttributes attributes = new ServletRequestAttributes(request, response);
initContextHolders(request, attributes);
......//省略代碼
}
private void initContextHolders(HttpServletRequest request, ServletRequestAttributes requestAttributes) {
LocaleContextHolder.setLocale(request.getLocale(), this.threadContextInheritable);
......//省略代碼
}
最終我們看到在 Request 對象里,成功獲取了“accept-lunguage” 請求。
org.apache.catalina.connector.Request.java
public Locale getLocale() {
if (!localesParsed) {
parseLocales();
}
if (locales.size() > 0) {
return locales.get(0);
}
return defaultLocale;
}
protected void parseLocales() {
......//省略代碼
TreeMap<Double, ArrayList<Locale>> locales = new TreeMap<>();
Enumeration<String> values = getHeaders("accept-language");
while (values.hasMoreElements()) {
String value = values.nextElement();
parseLocalesHeader(value, locales);
}
for (ArrayList<Locale> list : locales.values()) {
for (Locale locale : list) {
addLocale(locale);
}
}
}
添加一個多語言消息
- 如果是異常消息,定義異常消息編碼在代碼中 exception 需使用 msgCode,如果是業務包裝類型,那 BaseApiResponse 消息也要使用 msgCode
- 在 message 配置文件中添加對應的 {code}={message}
至此,我們的異常國際化配置就完成了,在客戶端我們只需要在請求里添加一個 header:Accept-Language=zh-CN,就可以驗證返回的結果。例如登錄出錯客戶端接收到的信息為:
{
"code": 500,
"message": "用戶名或者密碼錯誤,請重新輸入。",
"data":{}
}
結語
以上就是基于 SpringBoot 多語言支持方案的完整分享內容,現在你可以操作體驗了,希望本篇文章能帶給你一些幫助。更多示例可參考:
- 異常中使用 messageCode
if (pricePlan.getClusterNum() >= 0 && appNum >= pricePlan.getClusterNum()) {
throw new CommonsException(HttpResponseStatus.PAYMENT_REQUIRED.code(),
"price.plan.limit.instance.number",
"instance number is over limit!");
}
- 國際化文件中添加 message
#messages_en.properties
price.plan.limit.instance.number=instance number is over limit
#messages_zh_CN.properties
price.plan.limit.instance.number=實例數量超過限制
如果你在實操過程中還遇到了其他技術問題,或者正面臨著時序數據的處理難題,也可以添加小T vx:tdengine,和 TDengine 的技術研發人員進行直接溝通。
關于 TDengine
TDengine 核心是一款高性能、集群開源、云原生的時序數據庫(Time Series Database,TSDB),專為物聯網、工業互聯網、電力、IT 運維等場景設計并優化,具有極強的彈性伸縮能力。同時它還帶有內建的緩存、流式計算、數據訂閱等系統功能,能大幅減少系統設計的復雜度,降低研發和運營成本,是一個高性能、分布式的物聯網、工業大數據平臺。當前 TDengine 主要提供兩大版本,分別是支持私有化部署的 TDengine Enterprise 以及全托管的物聯網、工業互聯網云服務平臺 TDengine Cloud,兩者在開源時序數據庫 TDengine OSS 的功能基礎上有更多加強,用戶可根據自身業務體量和需求進行版本選擇。



























