javascript
Spring MVC-08循序渐进之国际化(AcceptHeaderLocaleResolver)
- 概述
- 概述
- 國際化SpringMVC應用程序
- 將文本元件隔離成屬性文件
- 選擇和讀取正確的屬性文件
- 告訴Spring MVC使用哪個語言區域
- 使用message標簽
- Demo
- 測試
- 源碼
概述
我們之前梳理過Spring相關的國際化的知識點,如下
Spring-國際化信息01-基礎知識
Spring-國際化信息02-MessageSource接口
Spring-國際化信息03-容器級的國際化信息資源
在這里,我們將國際化與Spring MVC結合起來,看SpringMVC如何整合國際化(其實03中已經闡述了)。
這里我們來重新看下
概述
概括的來講,我們需要了解兩個術語
國際化,即我們常講的i18n (internationalization 以i開頭n結尾,中間有18個字母)
本地化,即我們常講的L10N(localization,中間的 10 代表在首字母“L”和尾字母“N”之間省略了 10 個字母) 。這是將國際化應用程序改成支持特定語言區域(locale)的技術。 舉個例子:同樣是日期,2018年02月27日 , 美國顯示為02/27/2018, 澳大利亞則為27/02/2018 , 中國就是2018/02/27。
Java為字符和字符串提供了unicode支持,因此使用Java編寫國際化的應用程序是一件很容易的事情。
國際化應用程序的具體方式取決于有多少靜態數據需要以不同的語言顯示出來,一般來講
如果大量數據都是靜態的,就要針對每一個語言區域單獨創建一個資源版本,這種一般適用于帶有大量靜態HTML頁面的Web應用程序。這個很簡單,我們不討論這個.
如果需要國際化的靜態數據量有限,就可以將文本元素,比如元件標簽和錯誤消息隔離成文本文件。每個文本文件中都保存著一個語言區域的所有文本元素譯文。 隨后,應用程序會自動獲取每一個元素,這樣做的優勢是顯而易見的。我們這里討論是這種場景。
國際化SpringMVC應用程序
國際化和本地化應用程序時,需要具備以下條件:
1. 將文本元文件隔離成屬性文件
2. 選擇和讀取正確的屬性文件
將文本元件隔離成屬性文件
被國際化的應用程序是將每一個語言區域的文本元素都單獨保存在一個獨立的屬性文件中。 每個文件中都包含key/value對,并且每個key都是唯一標示一個特定語言區域的對象 。
key始終是字符串,value則可以是字符串,也可以是其他任意類型的對象。
為了支持美國英語、漢語,就要有2個屬性文件,他們都有著相同的key.
比如英語版本
greetings=hello farewell=goodbye漢語版本
greetings=\u4F60\u597D farewell=\u518D\u89C1漢語中的屬性文件value,漢字需要轉換為Unicode碼, 一般IDE都會自帶這種轉換功能。我們直接輸入漢字,就可以直接得到對應的Unicode碼了。
接下來我們要學習java.util.ResourceBundle ,
詳見 http://blog.csdn.net/yangshangwei/article/details/76946002#t8
ResourceBundle能夠輕松的選擇和讀取特定用戶語言區域的屬性,以及查找值。 ResourceBundle是一個抽象類,但它提供了靜態的getBundle方法,以返回一個具體子類的實例。
ResourceBundle有一個基準名,它可以是任意名稱。 但為了讓ResourceBundle正確的選擇屬性文件,這個文件名中最好必須包含基準名ResourceBundle,后面再接下劃線、語言碼,還可以選擇再加一條下劃線和國家碼。
basename_languageCode_countryCode假設基準名為MyResource, 并且定義了2個語言區域
- US-en
- CN-zh
那么,就會得到如下2個屬性文件
- MyResource_en_US.properties
- MyResource_zh_CN.properties
選擇和讀取正確的屬性文件
如前所述,雖然ResourceBundle是一個抽象類,但是它提供了靜態的getBundle方法來獲取一個ResourceBundle實例
比如
如果沒有找到合適的屬性文件,ResourceBundle對象就會返回到默認的屬性文件, 默認的屬性文件為基準名加上一個擴展名properties. 如果默認文件也沒有找到,則將拋出java.util.MissingResourceException.
隨后讀取值,利用getString方法即可,如果未找到指定的key,則將拋出java.util.MissingResourceException.
但在SpringMVC中,我們不直接使用ResourceBundle,而是利用messageSource bean來告訴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類作為實現, 另外一個是ResourceBundleMessageSource,但是ResourceBundleMessageSource不能重新加載,這意味著如果有任何屬性文件中修改了某一個屬性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>這兩個實現之間的另外一區別是: ReloadableResourceBundleMessageSource是在應用程序目錄下搜索這些屬性文件,而使用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使用哪個語言區域
為用戶選擇語言區域時,最常用的方法或許是通過讀取用戶瀏覽器的accept-language標題值。 accept-language標題提供了用戶偏好哪種語言的信息.
選擇語言區域的其他方法還包括讀取某個session屬性或者cookie。
在Spring MVC中選擇語言區域,可以使用語言解析器Bean,它包括幾個實現,如下
- AcceptHeaderLocaleResolver
- SessionLocaleResolver
- CookieLocaleResolver
這些實現都是org.springframework.web.servlet.i18n包的組成部分。 AcceptHeaderLocaleResolver或許是最容易使用的一個。
如果使用AcceptHeaderLocaleResolver這個語言區域解析器,Spring MVC將會讀取瀏覽器的accept-language標題,來確定瀏覽器接受哪個語言區域. 如果與應用程序支持的語言匹配,這就會使用這個語言區域,否則就會使用默認的語言區域。
下面是使用AcceptHeaderLocaleResolver的localeResolver bean定義
<bean id="localeResolver"class="org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver"></bean>使用message標簽
在Spring MVC中顯示本地化消息的最容易方法就是使用Spring的message標簽。
為了使用message標簽,需要在使用該標簽的所有JSP頁面最前面聲明這個taglib指令
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>message標簽屬性如下,均是可選項
| arguments | 該標簽的參數寫成一個有界的字符、一個對象數組或者單個對象 |
| argumentSeparator | 用來分隔該標簽參數的字符 |
| code | 獲取消息的key |
| htmlEscape | 接受True或者False,表示被渲染文本是否應該進行HTML轉義 |
| JavaScriptEscape | 接受True或者False,表示被渲染文本是否應該進行JavaScript轉義 |
| message | MessageSourceResolvable參數 |
| scope | 保存var屬性中定義的變量的范圍 |
| text | 如果code屬性不存在,或者指定碼無法獲取消息時,所顯示的默認文本 |
| var | 用來保存消息的有界變量 |
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) {// 校驗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" /><!-- 靜態資源文件 --><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><!-- 國際化資源文件 --><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><!-- 如果在國際化資源文件中找不到對應代碼的信息,就用這個代碼作為名稱 --><property name="useCodeAsDefaultMessage" value="true" /></bean><bean id="localeResolver"class="org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver"></bean></beans>這里用到了messageSource 和 localeResolver 這兩個bean。 messageSource 聲明用兩個基準名設置了basenames屬性 /WEB-INF/resource/messages 和 /WEB-INF/resource/labels 。 localeResolver 利用 AcceptHeaderLocaleResolver類實現消息的本地化。
我們支持en和zh兩種語言區域,因此屬性文件都有兩個版本,除此之外我們還添加了當兩種都找不到時的默認語言區域的版本。
為了實現本地化,JSP頁面中的每一段文本都要用message標簽代替。
為了方便查看,我們將當前語言區域和accept-language標題顯示在頁面的最上方
測試
Accept-Language說明 :https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Accept-Language
指令
<language> 用含有兩到三個字符的字符串表示的語言碼。 <locale> 完整的語言標簽。除了語言本身之外,還會包含其他方面的信息,顯示在中劃線("-")后面。最常見的額外信息是國家或地區變種(如"en-US")或者表示所用的字母系統(如"sr-Lat")。其他變種諸如拼字法("de-DE-1996")等通常不被應用在這種場合。 * 任意語言;"*"表示通配符。 ;q= (q-factor weighting) 值代表優先順序,用相對質量價值 表示,又稱為權重。源碼
代碼已提交到github
https://github.com/yangshangwei/SpringMvcTutorialArtisan
總結
以上是生活随笔為你收集整理的Spring MVC-08循序渐进之国际化(AcceptHeaderLocaleResolver)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring MVC-07循序渐进之验证
- 下一篇: Spring MVC-08循序渐进之国际