日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

这篇文章让你搞懂 SpringMVC 国际化!

發布時間:2023/12/14 javascript 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 这篇文章让你搞懂 SpringMVC 国际化! 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

松哥之前寫過 Spring Boot 國際化的問題,不過那一次沒講源碼,這次咱們整點源碼來深入理解下這個問題。

國際化,也叫 i18n,為啥叫這個名字呢?因為國際化英文是 internationalization ,在 i 和 n 之間有 18 個字母,所以叫 i18n。我們的應用如果做了國際化就可以在不同的語言環境下,方便的進行切換,最常見的就是中文和英文之間的切換,國際化這個功能也是相當的常見。

1.SpringMVC 國際化配置

還是先來說說用法,再來說源碼,這樣大家不容易犯迷糊。我們先說在 SSM 中如何處理國際化問題。

首先國際化我們可能有兩種需求:

  • 在頁面渲染時實現國際化(這個借助于 Spring 標簽實現)
  • 在接口中獲取國際化匹配后的消息

大致上就是上面這兩種場景。接下來松哥通過一個簡單的用法來和大家演示下具體玩法。

首先我們在項目的 resources 目錄下新建語言文件,language_en_US.properties 和 language_zh-CN.properties,如下圖:

內容分別如下:

language_en_US.properties:

login.username=Username login.password=Password

language_zh-CN.properties:

login.username=用戶名 login.password=用戶密碼

這兩個分別對應英中文環境。配置文件寫好之后,還需要在 SpringMVC 容器中提供一個 ResourceBundleMessageSource 實例去加載這兩個實例,如下:

<bean class="org.springframework.context.support.ResourceBundleMessageSource" id="messageSource"><property name="basename" value="language"/><property name="defaultEncoding" value="UTF-8"/> </bean>

這里配置了文件名 language 和默認的編碼格式。

接下來我們新建一個 login.jsp 文件,如下:

<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head><title>Title</title> </head> <body> <spring:message code="login.username"/> <input type="text"> <br> <spring:message code="login.password"/> <input type="text"> <br> </body> </html>

在這個文件中,我們通過 spring:message 標簽來引用變量,該標簽會根據當前的實際情況,選擇合適的語言文件。

接下來我們為 login.jsp 提供一個控制器:

@Controller public class LoginController {@AutowiredMessageSource messageSource;@GetMapping("/login")public String login() {String username = messageSource.getMessage("login.username", null, LocaleContextHolder.getLocale());String password = messageSource.getMessage("login.password", null, LocaleContextHolder.getLocale());System.out.println("username = " + username);System.out.println("password = " + password);return "login";} }

控制器中直接返回 login 視圖即可。

另外我這還注入了 MessageSource 對象,主要是為了向大家展示如何在處理器中獲取國際化后的語言文字。

配置完成后,啟動項目進行測試。

默認情況下,系統是根據請求頭的中 Accept-Language 字段來判斷當前的語言環境的,該這個字段由瀏覽器自動發送,我們這里為了測試方便,可以使用 POSTMAN 進行測試,然后手動設置 Accept_Language 字段。

首先測試中文環境:

然后測試英文環境:

都沒問題,完美!同時觀察 IDEA 控制臺,也能正確打印出語言文字。

上面這個是基于 AcceptHeaderLocaleResolver 來解析出當前的區域和語言的。

有的時候,我們希望語言環境直接通過請求參數來傳遞,而不是通過請求頭來傳遞,這個需求我們通過 SessionLocaleResolver 或者 CookieLocaleResolver 都可以實現。

先來看 SessionLocaleResolver。

首先在 SpringMVC 配置文件中提供 SessionLocaleResolver 的實例,同時配置一個攔截器,如下:

<mvc:interceptors><mvc:interceptor><mvc:mapping path="/**"/><bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"><property name="paramName" value="locale"/></bean></mvc:interceptor> </mvc:interceptors> <bean class="org.springframework.web.servlet.i18n.SessionLocaleResolver" id="localeResolver"> </bean>

SessionLocaleResolver 是負責區域解析的,這個沒啥好說的。攔截器 LocaleChangeInterceptor 則主要是負責參數解析的,我們在配置攔截器的時候,設置了參數名為 locale(默認即此),也就是說我們將來可以通過 locale 參數來傳遞當前的環境信息。

配置完成后,我們還是來訪問剛才的 login 控制器,如下:

此時我們可以直接通過 locale 參數來控制當前的語言環境,這個 locale 參數就是在前面所配置的 LocaleChangeInterceptor 攔截器中被自動解析的。

如果你不想配置 LocaleChangeInterceptor 攔截器也是可以的,直接自己手動解析 locale 參數然后設置 locale 也行,像下面這樣:

@Controller public class LoginController {@AutowiredMessageSource messageSource;@GetMapping("/login")public String login(String locale,HttpSession session) {if ("zh-CN".equals(locale)) {session.setAttribute(SessionLocaleResolver.LOCALE_SESSION_ATTRIBUTE_NAME, new Locale("zh", "CN"));} else if ("en-US".equals(locale)) {session.setAttribute(SessionLocaleResolver.LOCALE_SESSION_ATTRIBUTE_NAME, new Locale("en", "US"));}String username = messageSource.getMessage("login.username", null, LocaleContextHolder.getLocale());String password = messageSource.getMessage("login.password", null, LocaleContextHolder.getLocale());System.out.println("username = " + username);System.out.println("password = " + password);return "login";} }

