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 Java對I18N的支持
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范圍中時,屬性key為Globals.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對象,否則就直接調用HttpServletRequest的getLocale()方法取得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相似的功能,其中PropertyMessageResources是MessageResources類的子類。
3.3 MessageFormat類和符合消息。
Java的ResourceBundle和Struts的MessageResources類都允許使用靜態和動態的文本。靜態文本指定的是實現就已經具有明確內容的文本。動態文本指的是只有在運行時才能確定內容的文本。
通常把包含可變數據的消息成為符合消息。符合消息允許在程序運行時把動態數據加入到消息文本中。著能夠減少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 創建Struts的Resource 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.properties或application_ch.properties。
當Struts框架處理Locale為中文的用戶請求時,Struts框架首先在WEB-INF/classes/目錄下尋找application_ch_CH.properties文件,如果存在該文件,就從該文件中獲取文本消息,否則再一次尋找application_ch.properties和application.properties文件。
應該總是為Resource Bundle提供默認的資源文件,這樣,當不存在和某個Locale對應的資源文件時,就可以使用默認的資源文件。
應該把Resource Bundle的資源文件放在能被定位并加載的位置。對于Web應用程序,資源文件的存放目錄為WEB-INF/classes目錄。如果在配置Resource Bundle時還給定了包名,那么包名應該和資源文件所在的子目錄對應。
4.2 訪問 Resource Bundle
Struts應用的每個Resource Bundle和org.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)方法,其中參數key和Struts配置文件中的<message-resources>元素的key屬性對應。得到了MessageResources對象后,就可以通過它的方法來訪問消息文本。
Org.apache.struts.util.MessageResources的getMessage()方法有好幾種重載形式,下面列出常用的幾種:
·
根據參數置頂的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異常消息,如果這些異常消息使用的不是用戶的本地語言,那就更加會讓用戶困惑不解。
應該先把異常捕獲,對異常消息進行本地化后,再把它展示給用戶。Struts的Resource
Bundle和ActionMessage類可以完成這一功能。直接向終端用戶顯示Java異常絕對是不可取的。即使發生了無法恢復的系統錯誤,也應該向用戶顯示本地化的系統錯誤頁。
6 小結
本文檔介紹了軟件的本地化與國際化的概念,然后消息介紹了對Struts應用實現國際化的原理和方法。與國際化密切相關的兩個組件是Locale和Resource
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的資源文件中。
·
不比在每個JSP或Servlet中設置HTTP請求的字符編碼,可以在Servlet過濾器中設置編碼:
HttpServletRequest.setCharaterEncoding(String encoding);
·
盡量使用“UTF-8”作為HTTP請求和響應的字符編碼,而不是“GBK”或“GB2312”。
·
充分考慮底層數據庫所使用的編碼,它可能會給應用程序的移植帶來麻煩。
2005年06月01日 7:33 PM