javascript
Spring MVC-08循序渐进之国际化(AcceptHeaderLocaleResolver)
- 概述
- 概述
- 國(guó)際化SpringMVC應(yīng)用程序
- 將文本元件隔離成屬性文件
- 選擇和讀取正確的屬性文件
- 告訴Spring MVC使用哪個(gè)語(yǔ)言區(qū)域
- 使用message標(biāo)簽
- Demo
- 測(cè)試
- 源碼
概述
我們之前梳理過(guò)Spring相關(guān)的國(guó)際化的知識(shí)點(diǎn),如下
Spring-國(guó)際化信息01-基礎(chǔ)知識(shí)
Spring-國(guó)際化信息02-MessageSource接口
Spring-國(guó)際化信息03-容器級(jí)的國(guó)際化信息資源
在這里,我們將國(guó)際化與Spring MVC結(jié)合起來(lái),看SpringMVC如何整合國(guó)際化(其實(shí)03中已經(jīng)闡述了)。
這里我們來(lái)重新看下
概述
概括的來(lái)講,我們需要了解兩個(gè)術(shù)語(yǔ)
國(guó)際化,即我們常講的i18n (internationalization 以i開(kāi)頭n結(jié)尾,中間有18個(gè)字母)
本地化,即我們常講的L10N(localization,中間的 10 代表在首字母“L”和尾字母“N”之間省略了 10 個(gè)字母) 。這是將國(guó)際化應(yīng)用程序改成支持特定語(yǔ)言區(qū)域(locale)的技術(shù)。 舉個(gè)例子:同樣是日期,2018年02月27日 , 美國(guó)顯示為02/27/2018, 澳大利亞則為27/02/2018 , 中國(guó)就是2018/02/27。
Java為字符和字符串提供了unicode支持,因此使用Java編寫國(guó)際化的應(yīng)用程序是一件很容易的事情。
國(guó)際化應(yīng)用程序的具體方式取決于有多少靜態(tài)數(shù)據(jù)需要以不同的語(yǔ)言顯示出來(lái),一般來(lái)講
如果大量數(shù)據(jù)都是靜態(tài)的,就要針對(duì)每一個(gè)語(yǔ)言區(qū)域單獨(dú)創(chuàng)建一個(gè)資源版本,這種一般適用于帶有大量靜態(tài)HTML頁(yè)面的Web應(yīng)用程序。這個(gè)很簡(jiǎn)單,我們不討論這個(gè).
如果需要國(guó)際化的靜態(tài)數(shù)據(jù)量有限,就可以將文本元素,比如元件標(biāo)簽和錯(cuò)誤消息隔離成文本文件。每個(gè)文本文件中都保存著一個(gè)語(yǔ)言區(qū)域的所有文本元素譯文。 隨后,應(yīng)用程序會(huì)自動(dòng)獲取每一個(gè)元素,這樣做的優(yōu)勢(shì)是顯而易見(jiàn)的。我們這里討論是這種場(chǎng)景。
國(guó)際化SpringMVC應(yīng)用程序
國(guó)際化和本地化應(yīng)用程序時(shí),需要具備以下條件:
1. 將文本元文件隔離成屬性文件
2. 選擇和讀取正確的屬性文件
將文本元件隔離成屬性文件
被國(guó)際化的應(yīng)用程序是將每一個(gè)語(yǔ)言區(qū)域的文本元素都單獨(dú)保存在一個(gè)獨(dú)立的屬性文件中。 每個(gè)文件中都包含key/value對(duì),并且每個(gè)key都是唯一標(biāo)示一個(gè)特定語(yǔ)言區(qū)域的對(duì)象 。
key始終是字符串,value則可以是字符串,也可以是其他任意類型的對(duì)象。
為了支持美國(guó)英語(yǔ)、漢語(yǔ),就要有2個(gè)屬性文件,他們都有著相同的key.
比如英語(yǔ)版本
greetings=hello farewell=goodbye漢語(yǔ)版本
greetings=\u4F60\u597D farewell=\u518D\u89C1漢語(yǔ)中的屬性文件value,漢字需要轉(zhuǎn)換為Unicode碼, 一般IDE都會(huì)自帶這種轉(zhuǎn)換功能。我們直接輸入漢字,就可以直接得到對(duì)應(yīng)的Unicode碼了。
接下來(lái)我們要學(xué)習(xí)java.util.ResourceBundle ,
詳見(jiàn) http://blog.csdn.net/yangshangwei/article/details/76946002#t8
ResourceBundle能夠輕松的選擇和讀取特定用戶語(yǔ)言區(qū)域的屬性,以及查找值。 ResourceBundle是一個(gè)抽象類,但它提供了靜態(tài)的getBundle方法,以返回一個(gè)具體子類的實(shí)例。
ResourceBundle有一個(gè)基準(zhǔn)名,它可以是任意名稱。 但為了讓ResourceBundle正確的選擇屬性文件,這個(gè)文件名中最好必須包含基準(zhǔn)名ResourceBundle,后面再接下劃線、語(yǔ)言碼,還可以選擇再加一條下劃線和國(guó)家碼。
basename_languageCode_countryCode假設(shè)基準(zhǔn)名為MyResource, 并且定義了2個(gè)語(yǔ)言區(qū)域
- US-en
- CN-zh
那么,就會(huì)得到如下2個(gè)屬性文件
- MyResource_en_US.properties
- MyResource_zh_CN.properties
選擇和讀取正確的屬性文件
如前所述,雖然ResourceBundle是一個(gè)抽象類,但是它提供了靜態(tài)的getBundle方法來(lái)獲取一個(gè)ResourceBundle實(shí)例
比如
如果沒(méi)有找到合適的屬性文件,ResourceBundle對(duì)象就會(huì)返回到默認(rèn)的屬性文件, 默認(rèn)的屬性文件為基準(zhǔn)名加上一個(gè)擴(kuò)展名properties. 如果默認(rèn)文件也沒(méi)有找到,則將拋出java.util.MissingResourceException.
隨后讀取值,利用getString方法即可,如果未找到指定的key,則將拋出java.util.MissingResourceException.
但在SpringMVC中,我們不直接使用ResourceBundle,而是利用messageSource bean來(lái)告訴Spring MVC要將屬性文件保存在哪里
<bean id="messageSource"class="org.springframework.context.support.ReloadableResourceBundleMessageSource"><property name="basenames" ><list><value>/WEB-INF/resource/messages</value><value>/WEB-INF/resource/labels</value></list></property></bean>上面的bean定義中用ReloadableResourceBundleMessageSource類作為實(shí)現(xiàn), 另外一個(gè)是ResourceBundleMessageSource,但是ResourceBundleMessageSource不能重新加載,這意味著如果有任何屬性文件中修改了某一個(gè)屬性key或者value,并且正在使用ResourceBundleMessageSource,那么要使生效的話,就必須要重啟JVM。
<bean id="resource"class="org.springframework.context.support.ReloadableResourceBundleMessageSource"><property name="basenames" ref="resourceList"/><!-- 刷新資源文件的周期,以秒為單位 --><property name="cacheSeconds" value="5"/></bean><util:list id="resourceList"><value>i18n/fmt_resource</value></util:list>這兩個(gè)實(shí)現(xiàn)之間的另外一區(qū)別是: ReloadableResourceBundleMessageSource是在應(yīng)用程序目錄下搜索這些屬性文件,而使用ResourceBundleMessageSource,屬性文件則必須放在類路徑下,即WEB-INF/class目錄下。
如果只有一組屬性文件,則可以使用basename屬性代替basenames
<bean id="messageSource"class="org.springframework.context.support.ReloadableResourceBundleMessageSource"><property name="basename" ><list><value>/WEB-INF/resource/messages</value></list></property></bean>告訴Spring MVC使用哪個(gè)語(yǔ)言區(qū)域
為用戶選擇語(yǔ)言區(qū)域時(shí),最常用的方法或許是通過(guò)讀取用戶瀏覽器的accept-language標(biāo)題值。 accept-language標(biāo)題提供了用戶偏好哪種語(yǔ)言的信息.
選擇語(yǔ)言區(qū)域的其他方法還包括讀取某個(gè)session屬性或者cookie。
在Spring MVC中選擇語(yǔ)言區(qū)域,可以使用語(yǔ)言解析器Bean,它包括幾個(gè)實(shí)現(xiàn),如下
- AcceptHeaderLocaleResolver
- SessionLocaleResolver
- CookieLocaleResolver
這些實(shí)現(xiàn)都是org.springframework.web.servlet.i18n包的組成部分。 AcceptHeaderLocaleResolver或許是最容易使用的一個(gè)。
如果使用AcceptHeaderLocaleResolver這個(gè)語(yǔ)言區(qū)域解析器,Spring MVC將會(huì)讀取瀏覽器的accept-language標(biāo)題,來(lái)確定瀏覽器接受哪個(gè)語(yǔ)言區(qū)域. 如果與應(yīng)用程序支持的語(yǔ)言匹配,這就會(huì)使用這個(gè)語(yǔ)言區(qū)域,否則就會(huì)使用默認(rèn)的語(yǔ)言區(qū)域。
下面是使用AcceptHeaderLocaleResolver的localeResolver bean定義
<bean id="localeResolver"class="org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver"></bean>使用message標(biāo)簽
在Spring MVC中顯示本地化消息的最容易方法就是使用Spring的message標(biāo)簽。
為了使用message標(biāo)簽,需要在使用該標(biāo)簽的所有JSP頁(yè)面最前面聲明這個(gè)taglib指令
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>message標(biāo)簽屬性如下,均是可選項(xiàng)
| arguments | 該標(biāo)簽的參數(shù)寫成一個(gè)有界的字符、一個(gè)對(duì)象數(shù)組或者單個(gè)對(duì)象 |
| argumentSeparator | 用來(lái)分隔該標(biāo)簽參數(shù)的字符 |
| code | 獲取消息的key |
| htmlEscape | 接受True或者False,表示被渲染文本是否應(yīng)該進(jìn)行HTML轉(zhuǎn)義 |
| JavaScriptEscape | 接受True或者False,表示被渲染文本是否應(yīng)該進(jìn)行JavaScript轉(zhuǎn)義 |
| message | MessageSourceResolvable參數(shù) |
| scope | 保存var屬性中定義的變量的范圍 |
| text | 如果code屬性不存在,或者指定碼無(wú)法獲取消息時(shí),所顯示的默認(rèn)文本 |
| var | 用來(lái)保存消息的有界變量 |
Demo
Domain類
package com.artisan.domain; import java.io.Serializable;import javax.validation.constraints.Size;import org.hibernate.validator.constraints.NotBlank;public class Product implements Serializable {private static final long serialVersionUID = 78L;@NotBlank@Size(min=1, max=10)private String name;private String description;private Float price;public String getName() {return name;}public void setName(String name) {this.name = name;}public String getDescription() {return description;}public void setDescription(String description) {this.description = description;}public Float getPrice() {return price;}public void setPrice(Float price) {this.price = price;} }控制層
package com.artisan.controller;import javax.validation.Valid;import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; import org.springframework.validation.FieldError; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping;import com.artisan.domain.Product;@Controller @RequestMapping("/product") public class ProductController {private static final Log logger = LogFactory.getLog(ProductController.class);@RequestMapping(value="/product_input")public String inputProduct(Model model) {model.addAttribute("product", new Product());return "ProductForm";}@RequestMapping(value="/product_save")public String saveProduct(@Valid @ModelAttribute Product product, BindingResult bindingResult,Model model) {// 校驗(yàn)if (bindingResult.hasErrors()) {FieldError fieldError = bindingResult.getFieldError();logger.info("Code:" + fieldError.getCode() + " ,field:" + fieldError.getField());return "ProductForm";}// save product heremodel.addAttribute("product", product);return "ProductDetails";}}Spring MVC配置文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/mvchttp://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><!-- 掃描控制層的注解,使其成為Spring管理的Bean --><context:component-scan base-package="com.artisan.controller" /><!-- 靜態(tài)資源文件 --><mvc:annotation-driven /><mvc:resources mapping="/css/**" location="/css/" /><mvc:resources mapping="/*.jsp" location="/" /><!-- 視圖解析器 --><bean id="viewResolver"class="org.springframework.web.servlet.view.InternalResourceViewResolver"><property name="prefix" value="/WEB-INF/jsp/" /><property name="suffix" value=".jsp" /></bean><!-- 國(guó)際化資源文件 --><bean id="messageSource"class="org.springframework.context.support.ReloadableResourceBundleMessageSource"><property name="basenames"><list><value>/WEB-INF/resource/messages</value><value>/WEB-INF/resource/labels</value></list></property><!-- 如果在國(guó)際化資源文件中找不到對(duì)應(yīng)代碼的信息,就用這個(gè)代碼作為名稱 --><property name="useCodeAsDefaultMessage" value="true" /></bean><bean id="localeResolver"class="org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver"></bean></beans>這里用到了messageSource 和 localeResolver 這兩個(gè)bean。 messageSource 聲明用兩個(gè)基準(zhǔn)名設(shè)置了basenames屬性 /WEB-INF/resource/messages 和 /WEB-INF/resource/labels 。 localeResolver 利用 AcceptHeaderLocaleResolver類實(shí)現(xiàn)消息的本地化。
我們支持en和zh兩種語(yǔ)言區(qū)域,因此屬性文件都有兩個(gè)版本,除此之外我們還添加了當(dāng)兩種都找不到時(shí)的默認(rèn)語(yǔ)言區(qū)域的版本。
為了實(shí)現(xiàn)本地化,JSP頁(yè)面中的每一段文本都要用message標(biāo)簽代替。
為了方便查看,我們將當(dāng)前語(yǔ)言區(qū)域和accept-language標(biāo)題顯示在頁(yè)面的最上方
測(cè)試
Accept-Language說(shuō)明 :https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Accept-Language
指令
<language> 用含有兩到三個(gè)字符的字符串表示的語(yǔ)言碼。 <locale> 完整的語(yǔ)言標(biāo)簽。除了語(yǔ)言本身之外,還會(huì)包含其他方面的信息,顯示在中劃線("-")后面。最常見(jiàn)的額外信息是國(guó)家或地區(qū)變種(如"en-US")或者表示所用的字母系統(tǒng)(如"sr-Lat")。其他變種諸如拼字法("de-DE-1996")等通常不被應(yīng)用在這種場(chǎng)合。 * 任意語(yǔ)言;"*"表示通配符。 ;q= (q-factor weighting) 值代表優(yōu)先順序,用相對(duì)質(zhì)量?jī)r(jià)值 表示,又稱為權(quán)重。源碼
代碼已提交到github
https://github.com/yangshangwei/SpringMvcTutorialArtisan
總結(jié)
以上是生活随笔為你收集整理的Spring MVC-08循序渐进之国际化(AcceptHeaderLocaleResolver)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Spring MVC-07循序渐进之验证
- 下一篇: Spring MVC-08循序渐进之国际