SessionLocaleResolver 所實現的功能也可以通過 CookieLocaleResolver 來實現,不同的是前者將解析出來的區域信息保存在 session 中,而后者則保存在 Cookie 中。保存在 session 中,只要 session 沒有發生變化,后續就不用再次傳遞區域語言參數了,保存在 Cookie 中,只要 Cookie 沒變,后續也不用再次傳遞區域語言參數了

使用 CookieLocaleResolver 的方式很簡單,直接在 SpringMVC 中提供 CookieLocaleResolver 的實例即可,如下:

<bean class="org.springframework.web.servlet.i18n.CookieLocaleResolver" id="localeResolver"/>

注意這里也需要使用到 LocaleChangeInterceptor 攔截器,如果不使用該攔截器,則需要自己手動解析并配置語言環境,手動解析并配置的方式如下:

@GetMapping("/login3") public String login3(String locale, HttpServletRequest req, HttpServletResponse resp) {CookieLocaleResolver resolver = new CookieLocaleResolver();if ("zh-CN".equals(locale)) {resolver.setLocale(req, resp, new Locale("zh", "CN"));} else if ("en-US".equals(locale)) {resolver.setLocale(req, resp, new Locale("en", "US"));}String username = messageSource.getMessage("login.username", null, LocaleContextHolder.getLocale());String password = messageSource.getMessage("login.password", null, LocaleContextHolder.getLocale());System.out.println("username = " + username);System.out.println("password = " + password);return "login"; }

配置完成后,啟動項目進行測試,這次測試的方式跟 SessionLocaleResolver 的測試方式一致,松哥就不再多說了。

除了前面介紹的這幾種 LocaleResolver 之外,還有一個 FixedLocaleResolver,因為比較少見,松哥這里就不做過多介紹了。

2.Spring Boot 國際化配置

