FORTUNE

          THE WAY TO THE MASTER...
          posts - 49, comments - 18, trackbacks - 0, articles - 1
            BlogJava :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

          Java國際化——資源包的使用

          Posted on 2006-02-27 14:51 fortune 閱讀(1197) 評論(0)  編輯  收藏 所屬分類: java技術(shù)

          資源包


          在編寫應(yīng)用程序的時(shí)候,需要面對的一個(gè)問題是如何來處理與locale相關(guān)的一些信息。比如,頁面上的一些靜態(tài)文本就希望能夠以用戶習(xí)慣的語言顯示。最原始的做法是將這些信息硬編碼到程序中(可能是一大串判斷語句),但是這樣就將程序代碼和易變的locale信息捆綁在一起,以后如果需要修改locale信息或者添加其它的locale信息,你就不得不重新修改代碼。而資源包可以幫助你解決這個(gè)問題,它通過將可變的locale信息放入資源包中來達(dá)到兩者分離的目的。應(yīng)用程序可以自動(dòng)地通過當(dāng)前的locale設(shè)置到相應(yīng)的資源包中取得所要的信息。資源包的概念類似于Windows編程人員使用的資源文件(rc文件)。

          一般來說,資源包需要完成兩個(gè)功能:和具體的locale進(jìn)行綁定以及讀取locale相關(guān)信息。

          ResourceBundle類

          你可以把資源包看作為一個(gè)由許多成員(子類)組成的大家庭,其中每個(gè)成員關(guān)聯(lián)到不同的locale對象,那它是如何完成關(guān)聯(lián)功能的呢?

          資源包中的每個(gè)成員共享一個(gè)被稱作基名(base name)的名稱,然后在此基礎(chǔ)上根據(jù)一定的命名規(guī)范進(jìn)行擴(kuò)展。下面就列出了一些成員的名稱:
              LabelResources
                   LabelResources_de
                   LabelResources_de_CH
                   LabelResources_de_CH_UNIX
          可見這些子類依據(jù)這樣的命名規(guī)范:baseName_language_country_variant,其中l(wèi)anguage等幾個(gè)變量就是你在構(gòu)造Locale類時(shí)所使用的。而資源包正是通過這個(gè)符合命名規(guī)范的名稱來和locale進(jìn)行關(guān)聯(lián)的,比如LabelResource_de_CH就對應(yīng)于由德語(de)和瑞士(CH)組成的locale對象。

          當(dāng)你的應(yīng)用程序需要查找特定locale對象關(guān)聯(lián)的資源包時(shí),它可以調(diào)用ResourceBundle的getBundle方法,并將locale對象作為參數(shù)傳入。
          1. Locale currentLocale = new Locale("de""CH""UNIX");
          2. ResourceBundle myResources =
          3.       ResourceBundle.getBundle("LabelResources", currentLocale);

          如果該locale對象匹配的資源包子類找不到,getBundle將試著查找最匹配的一個(gè)子類。具體的查找策略是這樣的:getBundle使用基名,locale對象和缺省的locale來生成一個(gè)候選資源包名稱序列。如果特定locale對象的語言代碼、國家代碼和可選變量都是空值,則基名是唯一的候選資源包名稱。否則的話,具體locale對象(language1,country1和variant1)和缺省locale(language2,country2和variant2)將產(chǎn)生如下的序列:

          • baseName + "_" + language1 + "_" + country1 + "_" + variant1
          • baseName + "_" + language1 + "_" + country1 
          • baseName + "_" + language1 
          • baseName + "_" + language2 + "_" + country2 + "_" + variant2 
          • baseName + "_" + language2 + "_" + country2 
          • baseName + "_" + language2 
          • baseName 

          然后,getBundle方法按照產(chǎn)生的序列依次查找匹配的資源包子類并對結(jié)果子類初始化。首先,它將尋找類名匹配候選資源包名稱的類,如果找到將創(chuàng)建該類的一個(gè)實(shí)例,我們稱之為結(jié)果資源包。否則,getBundle方法將尋找對應(yīng)的資源文件,它通過候選資源包名稱來獲得資源文件的完整路徑(將其中的“.”替換為“/”,并加上“.properties”后綴),如果找到匹配文件,getBundle方法將利用該資源文件來創(chuàng)建一個(gè)PropertyResourceBundle實(shí)例,也就是最終的結(jié)果資源包。與此同時(shí),getBundle方法會(huì)將這些資源包實(shí)例緩存起來供以后使用。

          如果沒有找到結(jié)果資源包,該方法將拋出MissingResourceException異常。所以為了防止異常的拋出,一般來說都需要至少實(shí)現(xiàn)一個(gè)基名資源包子類。

          注意:基名參數(shù)必須是一個(gè)完整的類名稱(比如LabelResources,resource.LabelResources等),就相當(dāng)于你引用一個(gè)類時(shí)需要指定完整的類路徑。但是,為了和以前的版本保持兼容,在使用PropertyResourceBundles時(shí)也允許使用“/”來代替“.”表示路徑。

          比如你有以下這些資源類和資源文件:MyResources.class, MyResources_fr_CH.properties, MyResources_fr_CH.class, MyResources_fr.properties, MyResources_en.properties, MyResources_es_ES.class。你利用以下的locale設(shè)置來調(diào)用getBundle方法,你將會(huì)得到不同的結(jié)果資源包(假設(shè)缺省locale為Locale(“en”, “UK”)),請參考表13.4。
                 表13.4 locale設(shè)置與結(jié)果資源包
          locale設(shè)置        結(jié)果資源包
          Locale("fr", "CH")    MyResources_fr_CH.class
          Locale("fr", "FR")        MyResources_fr.properties
          Locale("de", "DE")        MyResources_en.properties
          Locale("en", "US")        MyResources_en.properties
          Locale("es", "ES")        MyResources_es_ES.class

          創(chuàng)建了具體的資源包子類實(shí)例以后,就需要獲得具體的信息。信息在資源包中是以鍵值對的方式存儲的,表13.5列出的是LabelResources.properties文件的內(nèi)容。

          表13.5 LabelResources.properties
          1. # This is LabelResources.properties file
          2. greetings = 您好!
          3. farewell = 再見。
          4. inquiry = 您好嗎?

          其中等號左邊的字符串表示主鍵,它們是唯一的。為了獲得主鍵對應(yīng)的值,你可以調(diào)用ResourceBundle類的getString方法,并將主鍵作為參數(shù)。此外,文件中以“#”號開頭的行表示注釋行。

          ListResourceBundle和PropertyResourceBundle子類

          抽象類ResourceBundle具有兩個(gè)子類:ListResourceBundle和PropertyResourceBundle,它們表示資源包子類兩種不同的實(shí)現(xiàn)方式。

          PropertyResourceBundle是和資源文件配對使用的,一個(gè)屬性文件就是一個(gè)普通的文本文件,你只需要為不同的locale設(shè)置編寫不同名稱的資源文件。但是,在資源文件中只能包含字符串,如果需要存儲其它類型對象,你可以使用ListResourceBundle。

          ListResourceBundle是將鍵值對信息保存在類中的列表中,而且你必須實(shí)現(xiàn)ListResourceBundle的具體子類。

          如果ListResourceBundle和PropertyResourceBundle不能夠滿足你的需要,你可以實(shí)現(xiàn)自己的ResourceBundle子類,你的子類必須覆蓋兩個(gè)方法:handleGetObject和getKeys。

          使用資源文件

          使用資源包最簡單的方法就是利用資源文件,利用資源文件一般需要以下幾個(gè)步驟:
          1、創(chuàng)建一個(gè)缺省的資源文件
          為了防止找不到資源文件,你最好實(shí)現(xiàn)一個(gè)缺省的資源文件,該文件的名稱為資源包的基名加上.properties后綴。
          2、創(chuàng)建所需的資源文件
          為你準(zhǔn)備支持的locale設(shè)置編寫對應(yīng)的資源文件。
          3、設(shè)置locale
          你必須在程序中的某個(gè)地方提供locale的設(shè)置或者切換功能,或者將其放入配置文件中。
          4、根據(jù)locale設(shè)置創(chuàng)建資源包
          ResourceBundle resource =
                  ResourceBundle.getBundle("LabelBundle",currentLocale);
          5、通過資源包獲取locale相關(guān)信息
          String value = resource.getString("welcome");

          注意:在使用基名的時(shí)候,特別要注意給出完整的類名(或者路徑名),比如你的應(yīng)用程序所在的類包為org.javaresearch.j2seimproved.i18n,而你的資源文件在你的應(yīng)用程序下的resource子目錄中,那你的基名就應(yīng)該是org.javaresearch.j2seimproved.i18n.resource.LabelBundleBundle而不是resource.LabelBundleBundle。


          使用ListResourceBundle

          使用ListResourceBundle和使用資源文件的步驟基本上一樣,只不過你需要用ListResourceBundle子類來替換相應(yīng)的資源文件。比如你的應(yīng)用程序的基名是LabelBundle,而且準(zhǔn)備支持Locale("en","US")和Locale("zh","CN"),那你需要提供以下幾個(gè)Java文件,注意類名和locale的對應(yīng)關(guān)系。
          LabelBundle_en_US.java
          LabelBundle_zh_CN.java
          LabelBundle.java(缺省類)

          代碼13.3列出的是LabelBundle_zh_CN.java的源代碼,相對于資源文件中“key = value”的寫法,在此文件中你首先利用鍵值對來初始化一個(gè)二維數(shù)組,并在getContents方法中返回該數(shù)組。
                
          代碼13.3:LabelBundle_zh_CN.java
          1. package org.javaresearch.j2seimproved.i18n;import 
          2. java.util.java/util/ListResourceBundle.java.html" target="_blank">ListResourceBundle;
          3. public class LabelBundle_zh_CN extends ListResourceBundle {   
          4.   public java/lang/Object.java.html" target="_blank">Object[][] getContents() {     
          5.     return contents;   
          6.   }   
          7.   private java/lang/Object.java.html" target="_blank">Object[][] contents = {      
          8.     {"title""稱謂"},      
          9.     {"surname""姓"},      
          10.     {"firstname""名"},   
          11.   };
          12. }


          創(chuàng)建完資源類以后,同樣需要設(shè)置locale以及根據(jù)locale來創(chuàng)建資源包。在通過資源包獲取具體值的時(shí)候,你不能再使用getString方法,而應(yīng)該調(diào)用getObject方法,而且由于getObject方法返回一個(gè)Object對象,你還需要進(jìn)行正確的類型轉(zhuǎn)換。其實(shí),為了你的程序通用性,我們建議在使用資源文件的時(shí)候你也應(yīng)該調(diào)用getObject方法,而不是getString方法。
          1.     java/lang/String.java.html" target="_blank">String title = (java/lang/String.java.html" target="_blank">String)resource.getObject("title");

          關(guān)于ListResourceBundle的詳細(xì)使用,可以參考本書所附代碼中國際化一節(jié)的ListResourceBundleSample.java程序

          MessageFormat類

          上面我們講到利用資源文件來分離代碼和可變的信息。但是在實(shí)際過程中,有些信息并不能夠完全事先定義好,其中可能會(huì)用到運(yùn)行時(shí)的一些結(jié)果,最典型例子的就是錯(cuò)誤提示代碼,比如提示某個(gè)輸入必須在一定范圍內(nèi)。利用上面所講的資源文件并不能夠很好地解決這個(gè)問題,所以Java中引入了MessageFormat類。

          MessageFormat提供一種語言無關(guān)的方式來組裝消息,它允許你在運(yùn)行時(shí)刻用指定的參數(shù)來替換掉消息字符串中的一部分。你可以為MessageFormat定義一個(gè)模式,在其中你可以用占位符來表示變化的部分,比如你有這樣一句話:

          您好,peachpi!歡迎來到Java研究組織網(wǎng)站!當(dāng)前時(shí)間是:2003-8-1 16:43:12。

          其中斜體帶下劃線的部分為可變化的,你需要根據(jù)當(dāng)前時(shí)間和不同的登錄用戶來決定最終的顯示。我們用占位符來表示這些變化的部分,可以得到下面這個(gè)模式:

          您好,{0}!歡迎來到Java研究組織網(wǎng)站!當(dāng)前時(shí)間是:{1,date} {1,time}。

          占位符的格式為{ ArgumentIndex , FormatType , FormatStyle },詳細(xì)說明可以參考MessageFormat的API說明文檔。這里我們定義了兩個(gè)占位符,其中的數(shù)字對應(yīng)于傳入的參數(shù)數(shù)組中的索引,{0}占位符被第一個(gè)參數(shù)替換,{1}占位符被第二個(gè)參數(shù)替換,依此類推。
          最多可以設(shè)置10個(gè)占位符,而且每個(gè)占位符可以重復(fù)出現(xiàn)多次,而且格式可以不同,比如{1,date}和{1,time}。而通過將這些模式定義放到不同的資源文件中,就能夠根據(jù)不同的locale設(shè)置,得到不同的模式定義,并用參數(shù)動(dòng)態(tài)替換占位符。

          下面我們就以MessageFormatSample.java程序(源文件見本書所附代碼)為例,來詳細(xì)說明其中的每個(gè)步驟。
          1、找出可變的部分,并據(jù)此定義模式,將模式放入不同的資源文件中。
          比如針對上面的模式,定義了下面兩個(gè)資源文件:
          MessagesBundle_en_US.properties
          Welcome = Hi, {0}! Welcome to Java Research Organization!
          MessagesBundle_zh_CN.properties
          Welcome = 您好,{0}!歡迎來到Java研究組織網(wǎng)站!

          2、創(chuàng)建MessageFormat對象,并設(shè)置其locale屬性。
          1.       MessageFormat formatter = new MessageFormat("");
          2.       formatter.setLocale(currentLocale);

          3、從資源包中得到模式定義,以及設(shè)置參數(shù)。
          1. messages =     ResourceBundle.getBundle(
          2.   "org.javaresearch.j2seimproved.i18n.resource.MessagesBundle",currentLocale);
          3. java/lang/Object.java.html" target="_blank">Object[] testArgs = {"peachpi",new Date()};

          4、利用模式定義和參數(shù)進(jìn)行格式化。
          1.       java/lang/System.java.html" target="_blank">System.out.println(formatter.format(messages.getString("welcome"),testArgs));


          關(guān)于資源包的組織

          一般來說,你是按照資源的用途來組織資源包的,比如會(huì)把所有的頁面按鈕的信息放入一個(gè)名為ButtonResources的資源包中。在實(shí)際的應(yīng)用過程中,以下幾個(gè)原則可以幫你決定如何組織資源包:
          1、要易于維護(hù)。
          2、最好不要將所有的信息都放入一個(gè)資源包中,因?yàn)檫@樣資源包載入內(nèi)存時(shí)將會(huì)很耗時(shí)。
          3、最好將一個(gè)大的資源包分為幾個(gè)小的資源包,這樣可以在使用的時(shí)候才導(dǎo)入必須的資源,減少內(nèi)存消耗。
          主站蜘蛛池模板: 汉源县| 青田县| 互助| 泰宁县| 建瓯市| 武鸣县| 白城市| 阿巴嘎旗| 改则县| 泰州市| 容城县| 霍林郭勒市| 平远县| 东丽区| 依安县| 左云县| 全椒县| 高州市| 肥城市| 新干县| 东丰县| 宁晋县| 万源市| 汝州市| 峨边| 渝中区| 阿拉善右旗| 望都县| 新津县| 河南省| 巴彦淖尔市| 新源县| 邵阳市| 龙胜| 宁海县| 大关县| 罗城| 灵石县| 晋江市| 通江县| 毕节市|