Spring Boot 國際化踩坑指南
- 2020 年 3 月 6 日
- 筆記
今日乾貨
剛剛發表
查看:66666回復:666
公眾號後台回復 ssm,免費獲取松哥純手敲的 SSM 框架學習乾貨。
國際化,也叫 i18n,為啥叫這個名字呢?因為國際化英文是 internationalization ,在 i 和 n 之間有 18 個字母,所以叫 i18n。我們的應用如果做了國際化就可以在不同的語言環境下,方便的進行切換,最常見的就是中文和英文之間的切換,國際化這個功能也是相當的常見。
在 Spring 中,就通過 AcceptHeaderLocaleResolver 對國際化提供了支援,開發者通過簡單配置,就可以在項目中直接使用國際化功能了。
這一支援,在 Spring Boot 中得到進一步的簡化,在 Spring Boot 中,我們也可以通過寥寥數行程式碼就能方便的實現國際化功能,接下來松哥就來和大家說一說 Spring Boot 中的國際化。
首先,需要給大家先說明一點,項目中的國際化我們往往需要多方面的支援,例如後端做國際化、前端頁面也要做國際化,共同搭配,才能真正實現國際化的功能。本文我先來和各位小夥伴們介紹 Spring Boot 中的國際化,後面我們再來介紹 Vue 的國際化,最後,再把這兩個結合應用到我們的 vhr 項目中,所以前後一共可能有三篇文章,本文是第一篇。
先來看影片吧:
影片 demo 我已經上傳到 GitHub 了,小夥伴們可以直接參考:https://github.com/lenve/javaboy-code-samples
1.基本使用
Spring Boot 和 Spring 一脈相承,對於國際化的支援,默認是通過 AcceptHeaderLocaleResolver 解析器來完成的,這個解析器,默認是通過請求頭的 Accept-Language 欄位來判斷當前請求所屬的環境的,進而給出合適的響應。
所以在 Spring Boot 中做國際化,這一塊我們可以不用配置,直接就開搞。
首先創建一個普通的 Spring Boot 項目,添加 web 依賴即可。項目創建成功後,默認的國際化配置文件放在 resources 目錄下,所以我們直接在該目錄下創建四個測試文件,如下:
- 我們的 message 文件是直接創建在 resources 目錄下的,IDEA 在展示的時候,會多出一個 Resource Bundle,這個大家不用管,千萬別手動去創建這個目錄。
- messages.properties 這個是默認的配置,其他的則是不同語言環境下的配置,en_US 是英語(美國),zh_CN 是中文簡體,zh_TW 是中文繁體(文末附錄裡邊有一個完整的語言簡稱表格)。
四個文件創建好之後,第一個默認的我們可以先空著,另外三個分別填入以下內容:
messages_zh_CN.properties
user.name=江南一點雨
messages_zh_TW.properties
user.name=江南壹點雨
messages_en_US.properties
user.name=javaboy
配置完成後,我們就可以直接開始使用了。在需要使用值的地方,直接注入 MessageSource 實例即可。
❝在 Spring 中需要配置的 MessageSource 現在不用配置了,Spring Boot 會通過
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration
自動幫我們配置一個 MessageSource 實例。 ❞
創建一個 HelloController ,內容如下:
@RestController public class HelloController { @Autowired MessageSource messageSource; @GetMapping("/hello") public String hello() { return messageSource.getMessage("user.name", null, LocaleContextHolder.getLocale()); } }
在 HelloController 中我們可以直接注入 MessageSource 實例,然後調用該實例中的 getMessage 方法去獲取變數的值,第一個參數是要獲取變數的 key,第二個參數是如果 value 中有佔位符,可以從這裡傳遞參數進去,第三個參數傳遞一個 Locale 實例即可,這相當於當前的語言環境。
接下來我們就可以直接去調用這個介面了。
默認情況下,在介面調用時,通過請求頭的 Accept-Language 來配置當前的環境,我這裡通過 POSTMAN 來進行測試,結果如下:
小夥伴們看到,我在請求頭中設置了 Accept-Language 為 zh-CN,所以拿到的就是簡體中文;如果我設置了 zh-TW,就會拿到繁體中文:
是不是很 Easy?
2.自定義切換
有的小夥伴覺得切換參數放在請求頭裡邊好像不太方便,那麼也可以自定義解析方式。例如參數可以當成普通參數放在地址欄上,通過如下配置可以實現我們的需求。
@Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { LocaleChangeInterceptor interceptor = new LocaleChangeInterceptor(); interceptor.setParamName("lang"); registry.addInterceptor(interceptor); } @Bean LocaleResolver localeResolver() { SessionLocaleResolver localeResolver = new SessionLocaleResolver(); localeResolver.setDefaultLocale(Locale.SIMPLIFIED_CHINESE); return localeResolver; } }
在這段配置中,我們首先提供了一個 SessionLocaleResolver 實例,這個實例會替換掉默認的 AcceptHeaderLocaleResolver,不同於 AcceptHeaderLocaleResolver 通過請求頭來判斷當前的環境資訊,SessionLocaleResolver 將客戶端的 Locale 保存到 HttpSession 對象中,並且可以進行修改(這意味著當前環境資訊,前端給瀏覽器發送一次即可記住,只要 session 有效,瀏覽器就不必再次告訴服務端當前的環境資訊)。
另外我們還配置了一個攔截器,這個攔截器會攔截請求中 key 為 lang 的參數(不配置的話是 locale),這個參數則指定了當前的環境資訊。
好了,配置完成後,啟動項目,訪問方式如下:
我們通過在請求中添加 lang 來指定當前環境資訊。這個指定只需要一次即可,也就是說,在 session 不變的情況下,下次請求可以不必帶上 lang 參數,服務端已經知道當前的環境資訊了。
3.其他自定義
默認情況下,我們的配置文件放在 resources 目錄下,如果大家想自定義,也是可以的,例如定義在 resources/i18n 目錄下:
但是這種定義方式系統就不知道去哪裡載入配置文件了,此時還需要 application.properties 中進行額外配置(注意這是一個相對路徑):
spring.messages.basename=i18n/messages
另外還有一些編碼格式的配置等,內容如下:
spring.messages.cache-duration=3600 spring.messages.encoding=UTF-8 spring.messages.fallback-to-system-locale=true
spring.messages.cache-duration 表示 messages 文件的快取失效時間,如果不配置則快取一直有效。
spring.messages.fallback-to-system-locale 屬性則略顯神奇,網上竟然看不到一個明確的答案,後來翻了一會源碼才看出端倪。
這個屬性的作用在 org.springframework.context.support.AbstractResourceBasedMessageSource#getDefaultLocale
方法中生效:
protected Locale getDefaultLocale() { if (this.defaultLocale != null) { return this.defaultLocale; } if (this.fallbackToSystemLocale) { return Locale.getDefault(); } return null; }
從這段程式碼可以看出,在找不到當前系統對應的資源文件時,如果該屬性為 true,則會默認查找當前系統對應的資源文件,否則就返回 null,返回 null 之後,最終又會調用到系統默認的 messages.properties 文件。
4.附錄
搜颳了一個語言簡稱表,分享給各位小夥伴:
語言 |
簡稱 |
---|---|
簡體中文(中國) |
zh_CN |
繁體中文(中國台灣) |
zh_TW |
繁體中文(中國香港) |
zh_HK |
英語(中國香港) |
en_HK |
英語(美國) |
en_US |
英語(英國) |
en_GB |
英語(全球) |
en_WW |
英語(加拿大) |
en_CA |
英語(澳大利亞) |
en_AU |
英語(愛爾蘭) |
en_IE |
英語(芬蘭) |
en_FI |
芬蘭語(芬蘭) |
fi_FI |
英語(丹麥) |
en_DK |
丹麥語(丹麥) |
da_DK |
英語(以色列) |
en_IL |
希伯來語(以色列) |
he_IL |
英語(南非) |
en_ZA |
英語(印度) |
en_IN |
英語(挪威) |
en_NO |
英語(新加坡) |
en_SG |
英語(紐西蘭) |
en_NZ |
英語(印度尼西亞) |
en_ID |
英語(菲律賓) |
en_PH |
英語(泰國) |
en_TH |
英語(馬來西亞) |
en_MY |
英語(阿拉伯) |
en_XA |
韓文(韓國) |
ko_KR |
日語(日本) |
ja_JP |
荷蘭語(荷蘭) |
nl_NL |
荷蘭語(比利時) |
nl_BE |
葡萄牙語(葡萄牙) |
pt_PT |
葡萄牙語(巴西) |
pt_BR |
法語(法國) |
fr_FR |
法語(盧森堡) |
fr_LU |
法語(瑞士) |
fr_CH |
法語(比利時) |
fr_BE |
法語(加拿大) |
fr_CA |
西班牙語(拉丁美洲) |
es_LA |
西班牙語(西班牙) |
es_ES |
西班牙語(阿根廷) |
es_AR |
西班牙語(美國) |
es_US |
西班牙語(墨西哥) |
es_MX |
西班牙語(哥倫比亞) |
es_CO |
西班牙語(波多黎各) |
es_PR |
德語(德國) |
de_DE |
德語(奧地利) |
de_AT |
德語(瑞士) |
de_CH |
俄語(俄羅斯) |
ru_RU |
義大利語(義大利) |
it_IT |
希臘語(希臘) |
el_GR |
挪威語(挪威) |
no_NO |
匈牙利語(匈牙利) |
hu_HU |
土耳其語(土耳其) |
tr_TR |
捷克語(捷克共和國) |
cs_CZ |
斯洛維尼亞語 |
sl_SL |
波蘭語(波蘭) |
pl_PL |
瑞典語(瑞典) |
sv_SE |
西班牙語(智利) |
es_CL |