2.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 {@AutowiredMessageSource 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.2 自定義切換

有的小伙伴覺得切換參數放在請求頭里邊好像不太方便,那么也可以自定義解析方式。例如參數可以當成普通參數放在地址欄上,通過如下配置可以實現我們的需求。

@Configuration public class WebConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {LocaleChangeInterceptor interceptor = new LocaleChangeInterceptor();interceptor.setParamName("lang");registry.addInterceptor(interceptor);}@BeanLocaleResolver 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 參數,服務端已經知道當前的環境信息了。

CookieLocaleResolver 也是類似用法,不再贅述。

2.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 文件。

3.LocaleResolver

國際化這塊主要涉及到的組件是 LocaleResolver,這是一個開放的接口,官方默認提供了四個實現。當前該使用什么環境,主要是通過 LocaleResolver 來進行解析的。

LocaleResolver

public interface LocaleResolver {Locale resolveLocale(HttpServletRequest request);void setLocale(HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable Locale locale);}

這里兩個方法:

  • resolveLocale:根據當前請求解析器出 Locale 對象。
  • 設置 Locale 對象。
  • 我們來看看 LocaleResolver 的繼承關系:

    雖然中間有幾個抽象類,不過最終負責實現的其實就四個:

    • AcceptHeaderLocaleResolver:根據請求頭中的 Accept-Language 字段來確定當前的區域語言等。
    • SessionLocaleResolver:根據請求參數來確定區域語言等,確定后會保存在 Session 中,只要 Session 不變,Locale 對象就一直有效。
    • CookieLocaleResolver:根據請求參數來確定區域語言等,確定后會保存在 Cookie 中,只要 Session 不變,Locale 對象就一直有效。
    • FixedLocaleResolver:配置時直接提供一個 Locale 對象,以后不能修改。

    接下來我們就對這幾個類逐一進行分析。

    3.1 AcceptHeaderLocaleResolver

    AcceptHeaderLocaleResolver 直接實現了 LocaleResolver 接口,我們來看它的 resolveLocale 方法:

    @Override public Locale resolveLocale(HttpServletRequest request) {Locale defaultLocale = getDefaultLocale();if (defaultLocale != null && request.getHeader("Accept-Language") == null) {return defaultLocale;}Locale requestLocale = request.getLocale();List<Locale> supportedLocales = getSupportedLocales();if (supportedLocales.isEmpty() || supportedLocales.contains(requestLocale)) {return requestLocale;}Locale supportedLocale = findSupportedLocale(request, supportedLocales);if (supportedLocale != null) {return supportedLocale;}return (defaultLocale != null ? defaultLocale : requestLocale); }
  • 首先去獲取默認的 Locale 對象。
  • 如果存在默認的 Locale 對象,并且請求頭中沒有設置 Accept-Language 字段,則直接返回默認的 Locale。
  • 從 request 中取出當前的 Locale 對象,然后查詢出支持的 supportedLocales,如果 supportedLocales 或者 supportedLocales 中包含 requestLocale,則直接返回 requestLocale。
  • 如果前面還是沒有匹配成功的,則從 request 中取出 locales 集合,然后再去和支持的 locale 進行比對,選擇匹配成功的 locale 返回。
  • 如果前面都沒能返回,則判斷 defaultLocale 是否為空,如果不為空,就返回 defaultLocale,否則返回 defaultLocale。
  • 再來看看它的 setLocale 方法,直接拋出異常,意味著通過請求頭處理 Locale 是不允許修改的。

    @Override public void setLocale(HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable Locale locale) {throw new UnsupportedOperationException("Cannot change HTTP accept header - use a different locale resolution strategy"); }

    3.2 SessionLocaleResolver

    SessionLocaleResolver 的實現多了一個抽象類 AbstractLocaleContextResolver,AbstractLocaleContextResolver 中增加了對 TimeZone 的支持,我們先來看下 AbstractLocaleContextResolver:

    public abstract class AbstractLocaleContextResolver extends AbstractLocaleResolver implements LocaleContextResolver {@Nullableprivate TimeZone defaultTimeZone;public void setDefaultTimeZone(@Nullable TimeZone defaultTimeZone) {this.defaultTimeZone = defaultTimeZone;}@Nullablepublic TimeZone getDefaultTimeZone() {return this.defaultTimeZone;}@Overridepublic Locale resolveLocale(HttpServletRequest request) {Locale locale = resolveLocaleContext(request).getLocale();return (locale != null ? locale : request.getLocale());}@Overridepublic void setLocale(HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable Locale locale) {setLocaleContext(request, response, (locale != null ? new SimpleLocaleContext(locale) : null));}}

    可以看到,多了一個 TimeZone 屬性。從請求中解析出 Locale 還是調用了 resolveLocaleContext 方法,該方法在子類中被實現,另外調用 setLocaleContext 方法設置 Locale,該方法的實現也在子類中。

    我們來看下它的子類 SessionLocaleResolver:

    @Override public Locale resolveLocale(HttpServletRequest request) {Locale locale = (Locale) WebUtils.getSessionAttribute(request, this.localeAttributeName);if (locale == null) {locale = determineDefaultLocale(request);}return locale; }

    直接從 Session 中獲取 Locale,默認的屬性名是 SessionLocaleResolver.class.getName() + ".LOCALE",如果 session 中不存在 Locale 信息,則調用 determineDefaultLocale 方法去加載 Locale,該方法會首先找到 defaultLocale,如果 defaultLocale 不為 null 就直接返回,否則就從 request 中獲取 Locale 返回。

    再來看 setLocaleContext 方法,就是將解析出來的 Locale 保存起來。

    @Override public void setLocaleContext(HttpServletRequest request, @Nullable HttpServletResponse response,@Nullable LocaleContext localeContext) {Locale locale = null;TimeZone timeZone = null;if (localeContext != null) {locale = localeContext.getLocale();if (localeContext instanceof TimeZoneAwareLocaleContext) {timeZone = ((TimeZoneAwareLocaleContext) localeContext).getTimeZone();}}WebUtils.setSessionAttribute(request, this.localeAttributeName, locale);WebUtils.setSessionAttribute(request, this.timeZoneAttributeName, timeZone); }

    保存到 Session 中即可。大家可以看到,這種保存方式其實和我們前面演示的自己保存代碼基本一致,殊途同歸。

    3.3 FixedLocaleResolver

    FixedLocaleResolver 有三個構造方法,無論調用哪一個,都會配置默認的 Locale:

    public FixedLocaleResolver() {setDefaultLocale(Locale.getDefault()); } public FixedLocaleResolver(Locale locale) {setDefaultLocale(locale); } public FixedLocaleResolver(Locale locale, TimeZone timeZone) {setDefaultLocale(locale);setDefaultTimeZone(timeZone); }

    要么自己傳 Locale 進來,要么調用 Locale.getDefault() 方法獲取默認的 Locale。

    再來看 resolveLocale 方法:

    @Override public Locale resolveLocale(HttpServletRequest request) {Locale locale = getDefaultLocale();if (locale == null) {locale = Locale.getDefault();}return locale; }

    這個應該就不用解釋了吧。

    需要注意的是它的 setLocaleContext 方法,直接拋異常出來,也就意味著 Locale 在后期不能被修改。

    @Override public void setLocaleContext( HttpServletRequest request, @Nullable HttpServletResponse response,@Nullable LocaleContext localeContext) {throw new UnsupportedOperationException("Cannot change fixed locale - use a different locale resolution strategy"); }

    3.4 CookieLocaleResolver

    CookieLocaleResolver 和 SessionLocaleResolver 比較類似,只不過存儲介質變成了 Cookie,其他都差不多,松哥就不再重復介紹了。

    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

    5.小結

    好啦,今天主要和小伙伴們聊了下 SpringMVC 中的國際化問題,以及 LocaleResolver 相關的源碼,相信大家對 SpringMVC 的理解應該又更近一步了吧。

    總結

    以上是生活随笔為你收集整理的这篇文章让你搞懂 SpringMVC 国际化!的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。