用Java解決國際化問題
首都經貿大學信息學院 尹海琴
|
01-7-18 上午 09:11:22
|
如果應用系統是面向多種語言的,編程時就不得不設法解決國際化問題,包括操作界面的風格問題、提示和幫助語言的版本問題、界面定制個性化問題等。 |
由于Java語言具有平臺無關、可移植性好等優點,并且提供了強大的類庫,所以Java語言可以輔助我們解決上述問題。Java語言本身采用雙字節字符編
碼,采用大漢字字符集,這就為解決國際化問題提供了很多方便。從設計角度來說,只要把程序中與語言和文化有關的部分分離出來,加上特殊處理,就可以部分解
決國際化問題。在界面風格的定制方面,我們把可以參數化的元素,如字體、顏色等,存儲在數據庫里,以便為用戶提供友好的界面;如果某些部分包含無法參數化
的元素,那么我們可能不得不分別設計,通過有針對性的編碼來解決具體問題。 |
在用Java解決國際化問題的過程中,可能利用到的主要的類都是由java.util包提供的。該類包中相關的類有Locale、
ResourceBundle、ListResourceBundle、PropertyResourceBundle等,其繼承關系如下圖所示。 |
Locale:該類包含對主要地理區域的地域化特征的封裝。其特定對象表示某一特定的地理、政治或文化區域。通過設定Locale,我們可以為特定的國家
或地區提供符合當地文化習慣的字體、符號、圖標和表達格式。例如,我們可以通過獲得特定Locale下的Calendar類的實例,顯示符合特定表達格式
的日期。 |
ResourceBundle:該類是一個抽象類,需要通過靜態方法ResourceBundle.getBundle()指定具體實現類或屬性文件的基
本名稱?;久Q會協同指定的或默認的Locale類,決定具體調用的類或屬性文件的唯一名稱。例如:指定基本類或屬性文件名稱為TestBundle,
而指定的Locale是CHINESE,那么最適合匹配的類名稱為TestBundle_zh_CN.class,而最佳匹配屬性文件名稱為
TestBundle_zh_CN.properties。按照Java
Doc和相關文檔的要求,如果該類或屬性文件沒有找到,系統會查找近似匹配(主文件名依次為TestBundle_zh和TestBundle的類或屬性
文件)。該類提供的getKeys()方法用于獲得所有成員的鍵名,并提供handleGetObject方法獲得指定鍵的對應元素。 |
ListResourceBundle:該類繼承ResourceBundle類,主要是增加了一些便于操作的成分,但還是抽象類。如果希望使用類的方式實現具體的ResourceBundle,一般情況下最好繼承這個類。 |
PropertyResourceBundle:該類也繼承ResourceBundle類,可以實例化。該類的行為特征如同java.util.properties類,可以從輸入流中獲得具體屬性對。 |
如果涉及日期和時間顯示等問題時,可以利用java.text包以及java.util包中的TimeZone、SimpleTimeZone和Calendar等類進行輔助處理。 |
在具體應用時,可以把具體國家或地區特征中可以參數化的部分放在經過特殊命名的屬性文件中,在確定具體的Locale后,通過PropertyResourceBundle類讀取相應的屬性文件,實現國際化特征。 |
使用PropertyResourceBundle類獲得當地版本的國際化信息,部分代碼如下: |
public static final String BASE_PROP_FILE = |
public static final String SUFFIX = |
locale = Locale.getDefault(); |
String propFile = BASE_PROP_FILE + “_” + locale.toString()+ SUFFIX; |
File file = new File(propFile); |
is = new FileInputStream(file); |
rb = new PropertyResourceBundle(is); |
if (rb == null) System.out.println(“No Resource”); |
} catch (IOException ioe) { |
System.out.println(“Error open file named ” + propFile); |
Enumeration e = rb.getKeys(); |
while (e.hasMoreElements()){ |
key = (String)e.nextElement(); |
value = (String)rb.handleGetObject(key); |
System.out.println(“KEY: ” + key + |
DISP_zh_TW.properties文件的具體內容如下: |
等號后面是利用native2ascii程序轉化后的繁體漢字,如果不進行轉化,系統可能顯示亂碼。 |
對于提示語言和幫助文件部分,可以把語言映射放在屬性文件或者ListResourceBundle類的子類中。下面程序是一個Servlet,它通過接受客戶端的選擇,把特定語言和字符版本的信息返回到客戶端。 |
public class ProcessServlet extends HttpServlet |
public static final String DEFAULT_LANGUAGE = “zh”; |
public static final String DEFAULT_COUNTRY = “CN”; |
public void service(HttpServletRequest req, |
HttpServletResponse res) throws IOException, ServletException { |
HttpSession session = req.getSession(true); |
// 從客戶端收到的指定語言和字符的參數應當與Sun公司相關規定一致 |
String lang = req.getParameter |
String country = req.getParameter |
//如果沒有收到參數,就試圖從Session里獲得 |
lang = (String) session.getAttribute |
country = (String) session.getAttribute |
session.setAttribute(“language”, lang); |
session.setAttribute(“country”, country); |
//如果無法從上述手段得到語言和字符信息,就使用默認值 |
country = DEFAULT_COUNTRY |
session.setAttribute(“language”, lang); |
session.setAttribute(“country”, country); |
ResourceBundle bundle = null; |
locale = new Locale(lang, country); |
System.out.println(“No locale with” + |
locale = Locale.getDefault(); |
bundle = ResourceBundle.getBundle( |
} catch( MissingResourceException e) { |
System.out.println( “No resources available for locale ” + locale); |
bundle = ResourceBundle.getBundle |
(“DisplayList”, Locale.US); |
res.setContentType(“text/html”); |
PrintWriter out = res.getWriter(); |
String title = bundle.getString(“title”); |
String welcome =bundle.getString |
String notice = bundle.getString(“notice”); |
out.println(“<title>”+ title + |
out.println(“<body bgcolor=\” |
out.println(“<h3>” + welcome + |
out.println(“<b>” + notice + |
上述Servlet使用的屬性文件(DisplayList_zh_CN. |
注意:該文件直接采用了中文,而不是經過轉化的Unicode編碼,這是由于大多數Web服務器不需要上述轉化。 |
在實際使用中,如果Web服務器支持Servlet 2.3規范(如jakarta-tomcate
4.0),那么上面提到的Servlet應當稍加改變,以作為其他Servlet的處理器使用。另外,如果把ResourceBundle的特定版本存放
在無狀態會話Bean中,就可以在一定程度上提高程序效率。 |
筆者在實際測試中發現了如下問題,其中部分問題得到了解決: |
1.
對于顯示字符出現亂碼的問題,如果是通過屬性文件實現國際化解決方案,那么可能是直接在屬性文件中寫入了非標準ASCII文字。解決方法是利用JDK提供
的工具native2ascii.exe掃描所有屬性文件,用掃描結果覆蓋原有文件內容。如果我們是利用類文件實現轉換方案,那么需要重新編譯相關類文
件,并在編譯時指定編碼集。例如,編譯使用國標碼的類文件,采用的編譯命令如下: |
javac -encoding GB2312 your_java_file |
2.
雖然Sun宣稱,在ResourceBundle類的實例化過程中,該類會查找與指定的基礎類絕對匹配和盡量與指定的Locale屬性相匹配的類。例如:
如果我們指定ResourceBundle基礎類為TestBundle,而Locale中指定使用zh_CN(中國大陸地區簡體中文),那么如果系統找
不到TestBundle_zh_CN,系統應當順次查找TestBundle_zh、TestBundle。但是筆者在系統開發過程中發現,該匹配沒有
產生任何實際效果。 |
筆者的測試平臺是Windows 2000 Server,沒有配置任何Service
Pack,使用的JDK版本是1.3.0版本。筆者試圖通過查看JDK目錄下src.jar中附帶的源碼找到引起問題的原因,但是發現有關的操作被封裝在
sun.misc包中,而src.jar文件沒有提供該包中任何類的源碼。本文把這個問題提出來,希望與有關開發人員一起探討。