Struts應用的國際化

萬維網(World Wide Web)的迅猛發展推動了跨國業務的發展,它成為一種在全世界范圍內發布產品信息、吸引客戶的有效手段。為了使企業Web應用能支持全球客戶,軟件開發者應該開發出支持多國語言、國際化的Web應用。

 

1 本地化與國際化的概念

國際化(簡稱為I18N)指的是軟件設計階段,就應該使軟件具有支持多種語言和地區的功能。這樣,當需要在應用中添加對一種新的語言和國家的支持時,不需要對已有的軟件返工,無需修改應用的程序代碼。

本地化意味著針對不同語言的客戶,開發出不同的軟件版本;國際化意味著同一個軟件可以面向使用各種不同語言的客戶。

如果一個應用支持國際化,它應該具備以下特征:

· 當應用需要支持一種新的語言時,無需修改應用程序代碼。

· 文本、消息和圖片從源程序代碼中抽取出來,存儲在外部。

· 應該根據用戶的語言和地理位置,對與特定文化相關的數據,如日期、時間和貨幣,進行正確的格式化。

· 支持非標準的字符集。

· 可以方便快捷地對應用作出調整,使它適應新的語言和地區。

 

在對一個Web應用進行國際化時,除了應該對網站上的文本、圖片和按鈕進行國際化外,還應該對數字和貨幣等根據不同國家的標準進行相關的格式化,這樣才能保證各個國家的用戶都能順利地讀懂這些數據。

Locale(本地)指的是一個具有相同風俗、文化和語言的區域。如果一個應用沒有事先把I18N作為那前的功能,那么當這個應用需要支持新的Locale時,開發人員必需對嵌入在源代碼中的文本、圖片和消息進行修改,然后重新編譯源代碼。每當這個應用需要支持新的Locale時,就必需重復這些繁瑣的步驟,這種做法顯然大大降低了軟件開發效率。

 

2 Web應用的中文本地化

無論時對Web應用的本地化還是國際化,都會涉及字符編碼轉換問題。當數據流的源與目的地使用不同的字符編碼時,就需要對字符編碼進行正確的轉換。

 

2.1 處理HTTP請求數據編碼

    默認情況下,IE瀏覽器發送請求時采用“ISO-8859-1字符編碼,如果Web應用程序要正確地讀取用戶發送的中文數據,則需要進行編碼轉換。

    一種方法是在處理請求前,先設置HttpServletRequest對象的字符編碼:

              request.setCharacterEncoding(“gb2312”);

    還有一種辦法是對用戶輸入的請求數據進行編碼轉換:

              String clientData = request.getParameter(“clientData”);

              if(clientData != null)

                     clientData = new String(clientData.getBytes(“ISO-8859-1”), “GB2312”);

 

2.2 處理數據庫數據編碼

    如果數據庫系統的字符編碼為“GB2312,那么可以直接讀取數據庫中的中文數據,而無需進行編碼轉換。如果數據庫字符編碼為“ISO-8859-1,那么必需先對來自數據庫的數據進行編碼轉換,然后才能使用。

 

2.3 處理XML配置文件編碼

    如果在XML文件中包含中文,可以將XML文件的字符編碼為“GB2312。這樣,當Java程序加載和解析XML文件時無需再進行編碼轉換。

              <?xml version=’1.0’ encoding=”GB2312”?>

 

2.4 處理響應結果的編碼

    可以通過以下方式來設置響應結果的編碼:

       · Servlet

              response.setContentType(“text/html;charset=GB2312”);

       · JSP

              <%@ page contentType=”text/html;charset=GB2312” %>

       · HTML

              <head>

       <META HTTP-EQUIV=”Content-Type” CONTENT=”text/html; charset=GB2312”>

              </head>

 

3 JavaI18N的支持

Java在其核心庫中提供了支持I18N的類和接口。Struts框架依賴于這些Java I18N組件來實現對I18N的支持,因此,掌握Java I18N組件的使用方法有助于理解Struts應用的國際化機制。

3.1 Locale

    java.util.Locale類時最重要的Java I18N類,在Java語言中,幾乎所有對國際化和本地化的支持都依賴于這個類。

Locale類的實例代表一種特定的語言和地區。如果Java類庫中的某個類在運行時需要根據Locale對象來調整其功能,那么就稱這個類是本地敏感的(Locale-Sensitive)。例如,java.text.DateFormat類就是本地敏感的,因為它需要依照特定的Locale對象來對日期進行相關的格式化。

