Spring MVC -- 國際化

在全球化的今天,如今比過去更須要編寫能夠在不一樣語言國家和地區部署的應用程序。在這方面,須要瞭解兩個術語:css

  • 國際化:經常縮寫爲i18n,由於其單詞internationalization以i開始,以n結尾,在它們中間有18個字母。國際化是開發支持多語言和數據格式的應用程序的技術,它使得頁面可以根據訪問者的語言或國家來呈現不一樣的翻譯版本。
  • 本地化:經常縮寫爲l10n,這是將國際化應用程序改爲支持特定語言區域(locale)的技術。語言區域是指一個特定的地理、政治或者文化區域,要考慮到語言區域的一個操做,就稱做區分語言區域的操做。例如,顯示日期就是一個區分語言區域的操做,由於日期必須以用戶所在的國家或者地區使用的格式顯示。2016年11月15,在美國顯示爲11/15/2016,可是在澳大利亞則顯示爲15/11/2016。

Java謹記國家互的需求,爲字符和字符串提供了Unicode支持。所以,用Java編寫國際化的應用程序是一件很容易的事情。國際化應用程序的具體方式取決於有多少靜態數據須要以不一樣的語言顯示出來。這裏有兩種方法:html

  • 若是大量數據時靜態的,就要針對每個語言區域單首創建一個資源版本。這種方法通常適用於帶有大量靜態HTML頁面的Web應用程序。這個比較簡單,本篇博客不作討論。
  • 若是須要國際化的靜態數據有限,就能夠將文本元素,如HTML標籤內容和錯誤消息隔離爲文本文件。每一個文本文件中都保留着一個語言區域的全部文本元素譯文。隨後,應用程序會自動獲取每個元素。這將是本篇博客的重點。

本篇博客首先解釋什麼是語言區域,接着講解國際化應用程序技術,最後介紹一個Spring MVC範例。java

一 語言區域

一、java.util.Locale類

每個Locale對象都表明了一個特定的地理、政治和文化地區。在操做 Date, Calendar等表示日期/時間的對象時,常常會用到;由於不一樣的區域,時間表示方式都不一樣。一個Locale對象包含3個主要參數:language、country和variant:web

  • language是最重要的部分;可是語言自己有時並不足以區分一個語言區域。例如,說英語的國家不少,如美國和英國。可是在美國說的英語,與在英國用的英語並不是如出一轍。所以,須要指定語言國家。
  • variant是一個特定於供應商或者特定於瀏覽器的代號。例如,用WIN表示Windows,用MAC表示Macintosh,用POSIX表示POSIX。兩個variant之間用一個下劃線隔開,並將最重要的部分放在最前面。例如,傳統西班牙語,用language、country和variant參數構造一個locale分別是es、ES、Traditional_WIN。

Locale的構造函數共有3個:spring

public Locale(String language)
public Locale(String language, String country)
public Locale(String language, String country, String variant)

參數language是一個有效的ISO語言碼。下表顯示了ISO 639語言碼範例:apache

代碼 語言
de 德語
el 希臘語
en 英語
es 西班牙語
fr 法語
hi 印地語
it 意大利語
ja 日語
nl 荷蘭語
pt 葡萄牙語
ru 俄語
zh 漢語

參數country是一個有效的ISO國家碼,由兩個字母組成,ISO 3166中指定爲大寫字母。下表展現了ISO 3166國家碼範例:數組

國家 代碼
澳大利亞 AU
巴西 BR
加拿大 CA
中國 CN
埃及 EG
法國 FR
德語 DE
印度 IN
墨西哥 MX
瑞士 CH
英國 GB
美國 US

Locale類支持很是多的國家和地區。咱們能夠經過如下方法,查看Locale支持的所有區域:瀏覽器

 Locale[] ls = Locale.getAvailableLocales();
 
    System.out.print("All Locales: ");
    for(Locale locale:ls) {
      System.out.printf(locale+", ");
    }

輸出結果以下:spring-mvc

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, 

