對(duì)于許多軟件開發(fā)者來(lái)說(shuō),一提到國(guó)際化(亦稱為 i18n)支持就會(huì)感到害怕。 要使編寫的代碼能夠面向外國(guó)使用者,確實(shí)需要費(fèi)一翻思量,因?yàn)樵诂F(xiàn)有軟件的代碼中添加國(guó)際化支持可不是一件輕而易舉的事。 如果您感覺(jué)到軟件需要支持不同語(yǔ)言和語(yǔ)言環(huán)境,哪怕這種可能性很小,從一開始就做國(guó)際化項(xiàng)目的準(zhǔn)備,比起項(xiàng)目開始后再試圖添加國(guó)際化支持也要明智得多。
有人問(wèn)“國(guó)際化”是什么意思? 國(guó)際化遠(yuǎn)不止于將用戶界面消息翻譯成不同的語(yǔ)言。 它還涉及到處理不同的字符編碼、日期/時(shí)間/貨幣的顯示形式、以及跨多區(qū)域時(shí)存在的一些其他差異。
版權(quán)聲明:任何獲得Matrix授權(quán)的網(wǎng)站,轉(zhuǎn)載時(shí)請(qǐng)務(wù)必保留以下作者信息和鏈接
作者:
suli2921
原文:
http://www.matrix.org.cn/resource/article/2007-04-16/I18N_a79590da-ebb0-11db-9270-6dd444a118cb.html
關(guān)鍵字:I18N;國(guó)際化
介紹 i18nlog
本文的目的并不在于討論那些關(guān)于國(guó)際化的瑣碎的方面,而是通過(guò)研究一個(gè)稱為 "I18N Messages and Logging" (簡(jiǎn)稱 i18nlog) 的開源項(xiàng)目,來(lái)介紹引入國(guó)際化功能時(shí)需要執(zhí)行的一些必要的任務(wù)
i18nlog 允許您在 Java 應(yīng)用程序內(nèi)集成國(guó)際化的消息,這是通過(guò)向以下內(nèi)容提供 API 來(lái)完成的:
+標(biāo)注 Java 類以識(shí)別國(guó)際化消息
+從所有支持的語(yǔ)言環(huán)境資源包中獲取國(guó)際化消息
+創(chuàng)建特定于語(yǔ)言環(huán)境的異常,并在其中使用國(guó)際化的消息
+使用任何日志框架創(chuàng)建國(guó)際化消息日志
+自動(dòng)生成特定于語(yǔ)言環(huán)境的資源包
+自動(dòng)生成幫助及參考文檔
定義國(guó)際化消息
國(guó)際化軟件時(shí)一項(xiàng)最為乏味的工作莫過(guò)于維護(hù)資源綁定包了。 資源綁定包是包含 "name=value" 這種信息對(duì)的屬性文件 (.properties),其中 "name" 是資源綁定包的關(guān)鍵字字符串 (key string),而 "value" 是翻譯過(guò)的消息字符串本身。 習(xí)慣上為每種語(yǔ)言創(chuàng)建一個(gè)資源綁定包,在每個(gè)綁定包中關(guān)鍵字的設(shè)置是唯一的,而各個(gè)關(guān)鍵字相關(guān)的值就要翻譯成各種語(yǔ)言了。 資源綁定文件的名稱應(yīng)該指明它是為哪種語(yǔ)言創(chuàng)建的,例如,mybundle_en.properties 文件中的消息是用英語(yǔ)寫的,而 mybundle_de.properties 包含德語(yǔ)消息。
i18nlog 提供了一些用于定義資源綁定消息及其關(guān)鍵字字符串的標(biāo)注--用于將資源注入到 i18nlog 的自定義 Ant 任務(wù)中,您可以自動(dòng)生成資源綁定包,而不必為確保屬性文件與訪問(wèn)屬性文件的 Java 代碼之間的一致性作過(guò)多的工作。
@I18NMessage 標(biāo)注被放在常量上,這些常量就是資源綁定包的關(guān)鍵字字符串使用的常量。 使用這些常量可以迫使執(zhí)行編譯時(shí)檢查;例如代碼中引入的拼寫錯(cuò)誤(如常量名稱的拼寫錯(cuò)誤)和使用過(guò)時(shí)消息或已刪除消息,這些錯(cuò)誤在編譯時(shí)就可以被探測(cè)到。 以下是使用此標(biāo)注的示例:
java 代碼
1. @I18NMessage( "Hello, {0}. You last visited on {1,date}" )
2. public static final String MSG_WELCOME = "example.welcome-msg";
上面的示例定義了一條國(guó)際化消息。 常量的值定義了資源綁定包的關(guān)鍵字字符串(key string)。 標(biāo)注的值是一條實(shí)際翻譯好的消息。 您可以將這些標(biāo)注過(guò)的常量放到應(yīng)用程序的任何類或接口中。 可以將它們放在單獨(dú)的類或接口中(將所有消息定義集中到一個(gè)地點(diǎn)),也可以放在使用到它們的類中。
@I18NResourceBundle 標(biāo)注用于定義存放消息的資源綁定包文件(.properties 文件)。 它可以標(biāo)注整個(gè)類或接口,也可以標(biāo)注特定的部分。 如果您標(biāo)注了一個(gè)類或接口,則該類或接口中的所有 @I18NMessage 標(biāo)注都將被存儲(chǔ)在該標(biāo)注定義的資源綁定包中(默認(rèn)情況下)。 如果只將標(biāo)注放在特定的常量上,那么它就只是那個(gè)常量的綁定包。 以下是使用此標(biāo)注的示例:
java 代碼
1. @I18NResourceBundle( baseName = "messages",
2. defaultLocale = "en" )
以上代碼的意思是,所有被 @I18NMessage 標(biāo)注的相關(guān)消息都將放置在名為 messages_en.properties 綁定包文件中。 下面是一個(gè)更復(fù)雜的示例(包含一系列國(guó)際化消息的接口):
java 代碼
1. @I18NResourceBundle( baseName = "messages",
2. defaultLocale = "en" )
3. public interface Messages {
4. @I18NMessage( "Hello, {0}. You last visited on {1,date}" )
5. String MSG_WELCOME = "welcome-msg";
6.
7. @I18NMessage( "An error occurred, please try again" )
8. String MSG_ERR = "error-occurred";
9.
10. @I18NMessage( "The value is {0}" )
11. String MSG_VALUE = "value";
12. }
檢索國(guó)際化消息
定義了國(guó)際化常量后,就可以使用 i18nlog 的核心類提供的 API:mazz.i18n.Msg。 該 API 用于裝載存放于資源綁定屬性文件中的消息。 它還用于將值作為變量參數(shù)進(jìn)行傳遞,從而替換消息中的占位符(例如, {0}, {1,date})。 另外,此 API 將您從直接使用 JDK 類進(jìn)行編碼的工作中解放出來(lái),也許您需要閱讀一下 Javadoc 的 java.text.MessageFormat 部分,以獲得對(duì) i18nlog 工作原理的初步認(rèn)識(shí),尤其是它如何使用本地化的數(shù)據(jù)替換占位符。
下面是 Msg 類的使用實(shí)例:
java 代碼
1. // 使用靜態(tài)工廠方法創(chuàng)建一個(gè) Msg 對(duì)象
2. // 假設(shè)默認(rèn)綁定包的名字是 "messages"
3. System.out.println( Msg.createMsg( Messages.MSG_WELCOME,
4. name,
5. date ) );
和
java 代碼
1. // 使用構(gòu)造函數(shù)創(chuàng)建一個(gè) Msg 對(duì)象
2. Msg msg = new Msg( new Msg.BundleBaseName("messages") );
3. try {
4. String hello = msg.getMsg(Messages.MSG_WELCOME, name, date );
5. ... do something ...
6. }
7. catch (Exception e) {
8. throw new RuntimeException( msg.getMsg( Messages.MSG_ERR ) );
9. }
name 和 date 參數(shù)是傳遞一個(gè)任意參數(shù)列表的示例--每個(gè)對(duì)象代表各個(gè)位置上要被替換掉的占位符({0} 和 {1,date} 分別基于前面給出的 Messages.MSG_WELCOME 中的定義)。
Msg 對(duì)象會(huì)根據(jù)“綁定包的名稱”及其當(dāng)前的語(yǔ)言環(huán)境設(shè)置獲知使用哪種語(yǔ)言環(huán)境的資源綁定包。 “綁定包的名稱”是綁定包文件的名稱減去語(yǔ)言環(huán)境符號(hào)和擴(kuò)展名(在上面的示例中,綁定包的名稱是 messages)。 您可以通過(guò)傳遞到 Msg 的構(gòu)造函數(shù)或者特定的靜態(tài)工廠方法的方式,來(lái)定義 Msg 對(duì)象使用的“綁定包的名稱”,如果不進(jìn)行明確地指定,將使用默認(rèn)的 messages。 “綁定包的名稱”在 Msg 對(duì)象的生命周期內(nèi)將一直存在,可以通過(guò)調(diào)用 Msg 對(duì)象的 setLocale() 來(lái)切換語(yǔ)言環(huán)境(默認(rèn)語(yǔ)言環(huán)境是 JVM 使用的語(yǔ)言環(huán)境)。 如果您使用了 Msg 對(duì)象并且要求根據(jù)(被分配了 Msg 對(duì)象的)使用者的需要來(lái)切換語(yǔ)言環(huán)境,這將是非常有用的。
本地化的異常
i18nlog 提供了兩個(gè)基本的異常類(分別用于已查看和未查看的異常-- LocalizedException 和 LocalizedRuntimeException),可以這兩個(gè)類創(chuàng)建自己的本地化異常子類。 這些類都具有構(gòu)造函數(shù),其函數(shù)簽名與 Msg 類非常相似。 在使用構(gòu)造函數(shù)時(shí),通過(guò)調(diào)用資源綁定包的關(guān)鍵字和占位符的變量參數(shù)列表的方法來(lái)指定異常消息,同時(shí)還可以選擇綁定包的名稱和語(yǔ)言環(huán)境。 這樣一來(lái),異常消息就可以使用本地化的各種語(yǔ)言,其原理就是利用了 Msg 可以檢索本地化的消息。
創(chuàng)建國(guó)際化消息的日志
i18nlog 提供了一種方法,可以通過(guò)該方法記錄國(guó)際化的日志信息。 日志系統(tǒng)的主類是 mazz.i18n.Logger。 它提供了 trace、debug、info、warn、error 和 fatal 方法等一些典型的設(shè)置。 需要指出的是,與傳遞一個(gè)消息本身組成的字符串不同,您為占位符傳遞的是資源綁定包的關(guān)鍵字字符串和參數(shù)列表。 它使用具有保護(hù)作用的 mazz.i18n.Msg 類來(lái)獲取實(shí)際的本地化消息。
通過(guò)使用工廠類 mazz.i18n.LoggerFactory 來(lái)獲得 Logger 對(duì)象,與 log4j 等獲得對(duì)象的方式基本相同。 而不同的是記錄日志消息的方法,該方法與通過(guò) mazz.i18n.Msg 對(duì)象獲取消息非常類似:
java 代碼
1. public static final mazz.i18n.Logger LOG =
2. mazz.i18n.LoggerFactory.getLogger(MyClass.class);
3. ...
4. LOG.debug(Messages.MSG_VALUE, value);
5. ...
6. try {
7. ...
8. }
9. catch (Exception e) {
10. LOG.warn(e, Messages.MSG_ERR);
11. }
如果沒(méi)有啟用日志級(jí)別,則不會(huì)查找綁定包,也不會(huì)執(zhí)行任何字符串連接。這一條件有效地加快了日志調(diào)用的速度。 如果日志消息與特定的表達(dá)式相關(guān)聯(lián),則需要將表達(dá)式的第一個(gè)參數(shù)傳遞給日志方法。 如果啟用了棧傾卸設(shè)置,這將允許棧跟蹤傾卸消息(請(qǐng)往下看)。
i18nlog 的日志框架中還添加了許多其他的功能,這些功能并非潛在的、第三方的日志框架所能媲美。 首先要介紹的功能就是,可以告訴 Logger 是否傾卸異常的棧跟蹤。 您可能需要查看一個(gè)特定運(yùn)行任務(wù)的所有異常的棧跟蹤,也可能不想看。 請(qǐng)注意,對(duì)于在 FATAL 日志級(jí)別上的異常,不能禁用此功能,使用該方法記錄的致命異常必需允許傾卸棧跟蹤。 對(duì)于其他的日志級(jí)別,日志程序只有在系統(tǒng)屬性 i18nlog.dump-stack-traces 設(shè)置為 true (或者在代碼中調(diào)用 Logger.setDumpStackTraces(true))的時(shí)候才會(huì)傾泄棧跟蹤。
i18nlog 日志框架附加的第二項(xiàng)功能是,在記錄與一個(gè)消息關(guān)聯(lián)的資源綁定包關(guān)鍵字的同時(shí),記錄消息本身。 資源綁定包關(guān)鍵字對(duì)于所有語(yǔ)言環(huán)境都是相同的,也就是說(shuō),無(wú)論消息是用何種語(yǔ)言寫成的,關(guān)鍵字是不變的。 可以把這些關(guān)鍵字想象成“消息的 ID”或“錯(cuò)誤的代碼”。 這在生成涉及到這些代碼的幫助文檔時(shí)非常有用,用戶將可以參考文檔中的額外幫助文本,以得知到底要傳遞什么消息(關(guān)于如何生成這種文檔,請(qǐng)查看下文)。 在默認(rèn)情況下,此功能處于啟用狀態(tài),要禁用此功能,只需將系統(tǒng)屬性 i18nlog.dump-keys 設(shè)置為 false 或者在代碼中調(diào)用 Logger.setDumpLogKeys(false)。
提供消息 ID、避免在已禁用日志級(jí)別上拼接字符串,這些 i18nlog 提供的日志機(jī)制也許已經(jīng)足夠了。 也許有人會(huì)爭(zhēng)論說(shuō),將(除用戶界面消息以外的)日志消息國(guó)際化是一種負(fù)擔(dān)而且也沒(méi)有必要。 我有時(shí)也感到很難說(shuō)服這種觀點(diǎn)。 如果項(xiàng)目沒(méi)有這種需求,您當(dāng)然不必使用國(guó)際化日志。 即使不使用 i18nlog 提供的日志功能,還可以使用它提供的其他功能嘛。
但是,我還是可以舉出一些具體的例子來(lái)證明使用國(guó)際化消息的好處。 請(qǐng)注意,i18nlog 允許定義一個(gè)不同的語(yǔ)言環(huán)境供日志程序使用(日志語(yǔ)言環(huán)境),該語(yǔ)言環(huán)境區(qū)別于 Msg 實(shí)例使用的語(yǔ)言環(huán)境。 這就可以幫助使用我的開發(fā)團(tuán)隊(duì)的語(yǔ)言來(lái)記錄日志消息,而我的用戶界面使用用戶可以閱讀的語(yǔ)言(可能與開發(fā)團(tuán)隊(duì)使用的語(yǔ)言不同)。 例如,我的用戶說(shuō)德語(yǔ),而軟件是由說(shuō)法語(yǔ)的法國(guó)團(tuán)隊(duì)開發(fā)的。 在這種情況下,德國(guó)用戶碰到一個(gè)問(wèn)題時(shí),就可以正常地給法國(guó)開發(fā)團(tuán)隊(duì)發(fā)送日志,軟件可以默認(rèn)地將語(yǔ)言環(huán)境設(shè)置為 Locale.FRENCH。 另一方面,如果德國(guó)用戶希望自行調(diào)試問(wèn)題,法語(yǔ)的日志消息根本不會(huì)有任何幫助。 在這種情況下,德國(guó)用戶簡(jiǎn)單地通過(guò)設(shè)置系統(tǒng)屬性,將日志消息記錄為德語(yǔ)。 關(guān)于如何切換日志語(yǔ)言環(huán)境的信息,請(qǐng)參考 mazz.i18n.LoggerLocale Javadoc。
自動(dòng)生成資源綁定包
i18nlog 提供了一個(gè) Ant 任務(wù),用于自動(dòng)生成資源綁定包屬性文件,以避免開發(fā)者擔(dān)負(fù)手動(dòng)向?qū)傩晕募刑砑酉?、清理過(guò)時(shí)消息的任務(wù)。
該 Ant 任務(wù)將掃描類以查找 @I18N 標(biāo)注,并根據(jù)這些標(biāo)注為您創(chuàng)建資源綁定包。 這意味著,無(wú)論添加多少 @I18NMessage 標(biāo)注字段,它們都將被加入到資源綁定包中。 如果您刪除了一個(gè)國(guó)際化消息常量,則該消息也將從 Ant 任務(wù)生成的資源綁定包結(jié)果中刪除。 要運(yùn)行該 Ant 任務(wù),需要在 Ant 腳本中添加以下類似代碼:
xml 代碼
1. <taskdef name="i18n"
2. classpathref="i18nlog-jar.classpath"
3. classname="mazz.i18n.ant.I18NAntTask" />
4.
5. <i18n outputdir="${classes.dir}" verify="true" verbose="true">
6. <classpath refid="my.classpath" />
7. <classfileset dir="${classes.dir}"/>
8. i18n>
必需讓 Ant 任務(wù)知道含有國(guó)際化標(biāo)注類及其依賴關(guān)系的類的類路徑 ,還必須給出類文件集 ,其中包含文件集的文件列表供掃描國(guó)際化標(biāo)注之用。 建議您在第一次運(yùn)行 Ant 任務(wù)的時(shí)候采用“冗長(zhǎng)”模式,以便知道 Ant 任務(wù)的執(zhí)行內(nèi)容。 一旦得到了想要的效果,再將“冗長(zhǎng)”模式關(guān)閉。 執(zhí)行此任務(wù)之后,資源綁定包屬性文件將出現(xiàn)在指定的輸出目錄中。
生成幫助文檔
使用此 Ant 任務(wù)的另一個(gè)可選功能是生成幫助文檔,該文檔是由所有對(duì)資源綁定包關(guān)鍵字名稱及其消息值的引用組成的,另外還包含了對(duì)這些消息的描述。 這是一個(gè)可在 @I18NMessage 標(biāo)注中指定的可選屬性,help 屬性。 屬性值可以是深入描述消息的任何字符串。 可以將文檔想象成在特定情況下將傳遞的具體消息。 自動(dòng)生成的幫助文檔可以提供消息關(guān)鍵字、消息本身以及消息描述之間的交叉引用:
java 代碼
1. @I18NMessage( value="The value is {0}",
2. help="This will show you the value of your"
3. +" current counter. If this value is over"
4. +" 1000, you should reset it.")
5. String MSG_VALUE = "value";
6.
7. @I18NMessage( value="Memory has {0} free bytes left",
8. help="The VM is very low on memory. Increase -Xmx"
9. String MSG_LOW_MEM = "low-memory";
大多數(shù)情況下,您可以將其作為“消息的 ID”或者“錯(cuò)誤的代碼”列表使用,可以將其中的資源綁定包關(guān)鍵字當(dāng)成一個(gè)“消息 ID”或者“錯(cuò)誤的代碼”。 要生成幫助文檔,需要在 任務(wù)中使用 內(nèi)部標(biāo)記:
xml 代碼
1. <i18n outputdir="${classes.dir}">
2. <classpath refid="my.classpath" />
3. <classfileset dir="${classes.dir}"/>
4. <helpdoc outputdir="${doc.dir}/help"/>
5. i18n>
對(duì)于每個(gè)生成的資源綁定包,都可以在 <helpdoc> 中指定的目錄下找到一個(gè)相應(yīng)的幫助文檔。 文檔是根據(jù)用于描述文檔樣式的模板生成的。 默認(rèn)情況下,模板是一個(gè)簡(jiǎn)單的 HTML 頁(yè),使用 <table> 標(biāo)記消息代碼,而輸出就是幫助文檔。 在 <helpdoc> 標(biāo)記中還可以指定一些其他的屬性來(lái)自定義一個(gè)模板,以滿足自行定制幫助文檔外觀的需求。 有關(guān)更多信息,請(qǐng)查閱 mazz.i18n.ant.Helpdoc 類的 Javadoc。
在幫助文檔的生成結(jié)束之后,您就可以得到一個(gè)(或一些)包含消息關(guān)鍵字代碼、消息內(nèi)容及任何 "help" 屬性定義內(nèi)容的文檔了。
本地化
我們已經(jīng)討論了許多關(guān)于如何通過(guò)獲取翻譯的消息和本地化的消息國(guó)際化軟件的內(nèi)容。 嵌入國(guó)際化功能之后,剩下的工作就是手動(dòng)處理那些需要被本地化的資源綁定屬性文件(由 <i18n> Ant 任務(wù)生成或者自行手動(dòng)編寫)了。 必須確保將所有資源綁定包的消息翻譯成需要支持的語(yǔ)言,而且這些消息包含的數(shù)據(jù)也進(jìn)行相應(yīng)的本地化。 通過(guò)定義占位符屬性(例如,{0,date} 將使用目標(biāo)語(yǔ)言環(huán)境的格式和語(yǔ)言輸出日期字符串)可以完成許多本地化方面的工作。 不言而喻的是,必須找一家優(yōu)秀的翻譯和本地化公司。
小結(jié)
這篇文章介紹了如何通過(guò)一個(gè)新的開源項(xiàng)目 i18nlog 在應(yīng)用程序中溶入國(guó)際化功能。 使用該開源項(xiàng)目提供的工具和 API 可以自動(dòng)管理資源綁定包屬性文件(.properties),檢索和管理這些綁定包中的本地化消息,甚至還可以生成供最終用戶使用的幫助文檔。
資源
+i18nlog 用戶指南:
i18nlog.sourceforge.net/doc/users-guide.html
+本地化消息時(shí)使用的占位符語(yǔ)法:
java.sun.com/j2se/1.5.0/docs/api/java/text/MessageFormat.html
+Matrix Java社區(qū):
http://www.matrix.org.cn/
John Mazzitelli 是一位 JBoss 開發(fā)者,Red Hat 的帶頭人,目前正致力于 “Boss Operations Network 管理平臺(tái)”的實(shí)現(xiàn)。