Locale對象本身病不執行和I18N相關的格式化或解析工作。Locale對象僅僅負責向本地敏感的類提供本地化信息。例如,DateFormat類依據Locale對象來確定日期的格式,然后對日期進行語法分析和格式化。

創建Locale對象時,需要明確地指定其語言和國家代碼。如:

       Locale usLocale = new Locale(“en”, “US”);

       Locale chLocale = new Locale(“ch”, “CH”);

構造方法的第一個參數是語言代碼。語言代碼由兩個小寫字母組成,遵從ISO-639規范。可以從http://www.unicode.org/unicode/onlinedat/languages.html中獲得完整的語言代碼列表。

構造方法的第二個參數是國家代碼,它由兩個大寫字母組成,遵從ISO-3166規范??梢詮?/span>http://www.unicode.org/unicode/onlinedat/countries.html中獲得完整的國家代碼列表。

Locale類提供了幾個靜態常量,他們代表一些常用的Locale實例。例如,如果要獲得Japanese Locale實例,可以使用如下兩種方法之一:

       Locale locale1 = Locale.JAPAN;

       Locale locale2 = new Locale(“ja”, “JP”);

 

3.1.1 Web容器中Locale對象的來源

    Java虛擬機在啟動時會查詢操作系統,為運行環境設置默認的Locale。Java程序可以調用java.util.Locale類的靜態方法getLocale()來獲得默認的Locale

              Locale defaultLocale = Locale.getDefault();

    Web容器在其本地環境中通常會使用以上默認的Locale;而對于特定的中斷用戶,Web容器會從HTTP請求中獲取Locale信息。

 

3.1.2 Web應用中訪問Locale對象

    在創建Locale對象時應該把語言和國家代碼兩個參數傳遞給構造方法。對于Web應用程序,通常不必創建自己的Locale實例,因為Web容器會負責創建所需的Locale實例。在應用程序中,可以調用HttpServletRequest對象的以下兩個方法,來取得包含Web客戶的Locale信息的Locale實例:

              public java.util.Locale getLocale();

              public java.util.Enumeration getLocales();

    著兩個方法都會訪問HTTP請求中的Accept-Language頭信息。getLocale()方法返回客戶優先使用的Locale,而getLocales()方法返回一個Enumeration集合對象,它包含了按優先級降序排列的所有Locale對象。如果客戶沒有配置任何Locale,getLocale()方法將會返回默認的Locale

 

3.1.3 Struts應用中訪問Locale對象

    有序Web服務器并不和客戶瀏覽器保持長期的連接,因此每個發送到Web容器的HTTP請求中都包含了Locale信息。Struts配置文件的<controller>元素的locale屬性指定是否把Locale對象保存在session范圍中,默認值為true,表示會把Locale對象保存在session范圍中。在處理每一個用戶請求時,RequestProcessor類都會調用它的processLocale()方法。

    盡管每次發送的HTTP請求都包含Locale信息,processLocale()方法把Locale對象存儲在session范圍中必需滿足以下條件:

    · Struts配置文件的<controller>元素的locale屬性為true。

    · session范圍內Locale對象還不存在。

    processLocale()方法把Locale對象存儲在session范圍中時,屬性keyGlobals.LOCALE_KEY,這個常量的字符串值為“org.apache.struts.action.LOCALE”。

    如果應用程序允許用戶在同一個會話中改變Locale,那么應該對每一個新的HttpServletRequest調用其getLocale()方法,來判斷用戶是否改變了Locale,如果Locale發生改變,就把新的Locale對象保存在session范圍內。

    Struts應用程序中可以很方便地獲取Locale信息。例如,如果在Action類中訪問Locale信息,可以調用在Struts Action基類中定義的getLocale()方法。

    Action類的getLocale()方法調用RequestUtils.getUserLocale()方法。

    getUserLocale()方法先通過HttpServletRequest參數獲得HttpSession對象,然后再通過HttpSession對象來讀取Locale對象。如果存在HttpSession對象并且HttpSession中存儲了Locale對象,就返回該Locale對象,否則就直接調用HttpServletRequestgetLocale()方法取得Locale對象,并降它返回。

    Web應用程序的其他地方也可以直接調用RequestUtils類的getUserLocale()方法來獲取Locale對象。

 

