Java國(guó)際化簡(jiǎn)介_(kāi)動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
假設(shè)我們正在開(kāi)發(fā)一個(gè)支持多國(guó)語(yǔ)言的Web應(yīng)用程序,要求系統(tǒng)能夠根據(jù)客戶端的系統(tǒng)的語(yǔ)言類(lèi)型返回對(duì)應(yīng)的界面:英文的操作系統(tǒng)返回英文界面,而中文的操作系統(tǒng)則返回中文界面——這便是典型的i18n國(guó)際化問(wèn)題。對(duì)于有國(guó)際化要求的應(yīng)用系統(tǒng),我們不能簡(jiǎn)單地采用硬編碼的方式編寫(xiě)用戶界面信息、報(bào)錯(cuò)信息等內(nèi)容,而必須為這些需要國(guó)際化的信息進(jìn)行特殊處理。簡(jiǎn)單來(lái)說(shuō),就是為每種語(yǔ)言提供一套相應(yīng)的資源文件,并以規(guī)范化命名的方式保存在特定的目錄中,由系統(tǒng)自動(dòng)根據(jù)客戶端語(yǔ)言選擇適合的資源文件。
基礎(chǔ)知識(shí)
“國(guó)際化信息”也稱(chēng)為“本地化信息”,一般需要兩個(gè)條件才可以確定一個(gè)特定類(lèi)型的本地化信息,它們分別是“語(yǔ)言類(lèi)型”和“國(guó)家/地區(qū)的類(lèi)型”。如中文本地化信息既有中國(guó)大陸地區(qū)的中文,又有中國(guó)臺(tái)灣、中國(guó)香港地區(qū)的中文,還有新加坡地區(qū)的中文。Java通過(guò)java.util.Locale類(lèi)表示一個(gè)本地化對(duì)象,它允許通過(guò)語(yǔ)言參數(shù)和國(guó)家/地區(qū)參數(shù)創(chuàng)建一個(gè)確定的本地化對(duì)象。
語(yǔ)言參數(shù)使用ISO標(biāo)準(zhǔn)語(yǔ)言代碼表示,這些代碼是由ISO-639標(biāo)準(zhǔn)定義的,每一種語(yǔ)言由兩個(gè)小寫(xiě)字母表示。
國(guó)家/地區(qū)參數(shù)也由標(biāo)準(zhǔn)的ISO國(guó)家/地區(qū)代碼表示,這些代碼是由ISO-3166標(biāo)準(zhǔn)定義的,每個(gè)國(guó)家/地區(qū)由兩個(gè)大寫(xiě)字母表示。
表5-2給出了一些語(yǔ)言和國(guó)家/地區(qū)的標(biāo)準(zhǔn)代碼:
java.util.Locale是表示語(yǔ)言和國(guó)家/地區(qū)信息的本地化類(lèi),它是創(chuàng)建國(guó)際化應(yīng)用的基礎(chǔ)。下面給出幾個(gè)創(chuàng)建本地化對(duì)象的示例:
//①帶有語(yǔ)言和國(guó)家/地區(qū)信息的本地化對(duì)象 Locale locale1 = new Locale("zh","CN"); //②只有語(yǔ)言信息的本地化對(duì)象 Locale locale2 = new Locale("zh"); //③等同于Locale("zh","CN") Locale locale3 = Locale.CHINA; //④等同于Locale("zh") Locale locale4 = Locale.CHINESE; //⑤獲取本地系統(tǒng)默認(rèn)的本地化對(duì)象 Locale locale 5= Locale.getDefault();
用戶既可以同時(shí)指定語(yǔ)言和國(guó)家/地區(qū)參數(shù)定義一個(gè)本地化對(duì)象①,也可以僅通過(guò)語(yǔ)言參數(shù)定義一個(gè)泛本地化對(duì)象②。Locale類(lèi)中通過(guò)靜態(tài)常量定義了一些常用的本地化對(duì)象,③和④處就直接通過(guò)引用常量返回本地化對(duì)象。此外,用戶還可以獲取系統(tǒng)默認(rèn)的本地化對(duì)象,如⑤所示。
在測(cè)試時(shí),如果希望改變系統(tǒng)默認(rèn)的本地化設(shè)置,可以在啟動(dòng)JVM時(shí)通過(guò)命令參數(shù)指定:java -Duser.language=en -Duser.region=US MyTest。
本地化工具類(lèi)
JDK的java.util包中提供了幾個(gè)支持本地化的格式化操作工具類(lèi):NumberFormat、DateFormat、MessageFormat。下面,我們分別通過(guò)實(shí)例了解它們的用法:
NumberFormat:
Locale locale = new Locale("zh", "CN"); NumberFormat currFmt = NumberFormat.getCurrencyInstance(locale); double amt = 123456.78; System.out.println(currFmt.format(amt));
上面的實(shí)例通過(guò)NumberFormat按本地化的方式對(duì)貨幣金額進(jìn)行格式化操作,運(yùn)行實(shí)例,輸出以下信息:
¥123,456.78
代碼清單5-14 DateFormat
Locale locale = new Locale("en", "US"); Date date = new Date(); DateFormat df = DateFormat.getDateInstance(DateFormat.MEDIUM, locale); System.out.println(df.format(date));
通過(guò)DateFormat#getDateInstance(int style,Locale locale)方法按本地化的方式對(duì)日期進(jìn)行格式化操作。該方法第一個(gè)入?yún)闀r(shí)間樣式,第二個(gè)入?yún)楸镜鼗瘜?duì)象。運(yùn)行以上代碼,輸出以下信息:
Jan 8, 2007
MessageFormat在NumberFormat和DateFormat的基礎(chǔ)上提供了強(qiáng)大的占位符字符串的格式化功能,它支持時(shí)間、貨幣、數(shù)字以及對(duì)象屬性的格式化操作。下面的實(shí)例演示了一些常見(jiàn)的格式化功能:
代碼清單5-15 MessageFormat
//①信息格式化串 String pattern1 = "{0},你好!你于 {1} 在工商銀行存入 {2} 元。"; String pattern2 = "At {1,time,short} On {1,date,long},{0} paid {2,number, currency}."; //②用于動(dòng)態(tài)替換占位符的參數(shù) Object[] params = {"John", new GregorianCalendar().getTime(), 1.0E3}; //③使用默認(rèn)本地化對(duì)象格式化信息 String msg1 = MessageFormat.format(pattern1, params); //④使用指定的本地化對(duì)象格式化信息 MessageFormat mf = new MessageFormat(pattern2, Locale.US); String msg2 = mf.format(params); System.out.println(msg1); System.out.println(msg2);
pattern1是簡(jiǎn)單形式的格式化信息串,通過(guò){n}占位符指定動(dòng)態(tài)參數(shù)的替換位置索引,{0}表示第一個(gè)參數(shù),{1}表示第二個(gè)參數(shù),以此類(lèi)推。
pattern2格式化信息串比較復(fù)雜一些,除參數(shù)位置索引外,還指定了參數(shù)的類(lèi)型和樣式。從pattern2中可以看出格式化信息串的語(yǔ)法是很靈活的,一個(gè)參數(shù)甚至可以出現(xiàn)在兩個(gè)地方:如 {1,time,short}表示從第二個(gè)入?yún)⒅蝎@取時(shí)間部分的值,顯示為短樣式時(shí)間;而{1,date,long}表示從第二個(gè)入?yún)⒅蝎@取日期部分的值,顯示為長(zhǎng)樣式時(shí)間。關(guān)于MessageFormat更詳細(xì)的使用方法,請(qǐng)參見(jiàn)JDK的Javadoc。
在②處,定義了用于替換格式化占位符的動(dòng)態(tài)參數(shù),這里,我們使用到了JDK5.0自動(dòng)裝包的語(yǔ)法,否則必須采用封裝類(lèi)表示基本類(lèi)型的參數(shù)值。
在③處,通過(guò)MessageFormat的format()方法格式化信息串。它使用了系統(tǒng)默認(rèn)的本地化對(duì)象,由于我們是中文平臺(tái),因此默認(rèn)為L(zhǎng)ocale.CHINA。而在④處,我們顯式指定MessageFormat的本地化對(duì)象。
運(yùn)行上面的代碼,輸出以下信息:
引用
John,你好!你于 14-7-7 下午11:29 在工商銀行存入 1,000 元。
At 11:29 PM On July 7, 2014,John paid $1,000.00.
如果應(yīng)用系統(tǒng)中某些信息需要支持國(guó)際化功能,則必須為希望支持的不同本地化類(lèi)型分別提供對(duì)應(yīng)的資源文件,并以規(guī)范的方式進(jìn)行命名。國(guó)際化資源文件的命名規(guī)范規(guī)定資源名稱(chēng)采用以下的方式進(jìn)行命名:
引用
<資源名>_<語(yǔ)言代碼>_<國(guó)家/地區(qū)代碼>.properties
其中,語(yǔ)言代碼和國(guó)家/地區(qū)代碼都是可選的。<資源名>.properties命名的國(guó)際化資源文件是默認(rèn)的資源文件,即某個(gè)本地化類(lèi)型在系統(tǒng)中找不到對(duì)應(yīng)的資源文件,就采用這個(gè)默認(rèn)的資源文件。<資源名>_<語(yǔ)言代碼>.properties命名的國(guó)際化資源文件是某一語(yǔ)言默認(rèn)的資源文件,即某個(gè)本地化類(lèi)型在系統(tǒng)中找不到精確匹配的資源文件,將采用相應(yīng)語(yǔ)言默認(rèn)的資源文件。
舉一個(gè)例子:假設(shè)資源名為resource,則語(yǔ)言為英文,國(guó)家為美國(guó),則與其對(duì)應(yīng)的本地化資源文件命名為resource_en_US.properties。信息在資源文件以屬性名/值的方式表示:
引用
greeting.common=How are you!
greeting.morning = Good morning!
greeting.afternoon = Good Afternoon!
對(duì)應(yīng)語(yǔ)言為中文,國(guó)家/地區(qū)為中國(guó)大陸的本地化資源文件則命名為resource_zh_ CN.properties,資源文件內(nèi)容如下:
greeting.common=\u60a8\u597d\uff01
greeting.morning=\u65e9\u4e0a\u597d\uff01
greeting.afternoon=\u4e0b\u5348\u597d\uff01
本地化不同的同一資源文件,雖然屬性值各不相同,但屬性名卻是相同的,這樣應(yīng)用程序就可以通過(guò)Locale對(duì)象和屬性名精確調(diào)用到某個(gè)具體的屬性值了。
讀者可能已經(jīng)注意到,上面中文的本地化資源文件內(nèi)容采用了特殊的編碼表示中文字符,這是因?yàn)橘Y源文件對(duì)文件內(nèi)容有嚴(yán)格的要求:只能包含ASCII字符。所以必須將非ASCII字符的內(nèi)容轉(zhuǎn)換為Unicode代碼的表示方式。如上面中文的resource_zh_CN.properties資源文件的三個(gè)屬性值分別是“您好!”、“早上好!”和“下午好!”三個(gè)中文字符串對(duì)應(yīng)的Unicode代碼串。
如果在應(yīng)用開(kāi)發(fā)時(shí),直接采用Unicode代碼編輯資源文件是很不方便的,所以,通常我們直接使用正常的方式編寫(xiě)資源文件,在測(cè)試或部署時(shí)再采用工具進(jìn)行轉(zhuǎn)換。JDK在bin目錄下為我們提供了一個(gè)完成此項(xiàng)功能的native2ascii工具,它可以將中文字符的資源文件轉(zhuǎn)換為Unicode代碼格式的文件,命令格式如下:
引用
native2ascii [-reverse] [-encoding 編碼] [輸入文件 [輸出文件]]
resource_zh_CN.properties包含中文字符并且以UTF-8進(jìn)行編碼,假設(shè)將該資源文件放到d:\目錄下,通過(guò)下面的命令就可以將其轉(zhuǎn)換為Unicode代碼的形式:
引用
D:\>native2ascii -encoding utf-8 d:\resource_zh_CN.properties
d:\resource_zh_CN_1.properties
由于原資源文件采用UTF-8編碼,所以必須顯式通過(guò)-encoding指定編碼格式。
引用
通過(guò)native2ascii命令手工轉(zhuǎn)換資源文件,不但在操作上不方便,轉(zhuǎn)換后資源文件中的屬性內(nèi)容由于采用了ASCII編碼,閱讀起來(lái)也不方便。很多IDE開(kāi)發(fā)工具都有屬性編輯器的插件,插件會(huì)自動(dòng)將資源文件內(nèi)容轉(zhuǎn)換為ASCII形式的編碼,同時(shí)以正常的方式閱讀和編輯資源文件的內(nèi)容,這給開(kāi)發(fā)和維護(hù)帶來(lái)了很大的便利。對(duì)于MyEclipse來(lái)說(shuō),使用MyEclipse Properties Editor編輯資源屬性文件;對(duì)于Intellij IDEA來(lái)說(shuō),無(wú)須安裝任何插件就自然支持資源屬性文件的這種編輯方式了。
如果應(yīng)用程序中擁有大量的本地化資源文件,直接通過(guò)傳統(tǒng)的File操作資源文件顯然太過(guò)笨拙。Java為我們提供了用于加載本地化資源文件的方便類(lèi)java.util.ResourceBoundle。
ResourceBoundle為加載及訪問(wèn)資源文件提供便捷的操作,下面的語(yǔ)句從相對(duì)于類(lèi)路徑的目錄中加載一個(gè)名為resource的本地化資源文件:
ResourceBundle rb = ResourceBundle.getBundle("com/baobaotao/i18n/resource", locale)
通過(guò)以下的代碼即可訪問(wèn)資源文件的屬性值:
rb.getString("greeting.common")
來(lái)看下面的實(shí)例:
代碼清單5-16 ResourceBoundle
ResourceBundle rb1 = ResourceBundle.getBundle("com/baobaotao/i18n/resource", Locale.US); ResourceBundle rb2 = ResourceBundle.getBundle("com/baobaotao/i18n/resource", Locale.CHINA); System.out.println("us:"+rb1.getString("greeting.common")); System.out.println("cn:"+rb2.getString("greeting.common"));
rb1加載了對(duì)應(yīng)美國(guó)英語(yǔ)本地化的resource_en_US.properties資源文件;而rb2加載了對(duì)應(yīng)中國(guó)大陸中文的resource_zh_CN.properties資源文件。運(yùn)行上面的代碼,將輸出以下信息:
引用
us:How are you!
cn:你好!
加載資源文件時(shí),如果不指定本地化對(duì)象,將使用本地系統(tǒng)默認(rèn)的本地化對(duì)象。所以,在中文系統(tǒng)中,ResourceBundle.getBundle("com/baobaotao/i18n/resource")語(yǔ)句也將返回和代碼清單5-14中rb2相同的本地化資源。
ResourceBundle在加載資源時(shí),如果指定的本地化資源文件不存在,它按以下順序嘗試加載其他的資源:本地系統(tǒng)默認(rèn)本地化對(duì)象對(duì)應(yīng)的資源→默認(rèn)的資源。上面的例子中,假設(shè)我們使用ResourceBundle.getBundle("com/baobaotao/i18n/resource",Locale.CANADA)加載資源,由于不存在resource_en_CA.properties資源文件,它將嘗試加載resource_zh_CN.properties的資源文件,假設(shè)resource_zh_CN.properties資源文件也不存在,它將繼續(xù)嘗試加載resource.properties的資源文件,如果這些資源都不存在,將拋出java.util.MissingResourceException異常。
在資源文件中使用格式化串
在上面的資源文件中,屬性值都是一般的字符串,它們不能結(jié)合運(yùn)行時(shí)的動(dòng)態(tài)參數(shù)構(gòu)造出靈活的信息,而這種需求是很常見(jiàn)的。要解決這個(gè)問(wèn)題很簡(jiǎn)單,只須使用帶占位符的格式化串作為資源文件的屬性值并結(jié)合使用MessageFormat就可以滿足要求了。
上面的例子中,我們僅向用戶提供一般性問(wèn)候,下面我們對(duì)資源文件進(jìn)行改造,通過(guò)格式化串讓問(wèn)候語(yǔ)更具個(gè)性化:
引用
greeting.common=How are you!{0},today is {1}
greeting.morning = Good morning!{0},now is {1 time short}
greeting.afternoon = Good Afternoon!{0} now is {1 date long}
將該資源文件保存在fmt_resource_en_US.properties中,按照同樣的方式編寫(xiě)對(duì)應(yīng)的中文本地化資源文件fmt_resource_zh_CN.properties。
下面,我們聯(lián)合使用ResourceBoundle和MessageFormat得到美國(guó)英文的本地化問(wèn)候語(yǔ):
//①加載本地化資源 ResourceBundle rb1 = ResourceBundle.getBundle("com/baobaotao/i18n/fmt_ resource",Locale.US); ResourceBundle rb2 = ResourceBundle.getBundle("com/baobaotao/i18n/fmt_ resource",Locale.CHINA); Object[] params = {"John", new GregorianCalendar().getTime()}; String str1 = new MessageFormat(rb1.getString("greeting.common"),Locale.US).format(params); String str2 =new MessageFormat(rb2.getString("greeting.morning"),Locale.CHINA).format(params); String str3 =new MessageFormat(rb2.getString("greeting.afternoon"),Locale.CHINA).format(params); System.out.println(str1); System.out.println(str2); System.out.println(str3);
運(yùn)行以上的代碼,將輸出以下信息:
引用
How are you!John,today is 1/9/07 4:11 PM
早上好!John,現(xiàn)在是下午4:11
下午好!John,現(xiàn)在是2007年1月9日
MessageSource
Spring定義了訪問(wèn)國(guó)際化信息的MessageSource接口,并提供了幾個(gè)易用的實(shí)現(xiàn)類(lèi)。首先來(lái)了解一下該接口的幾個(gè)重要方法:
String getMessage(String code, Object[] args, String defaultMessage, Locale locale)
code表示國(guó)際化資源中的屬性名;args用于傳遞格式化串占位符所用的運(yùn)行期參數(shù);當(dāng)在資源找不到對(duì)應(yīng)屬性名時(shí),返回defaultMessage參數(shù)所指定的默認(rèn)信息;locale表示本地化對(duì)象;
String getMessage(String code, Object[] args, Locale locale) throws NoSuchMessageException
與上面的方法類(lèi)似,只不過(guò)在找不到資源中對(duì)應(yīng)的屬性名時(shí),直接拋出NoSuchMessageException異常;
String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException
MessageSourceResolvable 將屬性名、參數(shù)數(shù)組以及默認(rèn)信息封裝起來(lái),它的功能和第一個(gè)接口方法相同。
MessageSource的類(lèi)結(jié)構(gòu)
MessageSource分別被HierarchicalMessageSource和ApplicationContext接口擴(kuò)展,這里我們主要看一下HierarchicalMessageSource接口的幾個(gè)實(shí)現(xiàn)類(lèi),如圖5-7所示:
HierarchicalMessageSource接口添加了兩個(gè)方法,建立父子層級(jí)的MessageSource結(jié)構(gòu),類(lèi)似于前面我們所介紹的HierarchicalBeanFactory。該接口的setParentMessageSource (MessageSource parent)方法用于設(shè)置父MessageSource,而getParentMessageSource()方法用于返回父MessageSource。
HierarchicalMessageSource接口最重要的兩個(gè)實(shí)現(xiàn)類(lèi)是ResourceBundleMessageSource和ReloadableResourceBundleMessageSource。它們基于Java的ResourceBundle基礎(chǔ)類(lèi)實(shí)現(xiàn),允許僅通過(guò)資源名加載國(guó)際化資源。ReloadableResourceBundleMessageSource提供了定時(shí)刷新功能,允許在不重啟系統(tǒng)的情況下,更新資源的信息。StaticMessageSource主要用于程序測(cè)試,它允許通過(guò)編程的方式提供國(guó)際化信息。而DelegatingMessageSource是為方便操作父MessageSource而提供的代理類(lèi)。
ResourceBundleMessageSource
該實(shí)現(xiàn)類(lèi)允許用戶通過(guò)beanName指定一個(gè)資源名(包括類(lèi)路徑的全限定資源名),或通過(guò)beanNames指定一組資源名。在前面的代碼清單中,我們通過(guò)JDK的基礎(chǔ)類(lèi)完成了本地化的操作,下面我們使用ResourceBundleMessageSource來(lái)完成相同的任務(wù)。讀者可以比較兩者的使用差別,并體會(huì)Spring所提供的國(guó)際化處理功能所帶給我們的好處:
通過(guò)ResourceBundleMessageSource配置資源
<bean id="myResource" class="org.springframework.context.support.ResourceBundleMessageSource"> <!--①通過(guò)基名指定資源,相對(duì)于類(lèi)根路徑--> <property name="basenames"> <list> <value>com/baobaotao/i18n/fmt_resource</value> </list> </property> </bean>
啟動(dòng)Spring容器,并通過(guò)MessageSource訪問(wèn)配置的國(guó)際化資源,如下代碼清單所示:
代碼清單5-19 訪問(wèn)國(guó)際化消息:ResourceBundleMessageSource:
String[] configs = {"com/baobaotao/i18n/beans.xml"}; ApplicationContext ctx = new ClassPathXmlApplicationContext(configs); //①獲取MessageSource的Bean MessageSource ms = (MessageSource)ctx.getBean("myResource"); Object[] params = {"John", new GregorianCalendar().getTime()}; //②獲取格式化的國(guó)際化信息 String str1 = ms.getMessage("greeting.common",params,Locale.US); String str2 = ms.getMessage("greeting.morning",params,Locale.CHINA); String str3 = ms.getMessage("greeting.afternoon",params,Locale.CHINA); System.out.println(str1); System.out.println(str2); System.out.println(str3);
比較代碼清單中的代碼,我們發(fā)現(xiàn)最主要的區(qū)別在于我們無(wú)須再分別加載不同語(yǔ)言、不同國(guó)家/地區(qū)的本地化資源文件,僅僅通過(guò)資源名就可以加載整套的國(guó)際化資源文件。此外,我們無(wú)須顯式使用MessageFormat操作國(guó)際化信息,僅通過(guò)MessageSource# getMessage()方法就可以完成操作了。這段代碼的運(yùn)行結(jié)果與前面的代碼的運(yùn)行結(jié)果完全一樣。
ReloadableResourceBundleMessageSource
前面,我們提到該實(shí)現(xiàn)類(lèi)比之于ResourceBundleMessageSource的唯一區(qū)別在于它可以定時(shí)刷新資源文件,以便在應(yīng)用程序不重啟的情況下感知資源文件的變化。很多生產(chǎn)系統(tǒng)都需要長(zhǎng)時(shí)間持續(xù)運(yùn)行,系統(tǒng)重啟會(huì)給運(yùn)行帶來(lái)很大的負(fù)面影響。這時(shí),通過(guò)該實(shí)現(xiàn)類(lèi)就可以解決國(guó)際化信息更新的問(wèn)題。請(qǐng)看下面的配置:
通過(guò)ReloadableResourceBundleMessageSource配置資源:
xml代碼
<bean id="myResource" lass="org.springframework.context.support.ReloadableResourceBundleMessageSource"> <property name="basenames"> <list> <value>com/baobaotao/i18n/fmt_resource</value> </list> </property> <!--① 刷新資源文件的周期,以秒為單位--> <property name="cacheSeconds" value="5"/> </bean>
在上面的配置中,我們通過(guò)cacheSeconds屬性讓ReloadableResourceBundleMessageSource每5秒鐘刷新一次資源文件(在真實(shí)的應(yīng)用中,刷新周期不能太短,否則頻繁的刷新將帶來(lái)性能上的負(fù)面影響,一般不建議小于30分鐘)。cacheSeconds默認(rèn)值為-1表示永不刷新,此時(shí),該實(shí)現(xiàn)類(lèi)的功能就蛻化為ResourceBundleMessageSource的功能。
我們編寫(xiě)一個(gè)測(cè)試類(lèi)對(duì)上面配置的ReloadableResourceBundleMessageSource進(jìn)行測(cè)試:
代碼清單5-21 刷新資源:ReloadableResourceBundleMessageSource
String[] configs = {"com/baobaotao/i18n/beans.xml"}; ApplicationContext ctx = new ClassPathXmlApplicationContext(configs); MessageSource ms = (MessageSource)ctx.getBean("myResource"); Object[] params = {"John", new GregorianCalendar().getTime()}; for (int i = 0; i < 2; i++) { String str1 = ms.getMessage("greeting.common",params,Locale.US); System.out.println(str1); Thread.currentThread().sleep(20000); //①模擬程序應(yīng)用,在此期間,我們更改資源文件 }
在①處,我們讓程序睡眠20秒鐘,在這期間,我們將fmt_resource_zh_CN.properties資源文件的greeting.common鍵值調(diào)整為:
引用
---How are you!{0},today is {1}---
我們將看到兩次輸出的格式化信息分別對(duì)應(yīng)更改前后的內(nèi)容,也即本地化資源文件的調(diào)整被自動(dòng)生效了:
引用
How are you!John,today is 1/9/07 4:55 PM
---How are you!John,today is 1/9/07 4:55 PM---
容器級(jí)的國(guó)際化信息資源
在如圖5-7所示的MessageSource類(lèi)圖結(jié)構(gòu)中,我們發(fā)現(xiàn)ApplicationContext實(shí)現(xiàn)了MessageSource的接口。也就是說(shuō)ApplicationContext的實(shí)現(xiàn)類(lèi)本身也是一個(gè)MessageSource對(duì)象。
將ApplicationContext和MessageSource整合起來(lái),乍一看挺讓人費(fèi)解的,Spring這樣設(shè)計(jì)的意圖究竟是什么呢?原來(lái)Spring認(rèn)為:在一般情況下,國(guó)際化信息資源應(yīng)該是容器級(jí)。我們一般不會(huì)將MessageSource作為一個(gè)Bean注入到其他的Bean中,相反MessageSource作為容器的基礎(chǔ)設(shè)施向容器中所有的Bean開(kāi)放。只要我們考察一下國(guó)際化信息的實(shí)際消費(fèi)場(chǎng)所就更能理解Spring這一設(shè)計(jì)的用意了。國(guó)際化信息一般在系統(tǒng)輸出信息時(shí)使用,如Spring MVC的頁(yè)面標(biāo)簽,控制器Controller等,不同的模塊都可能通過(guò)這些組件訪問(wèn)國(guó)際化信息,因此Spring就將國(guó)際化消息作為容器的公共基礎(chǔ)設(shè)施對(duì)所有組件開(kāi)放。
既然一般情況下我們不會(huì)直接通過(guò)引用MessageSource Bean使用國(guó)際信息,那如何聲明容器級(jí)的國(guó)際化信息呢?我們其實(shí)在5.1.1節(jié)講解Spring容器的內(nèi)部工作機(jī)制時(shí)已經(jīng)埋下了伏筆:在介紹容器啟動(dòng)過(guò)程時(shí),我們通過(guò)代碼清單5-1對(duì)Spring容器啟動(dòng)時(shí)的步驟進(jìn)行剖析,④處的initMessageSource()方法所執(zhí)行的工作就是初始化容器中的國(guó)際化信息資源:它根據(jù)反射機(jī)制從BeanDefinitionRegistry中找出名稱(chēng)為“messageSource”且類(lèi)型為org.springframework.context.MessageSource的Bean,將這個(gè)Bean定義的信息資源加載為容器級(jí)的國(guó)際化信息資源。請(qǐng)看下面的配置:
代碼清單5-22 容器級(jí)資源的配置
<!--①注冊(cè)資源Bean,其Bean名稱(chēng)只能為messageSource --> <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"> <property name="basenames"> <list> <value>com/baobaotao/i18n/fmt_resource</value> </list> </property> </bean>
下面,我們通過(guò)ApplicationContext直接訪問(wèn)國(guó)際化信息,如下代碼清單所示:
代碼清單5-23 通過(guò)ApplicationContext訪問(wèn)國(guó)際化信息
Java代碼
String[] configs = {"com/baobaotao/i18n/beans.xml"}; ApplicationContext ctx = new ClassPathXmlApplicationContext(configs); //①直接通過(guò)容器訪問(wèn)國(guó)際化信息 Object[] params = {"John", new GregorianCalendar().getTime()}; String str1 = ctx.getMessage("greeting.common",params,Locale.US); String str2 = ctx.getMessage("greeting.morning",params,Locale.CHINA); System.out.println(str1); System.out.println(str2);
運(yùn)行以上代碼,輸出以下信息:
引用
How are you!John,today is 1/9/07 5:24 PM
早上好!John,現(xiàn)在是下午5:24
假設(shè)MessageSource Bean名字沒(méi)有命名為“messageSource”,以上代碼將拋出NoSuchMessageException異常。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Java多線程下解決資源競(jìng)爭(zhēng)的7種方法詳解
- Java實(shí)現(xiàn)的讀取資源文件工具類(lèi)ResourcesUtil實(shí)例【可動(dòng)態(tài)更改值的內(nèi)容】
- 淺談Java工程讀取resources中資源文件路徑的問(wèn)題
- 詳解Java讀取Jar中資源文件及示例代碼
- Java 讀取類(lèi)路徑下的資源文件實(shí)現(xiàn)代碼
- Java 使用getClass().getResourceAsStream()方法獲取資源
- 詳解Java中用于國(guó)際化的locale類(lèi)
- Java語(yǔ)言資源國(guó)際化步驟解析
相關(guān)文章
Java中Scanner類(lèi)與BufferReader類(lèi)的不同點(diǎn)(非常詳細(xì))
這篇文章主要介紹了Java中Scanner類(lèi)與BufferReader類(lèi)的不同點(diǎn)(非常詳細(xì))的相關(guān)資料,需要的朋友可以參考下2016-08-08Javaweb項(xiàng)目啟動(dòng)Tomcat常見(jiàn)的報(bào)錯(cuò)解決方案
Java Web項(xiàng)目啟動(dòng)Tomcat時(shí)可能會(huì)遇到各種錯(cuò)誤,本文就來(lái)介紹一下Javaweb項(xiàng)目啟動(dòng)Tomcat常見(jiàn)的報(bào)錯(cuò)解決方案,具有一定的參考價(jià)值,感興趣的可以了解一下2024-02-02Java對(duì)象創(chuàng)建內(nèi)存案例解析
這篇文章主要介紹了Java對(duì)象創(chuàng)建內(nèi)存案例解析,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-08-08處理Log4j2不能打印行號(hào)的問(wèn)題(AsyncLogger)
這篇文章主要介紹了處理Log4j2不能打印行號(hào)的問(wèn)題(AsyncLogger),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12關(guān)于gradle多模塊項(xiàng)目依賴(lài)管理方式
這篇文章主要介紹了關(guān)于gradle多模塊項(xiàng)目依賴(lài)管理方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04Spring boot實(shí)現(xiàn)一個(gè)簡(jiǎn)單的ioc(1)
這篇文章主要為大家詳細(xì)介紹了Spring boot實(shí)現(xiàn)一個(gè)簡(jiǎn)單的ioc,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-04-04spring.mvc.servlet.load-on-startup屬性方法源碼解讀
這篇文章主要介紹了spring.mvc.servlet.load-on-startup的屬性方法源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12Java向Runnable線程傳遞參數(shù)方法實(shí)例解析
這篇文章主要介紹了Java向Runnable線程傳遞參數(shù)方法實(shí)例解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-06-06Springboot處理CORS跨域請(qǐng)求的三種方法
這篇文章主要介紹了Springboot處理CORS跨域請(qǐng)求的三種方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06