javascript
Spring MVC -- 国际化
在全球化的今天,現(xiàn)在比過去更需要編寫可以在不同語言國家和地區(qū)部署的應(yīng)用程序。在這方面,需要了解兩個術(shù)語:
- 國際化:常常縮寫為i18n,因為其單詞internationalization以i開始,以n結(jié)尾,在它們中間有18個字母。國際化是開發(fā)支持多語言和數(shù)據(jù)格式的應(yīng)用程序的技術(shù),它使得頁面能夠根據(jù)訪問者的語言或國家來呈現(xiàn)不同的翻譯版本。
- 本地化:常常縮寫為l10n,這是將國際化應(yīng)用程序改成支持特定語言區(qū)域(locale)的技術(shù)。語言區(qū)域是指一個特定的地理、政治或者文化區(qū)域,要考慮到語言區(qū)域的一個操作,就稱作區(qū)分語言區(qū)域的操作。例如,顯示日期就是一個區(qū)分語言區(qū)域的操作,因為日期必須以用戶所在的國家或者地區(qū)使用的格式顯示。2016年11月15,在美國顯示為11/15/2016,但是在澳大利亞則顯示為15/11/2016。
Java謹(jǐn)記國家互的需求,為字符和字符串提供了Unicode支持。因此,用Java編寫國際化的應(yīng)用程序是一件很容易的事情。國際化應(yīng)用程序的具體方式取決于有多少靜態(tài)數(shù)據(jù)需要以不同的語言顯示出來。這里有兩種方法:
- 如果大量數(shù)據(jù)時靜態(tài)的,就要針對每一個語言區(qū)域單獨創(chuàng)建一個資源版本。這種方法一般適用于帶有大量靜態(tài)HTML頁面的Web應(yīng)用程序。這個比較簡單,本篇博客不做討論。
- 如果需要國際化的靜態(tài)數(shù)據(jù)有限,就可以將文本元素,如HTML標(biāo)簽內(nèi)容和錯誤消息隔離為文本文件。每個文本文件中都保留著一個語言區(qū)域的所有文本元素譯文。隨后,應(yīng)用程序會自動獲取每一個元素。這將是本篇博客的重點。
本篇博客首先解釋什么是語言區(qū)域,接著講解國際化應(yīng)用程序技術(shù),最后介紹一個Spring MVC范例。
一 語言區(qū)域
1、java.util.Locale類
每一個Locale對象都代表了一個特定的地理、政治和文化地區(qū)。在操作 Date, Calendar等表示日期/時間的對象時,經(jīng)常會用到;因為不同的區(qū)域,時間表示方式都不同。一個Locale對象包含3個主要參數(shù):language、country和variant:
- language是最重要的部分;但是語言本身有時并不足以區(qū)分一個語言區(qū)域。例如,說英語的國家很多,如美國和英國。但是在美國說的英語,與在英國用的英語并非一模一樣。因此,需要指定語言國家。
- variant是一個特定于供應(yīng)商或者特定于瀏覽器的代號。例如,用WIN表示W(wǎng)indows,用MAC表示Macintosh,用POSIX表示POSIX。兩個variant之間用一個下劃線隔開,并將最重要的部分放在最前面。例如,傳統(tǒng)西班牙語,用language、country和variant參數(shù)構(gòu)造一個locale分別是es、ES、Traditional_WIN。
Locale的構(gòu)造函數(shù)共有3個:
public Locale(String language) public Locale(String language, String country) public Locale(String language, String country, String variant)參數(shù)language是一個有效的ISO語言碼。下表顯示了ISO 639語言碼范例:
| 代碼 | 語言 |
| de | 德語 |
| el | 希臘語 |
| en | 英語 |
| es | 西班牙語 |
| fr | 法語 |
| hi | 印地語 |
| it | 意大利語 |
| ja | 日語 |
| nl | 荷蘭語 |
| pt | 葡萄牙語 |
| ru | 俄語 |
| zh | 漢語 |
參數(shù)country是一個有效的ISO國家碼,由兩個字母組成,ISO 3166中指定為大寫字母。下表展示了ISO 3166國家碼范例:
| 國家 | 代碼 |
| 澳大利亞 | AU |
| 巴西 | BR |
| 加拿大 | CA |
| 中國 | CN |
| 埃及 | EG |
| 法國 | FR |
| 德語 | DE |
| 印度 | IN |
| 墨西哥 | MX |
| 瑞士 | CH |
| 英國 | GB |
| 美國 | US |
Locale類支持非常多的國家和地區(qū)。我們可以通過以下方法,查看Locale支持的全部區(qū)域:
Locale[] ls = Locale.getAvailableLocales();System.out.print("All Locales: ");for(Locale locale:ls) {System.out.printf(locale+", ");}輸出結(jié)果如下:
All Locales: , ar_AE, ar_JO, ar_SY, hr_HR, fr_BE, es_PA, mt_MT, es_VE, bg, zh_TW, it, ko, uk, lv, da_DK, es_PR, vi_VN, en_US, sr_ME, sv_SE, es_BO, en_SG, ar_BH, pt, ar_SA, sk, ar_YE, hi_IN, ga, en_MT, fi_FI, et, sv, cs, sr_BA_#Latn, el, uk_UA, hu, fr_CH, in, es_AR, ar_EG, ja_JP_JP_#u-ca-japanese, es_SV, pt_BR, be, is_IS, cs_CZ, es, pl_PL, tr, ca_ES, sr_CS, ms_MY, hr, lt, es_ES, es_CO, bg_BG, sq, fr, ja, sr_BA, is, es_PY, de, es_EC, es_US, ar_SD, en, ro_RO, en_PH, ca, ar_TN, sr_ME_#Latn, es_GT, sl, ko_KR, el_CY, es_MX, ru_RU, es_HN, zh_HK, no_NO_NY, hu_HU, th_TH, ar_IQ, es_CL, fi, ar_MA, ga_IE, mk, tr_TR, et_EE, ar_QA, sr__#Latn, pt_PT, fr_LU, ar_OM, th, sq_AL, es_DO, es_CU, ar, ru, en_NZ, sr_RS, de_CH, es_UY, ms, el_GR, iw_IL, en_ZA, th_TH_TH_#u-nu-thai, hi, fr_FR, de_AT, nl, no_NO, en_AU, vi, nl_NL, fr_CA, lv_LV, de_LU, es_CR, ar_KW, sr, ar_LY, mt, it_CH, da, de_DE, ar_DZ, sk_SK, lt_LT, it_IT, en_IE, zh_SG, ro, en_CA, nl_BE, no, pl, zh_CN, ja_JP, de_GR, sr_RS_#Latn, iw, en_IN, ar_LB, es_NI, zh, mk_MK, be_BY, sl_SI, es_PE, in_ID, en_GB,?下面選擇其中的一個進行說明,如何利用它們來創(chuàng)建Locale對象:例如,輸出“ja_JP”。其中,ja代表“語言”,這里指日語;“JP”代表國家,這里指日本。
我們可以通過如下方法,創(chuàng)建一個表示中國所用漢語的Locale對象,可以像下面這樣編寫:
Locale local = new Locale("zh", "CN");此外,Locale類提供了static final域(編譯期常量),用來返回特定國家或語言的語言區(qū)域:
public static final Locale CANADA public static final Locale CANADA_FRENCH public static final Locale CHINA public static final Locale CHINESE public static final Locale ENGLISH public static final Locale FRANCE public static final Locale FRENCH public static final Locale GERMAN public static final Locale GERMANY public static final Locale ITALIAN public static final Locale ITALY public static final Locale JAPAN public static final Locale JAPANESE public static final Locale KOREA public static final Locale KOREAN public static final Locale PRC public static final Locale ROOT public static final Locale SIMPLIFIED_CHINESE public static final Locale TAIWAN public static final Locale TRADITIONAL_CHINESE public static final Locale UK因此,也可以通過調(diào)用其static于來構(gòu)造Locale對象,下面的Locale對象是對應(yīng) “中國(簡體中文)”的
Locale locale = Locale.SIMPLIFIED_CHINESE此外,靜態(tài)的getDefault()方法會返回用戶計算機的語言區(qū)域:
Locale locale = Locale.getDefault()2、例子
下面通過示例演示在Date中使用Locale的。參考代碼如下(LocaleTest.java):
import java.util.Locale; import java.util.Date;import java.text.DateFormat;/*** Locale 的測試程序*/ public class LocaleTest {public static void main(String[] args) {// 2種不同的Locale的創(chuàng)建方法 testDiffDateLocales();// 顯示所有的Locales testAllLocales();}/*** 2種不同的Locale的創(chuàng)建方法*/private static void testDiffDateLocales() {// date為2013-09-19 14:22:30Date date = new Date(113, 8, 19, 14, 22, 30);System.out.println(date);// 創(chuàng)建“簡體中文”的LocaleLocale localeCN = Locale.SIMPLIFIED_CHINESE;// 創(chuàng)建“英文/美國”的LocaleLocale localeUS = new Locale("en", "US");// 獲取“簡體中文”對應(yīng)的date字符串String cn = DateFormat.getDateInstance(DateFormat.MEDIUM, localeCN).format(date);// 獲取“英文/美國”對應(yīng)的date字符串String us = DateFormat.getDateInstance(DateFormat.MEDIUM, localeUS).format(date);System.out.printf("cn=%s\nus=%s\n", cn, us);}/*** 顯示所有的Locales*/private static void testAllLocales() {Locale[] ls = Locale.getAvailableLocales();System.out.print("All Locales: ");for(Locale locale:ls) {System.out.printf(locale+", ");}System.out.println();} }輸出如下:
Thu Sep 19 14:22:30 CST 2013 cn=2013-9-19 us=Sep 19, 2013 All Locales: , ar_AE, ar_JO, ar_SY, hr_HR, fr_BE, es_PA, mt_MT, es_VE, bg, zh_TW, it, ko, uk, lv, da_DK, es_PR, vi_VN, en_US, sr_ME, sv_SE, es_BO, en_SG, ar_BH, pt, ar_SA, sk, ar_YE, hi_IN, ga, en_MT, fi_FI, et, sv, cs, sr_BA_#Latn, el, uk_UA, hu, fr_CH, in, es_AR, ar_EG, ja_JP_JP_#u-ca-japanese, es_SV, pt_BR, be, is_IS, cs_CZ, es, pl_PL, tr, ca_ES, sr_CS, ms_MY, hr, lt, es_ES, es_CO, bg_BG, sq, fr, ja, sr_BA, is, es_PY, de, es_EC, es_US, ar_SD, en, ro_RO, en_PH, ca, ar_TN, sr_ME_#Latn, es_GT, sl, ko_KR, el_CY, es_MX, ru_RU, es_HN, zh_HK, no_NO_NY, hu_HU, th_TH, ar_IQ, es_CL, fi, ar_MA, ga_IE, mk, tr_TR, et_EE, ar_QA, sr__#Latn, pt_PT, fr_LU, ar_OM, th, sq_AL, es_DO, es_CU, ar, ru, en_NZ, sr_RS, de_CH, es_UY, ms, el_GR, iw_IL, en_ZA, th_TH_TH_#u-nu-thai, hi, fr_FR, de_AT, nl, no_NO, en_AU, vi, nl_NL, fr_CA, lv_LV, de_LU, es_CR, ar_KW, sr, ar_LY, mt, it_CH, da, de_DE, ar_DZ, sk_SK, lt_LT, it_IT, en_IE, zh_SG, ro, en_CA, nl_BE, no, pl, zh_CN, ja_JP, de_GR, sr_RS_#Latn, iw, en_IN, ar_LB, es_NI, zh, mk_MK, be_BY, sl_SI, es_PE, in_ID, en_GB,二 國際化Spring MVC應(yīng)用程序
?國際化和本地化應(yīng)用程序時,需要具備以下條件:
- 將文本元素隔離成屬性文件;
- 選擇和讀取正確的屬性文件;
下面詳細(xì)介紹這兩個步驟,并進行簡單的示范。
1、將文本元素隔離成屬性文件
國際化的應(yīng)用程序是將每一個語言區(qū)域的文本元素都單獨保存在一個獨立的屬性文件中。每個文件中都包含key/value對,并且每個key都唯一標(biāo)示一個特定語言區(qū)域的對象。key始終是字符串,value則可以是字符串,也可以是其他任意類型的對象。例如,為了支持英語、德語、以及漢語,就要有3個屬性文件,它們都有相同的key。
以下是英語版的屬性文件。注意:它有g(shù)reetings和farewell兩個key:
greetings=Hello farewell=Goodbye德語版的屬性文件如下:
greetings=Hallo farewell=Auf Wiedersehen漢語版的屬性文件如下:
greetings=\u4f60\u597d farewell=\u518d\u89c1如果你是中文用戶,你可以使用任何中文文本編輯器并寫入漢字字符。完成后,將文件轉(zhuǎn)換成Unicode。
現(xiàn)在,要學(xué)習(xí)java.util.ResourceBundle類。它能使你輕松的選擇和讀取特定于用戶語言區(qū)域的屬性,以及查找值。ResourceBundle是一個抽象類,但是它提供了靜態(tài)的getBundle()方法,返回一個具體子類的實例。
ResourceBoundle有一個基準(zhǔn)名,它可以是任意名稱。但是,為了讓ResourceBundle正確的選擇屬性文件,這個文件名中最好必須包含基準(zhǔn)名ResourceBundle,后面再接下劃線、語言碼,還可以選擇再加一條下劃線和國家碼。屬性文件名的格式如下所示:
basename_lanuageCode_countryCode例如,假設(shè)基準(zhǔn)名為MyResources,并且定義了以下3個語言區(qū)域:
- DE-de;
- CN-zh;
- US-en;
那么就會得到下面這3個屬性文件:
- MyResources_de_DE.properties;
- MyResources_zh_CN.properties;
- MyResources_en_US.properties;
2、選擇和讀取正確的屬性文件
如前所述。ResourceBundle是一個抽象類。盡管如此,還是可以通過調(diào)用它的靜態(tài)getBundle()方法來獲取一個ResourceBundle實例。它的重載簽名如下:
public static final ResourceBundle getBundle(String baseName) public static final ResourceBundle getBundle(String baseName,Locale locale)例如:
ResourceBundle rb = ResourceBundle .getBundle("MyResources",Locale.US);這樣就會加載ResourceBundle在相應(yīng)屬性文件中的值。
如果沒有找到合適的屬性文件,ResourceBundle對象就會返回到默認(rèn)的屬性文件。默認(rèn)屬性文件的名稱為基準(zhǔn)名加一個擴展名properties。在這個例子中,默認(rèn)文件就是MyResource.properties。如果沒有找到默認(rèn)文件,將拋出java.util.MissingResourceException。
隨后,讀取值,利用ResourceBundle類的getString()方法傳入一個key。
public String getString(String key)如果沒有找到指定key的入口,就會拋出出java.util.MissingResourceException。
在Spring MVC中,不直接使用ResourceBundle,而是利用messageSource bean告訴Spring MVC要將屬性文件保存在哪里。例如,下面的messageSource 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></bean>上面的bean定義中用了ReloadableResourceBundleMessageSource?類作為的實現(xiàn)類。另一個實現(xiàn)類是ResourceBundleMessageSource,但是它是不能重新加載的,這意味著,如果在任意屬性文件中修改了某一個屬性key或者value,并且正在使用ResourceBundleMessageSource,那么要使修改生效,就必須先重啟JVM。
這兩個實現(xiàn)之間的另一個區(qū)別是:使用ReloadableResourceBundleMessageSource時,是在應(yīng)用程序目錄下搜索這些屬性文件,而使用ResourceBundleMessageSource時,屬性文件則必須放在類路徑下,即/WEB-INF/class目錄下。
還要注意,如果只有一組屬性文件,則可以用basename屬性替代basenames,像下面這樣:
<bean id="messageSource"class="org.springframework.context.support.ReloadableResourceBundleMessageSource"><property name="basename" value="/WEB-INF/resource/messages" /></bean>3、告訴Spring MVC使用哪個語言區(qū)域
為用戶選擇語言區(qū)域時,最常用的方法或許是讀取用戶瀏覽器的accept-language標(biāo)題值。accept-language標(biāo)題值提供了關(guān)于偏好哪種語言的信息。
選擇語言區(qū)域的其它方法還包括讀取某個session屬性或者cookie。
在Spring MVC中選擇語言區(qū)域,可以使用語言區(qū)域解析器bean。它有幾個實現(xiàn):
- AcceptHeaderLocaleResolver;
- CookieLocaleResolver;
- SessionLocaleResolver;
- FixedLocaleResolver.;
所有這些實現(xiàn)都是org.springframework.web.server.i18n包的組成部分。AccpetHeaderLocaleResolver或許是其中最容易使用的一個。如果選擇使用這個語言區(qū)域解析器,Spring MVC將會讀取瀏覽器的accept-language標(biāo)題,來確定瀏覽器要接受哪個語言區(qū)域。如果瀏覽器的某個語言區(qū)域與Spring MVC應(yīng)用程序支持的某個語言區(qū)域匹配,就會使用這個語言區(qū)域。如果沒有找到匹配的語言區(qū)域,則使用默認(rèn)的語言區(qū)域。
下面是使用AccpetHeaderLocaleResolver的localeResovler bean定義:
<bean id="localeResolver"class="org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver"></bean>4、使用message標(biāo)簽
在Spring MVC中顯示本地化消息最容易方法是使用Spring的message標(biāo)簽。為了使用這個標(biāo)簽,要在使用該標(biāo)簽的所有JSP頁面最前面聲明如下一個taglib指令:
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>message標(biāo)簽的屬性見下表,所有這些屬性都是可選的:
| 屬性 | 描述 |
| arguments | 該標(biāo)簽的參數(shù)寫成一個有界的字符串、一個對象數(shù)組或者單個對象 |
| argumentSeparator | 用來分割該標(biāo)簽參數(shù)的字符 |
| code | 獲取消息的key |
| htmlEscape | 接受true或者false,表示被渲染文本是都應(yīng)該進行HTML轉(zhuǎn)義 |
| javaScriptEscape | 接受true或者false,表示被渲染文本是都應(yīng)該進行JavaScript轉(zhuǎn)義 |
| message | MessageSourceResolvable參數(shù) |
| scope | 保存var屬性中定義的變量的范圍 |
| text | 如果code屬性不存在,或者指定碼無法獲取消息,所顯示的默認(rèn)文本 |
| var | 用于保存消息的有界變量 |
三 范例
下面我們將會創(chuàng)建一個i18n應(yīng)用程序,該應(yīng)用展示了用localeResovler bean將JSP頁面中的消息本地化的方法。
1、目錄結(jié)構(gòu)
i18n應(yīng)用程序目錄結(jié)構(gòu)如下圖:
2、Product類
package domain; import java.io.Serializable; import java.math.BigDecimal;public class Product implements Serializable {private static final long serialVersionUID = 78L;private String name;private String description;private BigDecimal 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 BigDecimal getPrice() {return price;}public void setPrice(BigDecimal price) {this.price = price;} }3、Validator
該應(yīng)用包含一個名為ProductValidator的驗證器:
package validator;import java.math.BigDecimal;import org.springframework.validation.Errors; import org.springframework.validation.ValidationUtils; import org.springframework.validation.Validator;import domain.Product;public class ProductValidator implements Validator {@Overridepublic boolean supports(Class<?> klass) {//支持Product類?return Product.class.isAssignableFrom(klass);}//將目標(biāo)對象target的錯誤注冊到errors對象中 @Overridepublic void validate(Object target, Errors errors) {Product product = (Product) target;//如果目標(biāo)對象的name屬性為null,或者為""字符串,則將錯誤注冊到errors對象ValidationUtils.rejectIfEmpty(errors, "name", "productname.required");BigDecimal price = product.getPrice();//如果價格為負(fù)數(shù) 則將錯誤注冊到errors對象中if (price != null && price.compareTo(BigDecimal.ZERO) < 0) {errors.rejectValue("price", "price.negative");}} }ProductValidator驗證器是一個非常簡單的驗證器。它的validate()方法會檢驗Product是否有名稱,并且價格是否不為負(fù)數(shù)。
4、Controller類
在Controller類中通過實例化validator類,可以使用Spring驗證器:
package 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 validator.ProductValidator; import domain.Product;@Controllerpublic class ProductController {private static final Log logger = LogFactory.getLog(ProductController.class);@RequestMapping(value="/add-product")public String inputProduct(Model model) {model.addAttribute("product", new Product());return "ProductForm";}@RequestMapping(value="/save-product")public String saveProduct(@ModelAttribute Product product, BindingResult bindingResult,Model model) {//創(chuàng)建一個ProductValidator,并調(diào)用其validate()方法校驗Product對象,并將驗證錯誤填入bindingResult中。ProductValidator productValidator = new ProductValidator();productValidator.validate(product, bindingResult);if (bindingResult.hasErrors()) {FieldError fieldError = bindingResult.getFieldError();logger.debug("Code:" + fieldError.getCode() + ", field:"+ fieldError.getField());return "ProductForm";}// save product here //model.addAttribute("product", product);return "ProductDetails";}}ProductController類的saveProduct()方法,有三個參數(shù):
- 第一個參數(shù)product,使用了注解@ModelAttribute,該對象的各個屬性被用來接受表單的各個字段信息,并且將"product"屬性添加到Model對象中;
- 第二個參數(shù)bindingResult中設(shè)置了Spring所有的綁定錯誤(主要是類型轉(zhuǎn)換問題,例如將表單String轉(zhuǎn)換為BigDecimal?類型);
- 第三個參數(shù)是Model。
注意:BindingResult接口是Errors接口的子類,在請求處理方法的簽名中使用了BindingResult參數(shù),就是告訴Spring關(guān)于表單對象數(shù)據(jù)校驗的錯誤將由我們自己來處理,否則Spring會直接拋出異常。
該方法創(chuàng)建一個ProductValidator,并調(diào)用其validate()方法校驗Product對象,并將驗證錯誤填入bindingResult中。
ProductValidator productValidator = new ProductValidator(); productValidator.validate(product, bindingResult);為了檢驗該驗證器是否生成錯誤消息,需要在BindingResult中調(diào)用hasErrors()方法:
if (bindingResult.hasErrors()) {FieldError fieldError = bindingResult.getFieldError();logger.debug("Code:" + fieldError.getCode() + ", field:"+ fieldError.getField());return "ProductForm";}如果存在表單綁定錯誤或者是輸入驗證錯誤,將會打印出錯誤相關(guān)的字段,并重定位到ProductForm.jsp頁面。
如果表單輸入的數(shù)據(jù)均合法,則會重定位到ProductDetails.jsp頁面。
5、視圖
i18n應(yīng)用包含2個視圖文件:
ProductForm.jsp:
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <!DOCTYPE html> <html> <head> <title><spring:message code="page.productform.title"/></title> <style type="text/css">@import url("<c:url value="/css/main.css"/>");</style> </head> <body><div id="global"> Current Locale : ${pageContext.response.locale} <br/> accept-language header: ${header["accept-language"]}<form:form modelAttribute="product" action="save-product" method="post"><fieldset><legend><spring:message code="form.name"/></legend><p><label for="name"><spring:message code="label.productName" text="default text" />:</label><form:input id="name" path="name" cssErrorClass="error"/><form:errors path="name" cssClass="error"/></p><p><label for="description"><spring:message code="label.description"/>: </label><form:input id="description" path="description"/></p><p><label for="price"><spring:message code="label.price" text="default text" />: </label><form:input id="price" path="price" cssErrorClass="error"/><form:errors path="price" cssClass="error"/></p><p id="buttons"><input id="reset" type="reset" tabindex="4" value="<spring:message code="button.reset"/>"><input id="submit" type="submit" tabindex="5" value="<spring:message code="button.submit"/>"></p></fieldset> </form:form> </div> </body> </html>為了實現(xiàn)本地化,JSP頁面中的每一段文本都使用了message標(biāo)簽代替html中的label標(biāo)簽,如:
<label for="name"><spring:message code="label.productName" text="default text" />:</label>同時為了調(diào)試,當(dāng)前的語言區(qū)域和accept-language標(biāo)題顯示在頁面的最前面:
Current Locale : ${pageContext.response.locale} <br/> accept-language header: ${header["accept-language"]}ProductDetails.jsp:
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <!DOCTYPE html> <html> <head> <title>Save Product</title> <style type="text/css">@import url("<c:url value="/css/main.css"/>");</style> </head> <body> <div id="global"><h4>The product has been saved.</h4><p><h5>Details:</h5>Product Name: ${product.name}<br/>Description: ${product.description}<br/>Price: $${product.price}</p> </div> </body> </html> View Code?main.css:
#global {text-align: left;border: 1px solid #dedede;background: #efefef;width: 560px;padding: 20px;margin: 100px auto; }form {font:100% verdana;min-width: 500px;max-width: 600px;width: 560px; }form fieldset {border-color: #bdbebf;border-width: 3px;margin: 0; }legend {font-size: 1.3em; }form label { width: 250px;display: block;float: left;text-align: right;padding: 2px; }#buttons {text-align: right; } #errors, li {color: red; } .error {color: red;font-size: 9pt; } View Code6、配置文件
部署描述符(web.xml文件):
<?xml version="1.0" encoding="UTF-8"?> <web-app version="3.1" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"> <servlet><servlet-name>springmvc</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>/WEB-INF/config/springmvc-config.xml</param-value></init-param><load-on-startup>1</load-on-startup> </servlet><servlet-mapping><servlet-name>springmvc</servlet-name><url-pattern>/</url-pattern></servlet-mapping> </web-app>下面給出springmvc-config.xml文件的所有內(nèi)容:
<?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"><context:component-scan base-package="controller" /><mvc:annotation-driven/><mvc:resources mapping="/css/**" location="/css/" /><mvc:resources mapping="/*.html" 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></bean><bean id="localeResolver"class="org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver"></bean> </beans>這里用到了messageSource? bean和localeResolver? bean:
- messageSource? bean:聲明用兩個基準(zhǔn)名設(shè)置了basenames屬性:/WEB-INF/resource/messages和/WEB-INF/resource/labels。
- localeResolver? bean:利用AcceptHeaderLocaleResolver類實現(xiàn)消息的本地化;
Spring MVC通過讀取瀏覽器的accept-language標(biāo)題,來確定瀏覽器接受哪個語言區(qū)域,然后再結(jié)合基準(zhǔn)名查找該語言區(qū)域?qū)?yīng)的屬性文件,如果有,則使用語言區(qū)域。如果沒有,則使用默認(rèn)的語言區(qū)域。
該應(yīng)用支持en、fr、zh三種語言區(qū)域,此外,還有一個默認(rèn)的語言區(qū)域(和en語言區(qū)域一樣),因此屬性文件有四種版本。為了實現(xiàn)本地化,JSP頁面中的每一段文本都使用了message標(biāo)簽代替。具體可以查看ProductForm.jsp文件。
7、屬性文件
將屬性文件分為兩類:
- messages_languageCode_countryCode.property:該類屬性文件用來設(shè)置表單數(shù)據(jù)綁定的錯誤,主要包括類型轉(zhuǎn)換錯誤、以及數(shù)據(jù)合法性驗證錯誤;通過與Spring的表單errors標(biāo)簽結(jié)合,用來顯示錯誤消息;
- labels_languageCode_countryCode:該類屬性文件用來設(shè)置Spring的message標(biāo)簽顯示的內(nèi)容;
messages_en.properties(英語):
productname.required=Please enter a name price.negative=Invalid pricemessages_fr.properties(法語):
productname.required=S'il vous pla�t entrer un nom de produit price.negative=Valeur invalidemessages_zh.properties(漢語):
productname.required=\u4EA7\u54C1\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A price.negative=\u5546\u54C1\u4EF7\u683C\u4E0D\u80FD\u4E3A\u8D1F\u6570messages.properties(默認(rèn)):
productname.required.product.name=Please enter a name price.negative=Invalid pricelabels_en.properties(英語):
label.productName=Product Name label.description=Description label.price=Price button.reset=Reset button.submit=Add Product form.name=Product Form page.productform.title=New Productlabels_fr.properties(法語):
label.productName=Nom du Produit label.description=Description label.price=Prix button.reset=Réinitialiser button.submit=Ajouter Produit form.name=Formulaire de Produit page.productform.title=Formulairelabels_zh.properties(漢語):
label.productName=\u4EA7\u54C1\u540D\u79F0 label.description=\u63CF\u8FF0 label.price=\u4EF7\u683C button.reset=\u91CD\u7F6E button.submit=\u589E\u52A0\u4EA7\u54C1 form.name=\u4EA7\u54C1\u8868\u5355 page.productform.title=\u65B0\u589E\u4EA7\u54C1labels.properties(默認(rèn)):
label.productName=Product Name label.description=Description label.price=Price button.reset=Reset button.submit=Add Product form.name=Product Form page.productform.title=New Product8、測試
為了測試應(yīng)用程序,要修改瀏覽器的accept-language標(biāo)簽。
對于Chrome瀏覽器,打開”設(shè)置“頁面,點擊“顯示高級設(shè)置”,點擊“語言和輸入設(shè)置”,添加并移動語言到列表的頂部。
將應(yīng)用程序部署到tomcat服務(wù)器,訪問以下URL:
http://localhost:8008/i18n/add-product你會看到Product的中文版:
修改accept-language標(biāo)簽,將英語移動到列表的頂部,再次刷新頁面:
修改accept-language標(biāo)簽,將法語移動到列表的頂部,再次刷新頁面:
?
注意:該應(yīng)用的所有文件都采用UTF-8編碼。
參考文章
[1]詳解Java中用于國際化的locale類
[2]java中的編碼問題
[3]Spring MVC學(xué)習(xí)指南
[4]SpringMVC國際化探索
轉(zhuǎn)載于:https://www.cnblogs.com/zyly/p/10873703.html
總結(jié)
以上是生活随笔為你收集整理的Spring MVC -- 国际化的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Python3之多线程学习
- 下一篇: nginx+keepalived互为主主