3.2 ResourceBundle

    java.util.ResourceBundle類提供存放和管理與Locale相關的資源的功能。這些資源包括文本域或按鈕的Label、狀態信息、圖片名、錯誤信息和網頁標題等。

    Struts框架并沒有直接使用Java語言提供的ResourceBundle類。在Struts框架中提供了兩個類:

    · org.apache.struts.util.MessageResources

    · org.apache.struts.util.PropertyMesasgeResources

    這兩個類具有和ResourceBundle相似的功能,其中PropertyMessageResourcesMessageResources類的子類。

 

3.3 MessageFormat類和符合消息。

    JavaResourceBundleStrutsMessageResources類都允許使用靜態和動態的文本。靜態文本指定的是實現就已經具有明確內容的文本。動態文本指的是只有在運行時才能確定內容的文本。

    通常把包含可變數據的消息成為符合消息。符合消息允許在程序運行時把動態數據加入到消息文本中。著能夠減少Resource Bundle中的靜態消息數量,從而減少把靜態消息文本翻譯成其他Locale版本所花費的時間。

    當然,在Resource Bundle中使用符合信息會使文本的翻譯變得更加困難。因為文本包含了直到運行時才知道的替代值,而把包含替代值的消息文本翻譯成不同的語言時,往往要對語言做適當調整。

    Struts框架封裝了MessageFormat類的功能,支持復合消息文本,該功能的實現對于Struts的其他組件是透明的。

 

4 Struts框架對國際化的支持

Struts框架對國際化的支持體現在能夠輸出何用戶Locale相符合的文本何圖片上。當Struts配置文件的<controller>元素的locale屬性為true時,Struts框架把用戶的Locale實例保存在session范圍內,這樣,Struts框架能自動根據這一Lcoale實例來從Resource Bundle中選擇合適的資源文件。當用戶的Locale為英文時,Struts框架就會向用戶返回來自于application_en.properties文件的文本內容:當用戶的Locale為中文時,Struts框架就會向用戶返回來自于appcation_ch.properties文件的文本內容。

4.1 創建StrutsResource Bundle

    對于多應用模塊的Struts應用,可以為每個子應用配置一個或多個Resource Bundle,應用模塊中的Action、ActionForm Bean、JSP頁和客戶化標簽都可以訪問這些Bundle。Struts配置文件中的每個<message-resources>元素定義了一個Resource Bundle。當應用中包含多個Resource Bundle時,它們通過<message-resources>元素的key屬性來區別。

Resource Bundle的持久化消息文本存儲在資源文件中,其擴展名為“.properties”,這一文件中消息的格式為:key=value。

在創建Resource Bundle的資源文件時,可以先提供一個默認的資源文件,默認資源文件應該取名為application.properties。如果應用程序需要支持中文用戶,可以再創建一個包含中文消息的資源文件,文件名為:application_ch_CH.propertiesapplication_ch.properties。

Struts框架處理Locale為中文的用戶請求時,Struts框架首先在WEB-INF/classes/目錄下尋找application_ch_CH.properties文件,如果存在該文件,就從該文件中獲取文本消息,否則再一次尋找application_ch.propertiesapplication.properties文件。

應該總是為Resource Bundle提供默認的資源文件,這樣,當不存在和某個Locale對應的資源文件時,就可以使用默認的資源文件。

應該把Resource Bundle的資源文件放在能被定位并加載的位置。對于Web應用程序,資源文件的存放目錄為WEB-INF/classes目錄。如果在配置Resource Bundle時還給定了包名,那么包名應該和資源文件所在的子目錄對應。

4.2 訪問 Resource Bundle

    Struts應用的每個Resource Bundleorg.apache.struts.util.MessageResources類(實際上是其子類PropertyMessageResources)的一個實例對應。MessageResources對象中存放了來自資源文件的文本。當應用程序初始化時,這些MessageResources實例被存儲在ServletContext中(即application范圍內),因此任何一個Web組件都可以訪問它們。一個MessageResources對象可以包含多種本地化版本的資源文件的數據。

    Struts應用、子應用模塊、Resource Bundle和資源文件之間存在以下關系:

    · 一個Struts應用可以有多個子應用模塊,必須有且只有一個默認子應用模塊。

    · 一個子應用模塊可以有多個Resource Bundle,必須有且只有一個默認的Resource Bundle。

    · 一個Resource Bundle可以有多個資源文件,必須有且只有一個默認資源文件。

 

    下面介紹在Struts應用中訪問Resource Bundle的途徑。

1.  通過編程來訪問Resource Bundle