下面選擇其中的一個進行說明,如何利用它們來建立Locale對象:例如,輸出「ja_JP」。其中,ja表明「語言」,這裏指日語;「JP」表明國家,這裏指日本。tomcat

咱們能夠經過以下方法,建立一個表示中國所用漢語的Locale對象,能夠像下面這樣編寫:

Locale local = new Locale("zh", "CN");

此外,Locale類提供了static final域(編譯期常量),用來返回特定國家或語言的語言區域:

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

所以,也能夠經過調用其static於來構造Locale對象,下面的Locale對象是對應 「中國(簡體中文)」的

Locale locale = Locale.SIMPLIFIED_CHINESE

此外,靜態的getDefault()方法會返回用戶計算機的語言區域:

Locale locale = Locale.getDefault()

二、例子

下面經過示例演示在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的建立方法
    testDiffDateLocales();
 
    // 顯示全部的Locales
    testAllLocales();
  }
  
  /**
   * 2種不一樣的Locale的建立方法
   */
  private static void testDiffDateLocales() {
    // date爲2013-09-19 14:22:30
    Date date = new Date(113, 8, 19, 14, 22, 30);
 
    System.out.println(date);
    // 建立「簡體中文」的Locale
    Locale localeCN = Locale.SIMPLIFIED_CHINESE;
    // 建立「英文/美國」的Locale
    Locale localeUS = new Locale("en", "US");
 
    // 獲取「簡體中文」對應的date字符串
    String cn = DateFormat.getDateInstance(DateFormat.MEDIUM, localeCN).format(date);
    // 獲取「英文/美國」對應的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應用程序

 國際化和本地化應用程序時,須要具有如下條件:

  • 將文本元素隔離成屬性文件;
  • 選擇和讀取正確的屬性文件;

下面詳細介紹這兩個步驟,並進行簡單的示範。

一、將文本元素隔離成屬性文件

國際化的應用程序是將每個語言區域的文本元素都單獨保存在一個獨立的屬性文件中。每一個文件中都包含key/value對,而且每一個key都惟一標示一個特定語言區域的對象。key始終是字符串,value則能夠是字符串,也能夠是其餘任意類型的對象。例如,爲了支持英語、德語、以及漢語,就要有3個屬性文件,它們都有相同的key。

如下是英語版的屬性文件。注意:它有greetings和farewell兩個key:

greetings=Hello
farewell=Goodbye

德語版的屬性文件以下:

greetings=Hallo
farewell=Auf Wiedersehen

漢語版的屬性文件以下:

greetings=\u4f60\u597d
farewell=\u518d\u89c1

若是你是中文用戶,你可使用任何中文文本編輯器並寫入漢字字符。完成後,將文件轉換成Unicode。

如今,要學習java.util.ResourceBundle類。它能使你輕鬆的選擇和讀取特定於用戶語言區域的屬性,以及查找值。ResourceBundle是一個抽象類,可是它提供了靜態的getBundle()方法,返回一個具體子類的實例。

ResourceBoundle有一個基準名,它能夠是任意名稱。可是,爲了讓ResourceBundle正確的選擇屬性文件,這個文件名中最好必須包含基準名ResourceBundle,後面再接下劃線、語言碼,還能夠選擇再加一條下劃線和國家碼。屬性文件名的格式以下所示:

basename_lanuageCode_countryCode

例如,假設基準名爲MyResources,而且定義瞭如下3個語言區域:

  • DE-de;
  • CN-zh;
  • US-en;

那麼就會獲得下面這3個屬性文件:

  • MyResources_de_DE.properties;
  • MyResources_zh_CN.properties;
  • MyResources_en_US.properties;

二、選擇和讀取正確的屬性文件

如前所述。ResourceBundle是一個抽象類。儘管如此,仍是能夠經過調用它的靜態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在相應屬性文件中的值。

若是沒有找到合適的屬性文件,ResourceBundle對象就會返回到默認的屬性文件。默認屬性文件的名稱爲基準名加一個擴展名properties。在這個例子中,默認文件就是MyResource.properties。若是沒有找到默認文件,將拋出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 類做爲的實現類。另外一個實現類是ResourceBundleMessageSource,可是它是不能從新加載的,這意味着,若是在任意屬性文件中修改了某一個屬性key或者value,而且正在使用ResourceBundleMessageSource,那麼要使修改生效,就必須先重啓JVM。

這兩個實現之間的另外一個區別是:使用ReloadableResourceBundleMessageSource時,是在應用程序目錄下搜索這些屬性文件,而使用ResourceBundleMessageSource時,屬性文件則必須放在類路徑下,即/WEB-INF/class目錄下。

還要注意,若是隻有一組屬性文件,則能夠用basename屬性替代basenames,像下面這樣:

    <bean id="messageSource"
        class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
        <property name="basename" value="/WEB-INF/resource/messages" />
    </bean>

三、告訴Spring MVC使用哪一個語言區域

爲用戶選擇語言區域時,最經常使用的方法或許是讀取用戶瀏覽器的accept-language標題值。accept-language標題值提供了關於偏好哪一種語言的信息。

選擇語言區域的其它方法還包括讀取某個session屬性或者cookie。

在Spring MVC中選擇語言區域,可使用語言區域解析器bean。它有幾個實現:

  • AcceptHeaderLocaleResolver;
  • CookieLocaleResolver;
  • SessionLocaleResolver;
  • FixedLocaleResolver.;

全部這些實現都是org.springframework.web.server.i18n包的組成部分。AccpetHeaderLocaleResolver或許是其中最容易使用的一個。若是選擇使用這個語言區域解析器,Spring MVC將會讀取瀏覽器的accept-language標題,來肯定瀏覽器要接受哪一個語言區域。若是瀏覽器的某個語言區域與Spring MVC應用程序支持的某個語言區域匹配,就會使用這個語言區域。若是沒有找到匹配的語言區域,則使用默認的語言區域。

下面是使用AccpetHeaderLocaleResolver的localeResovler bean定義:

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

四、使用message標籤

在Spring MVC中顯示本地化消息最容易方法是使用Spring的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 用於保存消息的有界變量

三 範例

下面咱們將會建立一個i18n應用程序,該應用展現了用localeResovler bean將JSP頁面中的消息本地化的方法。

一、目錄結構

i18n應用程序目錄結構以下圖:

二、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;
    }
}