Action積累中定義了getResources(request)方法,它可以返回默認的Message Resources對象,代表當前應用模塊使用的默認Resource Bundle。如果要活得特定的MessageResources對象,可以調用Action基類的getResources(request, key)方法,其中參數keyStruts配置文件中的<message-resources>元素的key屬性對應。得到了MessageResources對象后,就可以通過它的方法來訪問消息文本。

Org.apache.struts.util.MessageResourcesgetMessage()方法有好幾種重載形式,下面列出常用的幾種:

· 根據參數置頂的Locale檢索對應的資源文件,然后返回和參數key對應的消息文本:

getMessage(java.util.Locale locale, java.lang.String key)

· 根據參數指定的Locale檢索對應的資源文件,然后返回和參數key對應的消息文本,args參數用于替換復合消息文本中的參數:

getMessage(java.util.Locale locale, java.lang.String key, java.lang.Object[] args)

· 根據默認的Locale檢索對應的資源文件,然后返回和參數key對應的消息文本:

getMessaeg(java.lang.String key)

2.  使用和Resource Bundle綁定的Struts組件

Struts框架中的許多內在組件和Resource Bundle是綁定在一起的,如:

· ActionMessage類和<html:errors>標簽。

每個ActionMessage實例代表Resource Bundle中的一條消息。在調用ActionMessage的構造方法時,需要傳遞消息key

對于復合消息,在創建ActionMessage對象時,可以調用帶兩個參數的構造方法:ActionMessage(java.lang.String key, java.lang.Object[] values),values參數用戶替換復合消息中的參數。

JSP頁面中,使用<html:errors>標簽,就能讀取并顯示ActionErrors集合中所有ActionMessage對象包含的消息文本。

· Struts Bean標簽庫的<bean:message>標簽。

        Struts框架包含了一些可以訪問應用的消息資源的客戶化標簽,其中使用最頻繁的一個標簽為<bean:message>標簽。<bean:message>標簽從應用的Resource Bundle中獲取消息字符串。

        <bean:message>有一個bundle屬性,用于指定被訪問的Resource Bundle,它和<message-resources>元素的key屬性匹配。如果沒有設置bundle屬性,將訪問默認的Resource Bundle

· Validator驗證框架中訪問Resource Bundle。

· 在聲明型異常處理中訪問Resource Bundle。

 

5 異常處理的國際化

在處理異常時,也應該考慮對I18N的支持。除非已經對應用拋出的異常消息做本地化,否則不應該直接向終端用戶展示原始的異常消息。不懂Java語言的終端用戶很難理解從Java虛擬機堆棧中拋出的Java異常消息,如果這些異常消息使用的不是用戶的本地語言,那就更加會讓用戶困惑不解。

應該先把異常捕獲,對異常消息進行本地化后,再把它展示給用戶。StrutsResource BundleActionMessage類可以完成這一功能。直接向終端用戶顯示Java異常絕對是不可取的。即使發生了無法恢復的系統錯誤,也應該向用戶顯示本地化的系統錯誤頁。

 

6 小結

本文檔介紹了軟件的本地化與國際化的概念,然后消息介紹了對Struts應用實現國際化的原理和方法。與國際化密切相關的兩個組件是LocaleResource Bundle

· Locale:包含了用戶的本地化信息,如語言和國家。

· Resource Bundle:包含了多個消息資源文件,每個消息資源文件存放和一種Locale相對應的本地化消息文本。

Struts框架的初始化時,把Resource Bundle(即MessageResources對象)存儲在application范圍內;在響應用戶請求時,把包裝用戶Locale信息的Locale實例存儲在session范圍內。Struts框架能自動根據這一Locale實例,從Resource Bundle中檢索相應的資源文件,再從資源文件中讀取本地化的消息文本。

Struts應用實現國際化應該遵循以下原則:

· 盡量不在Servlet中使用含非英文字符的常量字符串。

· 對于JSP文件,應該對page指令中的charset屬性進行相應的設置。

· 不要在JSP文件中直接包含本地化的消息資源,兒應該把消息資源存放在Resource Bundle的資源文件中。

· 不比在每個JSPServlet中設置HTTP請求的字符編碼,可以在Servlet過濾器中設置編碼:

   HttpServletRequest.setCharaterEncoding(String encoding);

· 盡量使用“UTF-8”作為HTTP請求和響應的字符編碼,而不是“GBK”或“GB2312”。

· 充分考慮底層數據庫所使用的編碼,它可能會給應用程序的移植帶來麻煩。





                                       2005年06月01日 7:33 PM