三、Validator

該應用包含一個名爲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 {

    @Override
    public boolean supports(Class<?> klass) {
        //支持Product類?
        return Product.class.isAssignableFrom(klass);
    }

    //將目標對象target的錯誤註冊到errors對象中
    @Override
    public void validate(Object target, Errors errors) {
        Product product = (Product) target;
        
      //若是目標對象的name屬性爲null,或者爲""字符串,則將錯誤註冊到errors對象
        ValidationUtils.rejectIfEmpty(errors, "name", "productname.required");
        
        BigDecimal price = product.getPrice();
        //若是價格爲負數 則將錯誤註冊到errors對象中
        if (price != null && price.compareTo(BigDecimal.ZERO) < 0) {
            errors.rejectValue("price", "price.negative");
        }
    }
}

ProductValidator驗證器是一個很是簡單的驗證器。它的validate()方法會檢驗Product是否有名稱,而且價格是否不爲負數。

四、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;

@Controller

public 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) {
        //建立一個ProductValidator,並調用其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()方法,有三個參數:

  • 第一個參數product,使用了註解@ModelAttribute,該對象的各個屬性被用來接受表單的各個字段信息,而且將"product"屬性添加到Model對象中;
  • 第二個參數bindingResult中設置了Spring全部的綁定錯誤(主要是類型轉換問題,例如將表單String轉換爲BigDecimal 類型);
  • 第三個參數是Model。

注意:BindingResult接口是Errors接口的子類,在請求處理方法的簽名中使用了BindingResult參數,就是告訴Spring關於表單對象數據校驗的錯誤將由咱們本身來處理,不然Spring會直接拋出異常。

該方法建立一個ProductValidator,並調用其validate()方法校驗Product對象,並將驗證錯誤填入bindingResult中。

ProductValidator productValidator = new ProductValidator();
productValidator.validate(product, bindingResult);

爲了檢驗該驗證器是否生成錯誤消息,須要在BindingResult中調用hasErrors()方法:

 if (bindingResult.hasErrors()) {
            FieldError fieldError = bindingResult.getFieldError();
            logger.debug("Code:" + fieldError.getCode() + ", field:"
                    + fieldError.getField());
            return "ProductForm";
        }

若是存在表單綁定錯誤或者是輸入驗證錯誤,將會打印出錯誤相關的字段,並重定位到ProductForm.jsp頁面。

若是表單輸入的數據均合法,則會重定位到ProductDetails.jsp頁面。

五、視圖

i18n應用包含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>

爲了實現本地化,JSP頁面中的每一段文本都使用了message標籤代替html中的label標籤,如:

 <label for="name"><spring:message code="label.productName" text="default text" />:</label>

同時爲了調試,當前的語言區域和accept-language標題顯示在頁面的最前面:

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 Code

六、配置文件

部署描述符(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文件的全部內容:

<?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/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd     
        http://www.springframework.org/schema/context
        http://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:聲明用兩個基準名設置了basenames屬性:/WEB-INF/resource/messages和/WEB-INF/resource/labels。
  • localeResolver  bean:利用AcceptHeaderLocaleResolver類實現消息的本地化;

Spring MVC經過讀取瀏覽器的accept-language標題,來肯定瀏覽器接受哪一個語言區域,而後再結合基準名查找該語言區域對應的屬性文件,若是有,則使用語言區域。若是沒有,則使用默認的語言區域。

該應用支持en、fr、zh三種語言區域,此外,還有一個默認的語言區域(和en語言區域同樣),所以屬性文件有四種版本。爲了實現本地化,JSP頁面中的每一段文本都使用了message標籤代替。具體能夠查看ProductForm.jsp文件。

七、屬性文件

將屬性文件分爲兩類:

  • messages_languageCode_countryCode.property:該類屬性文件用來設置表單數據綁定的錯誤,主要包括類型轉換錯誤、以及數據合法性驗證錯誤;經過與Spring的表單errors標籤結合,用來顯示錯誤消息;
  • labels_languageCode_countryCode:該類屬性文件用來設置Spring的message標籤顯示的內容;

messages_en.properties(英語):

productname.required=Please enter a name
price.negative=Invalid price

messages_fr.properties(法語):

productname.required=S'il vous pla�t entrer un nom de produit
price.negative=Valeur invalide

messages_zh.properties(漢語):

productname.required=\u4EA7\u54C1\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A
price.negative=\u5546\u54C1\u4EF7\u683C\u4E0D\u80FD\u4E3A\u8D1F\u6570

messages.properties(默認):

productname.required.product.name=Please enter a name
price.negative=Invalid price

labels_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 Product

labels_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=Formulaire

labels_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\u54C1

labels.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 Product

八、測試

爲了測試應用程序,要修改瀏覽器的accept-language標籤。

對於Chrome瀏覽器,打開」設置「頁面,點擊「顯示高級設置」,點擊「語言和輸入設置」,添加並移動語言到列表的頂部。

將應用程序部署到tomcat服務器,訪問如下URL:

http://localhost:8008/i18n/add-product

你會看到Product的中文版:

修改accept-language標籤,將英語移動到列表的頂部,再次刷新頁面:

修改accept-language標籤,將法語移動到列表的頂部,再次刷新頁面:

 

注意:該應用的全部文件都採用UTF-8編碼。

參考文章

[1]詳解Java中用於國際化的locale類

[2]java中的編碼問題

[3]Spring MVC學習指南

[4]SpringMVC國際化探索

相關文章
相關標籤/搜索