com.bstek.dorado.core.resource.LocaleResolver接口用于告知Dorado系統(tǒng)當(dāng)前應(yīng)使用什么地區(qū)和語種,Dorado提供的默認(rèn)配置如下:
<bean id="dorado.localeResolver" class="com.bstek.dorado.view.resource.SpringLocaleResolverAdapter">
<property name="springLocaleResolver">
<bean class="org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver" />
</property>
</bean>
該配置通過Spring中的org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver來確定地區(qū)和語種。您也可以通過自定義com.bstek.dorado.core.resource.LocaleResolver的實(shí)現(xiàn)類來改變?cè)械倪壿嫞碌膶?shí)現(xiàn)類只要通過下面的方式配置到 home:context.xml 既可生效。
<bean id="dorado.localeResolver" class="xxx.MyLocaleResolver"/>
在自定義的LocaleResolver中實(shí)現(xiàn)resolveLocale方法:
public Locale resolveLocale() throws Exception {
//創(chuàng)建并返回符合條件的java.util.Locale
}
資源文件的基本命名規(guī)則如下:
主文件名[.Locale].properties
假設(shè)當(dāng)前系統(tǒng)的Locale是zh_CN,那么Dorado會(huì)首先查找?guī)в?zh_CN.properties后綴的資源文件,如果系統(tǒng)中并不存在該文件Dorado會(huì)進(jìn)一步查找只帶有.properties后綴的資源文件。
公有國(guó)際化資源是指那些整個(gè)系統(tǒng)的各個(gè)功能模塊重用的國(guó)際化資源,公有國(guó)際化資源與私有國(guó)際化資源的區(qū)別主要體現(xiàn)在緩存管理。出于節(jié)省系統(tǒng)內(nèi)存的考慮,我們建議您只把那些確實(shí)可能被重用的資源項(xiàng)放入公有國(guó)際化資源文件。 公有國(guó)際化資源文件應(yīng)被放置在搜索路徑(SearchPath)下,系統(tǒng)中可以存在1到多個(gè)SearchPath。添加SearchPath的方式如下:
<bean parent="dorado.globalResourceSearchPathRegister">
<property name="searchPath" value="home:resources/" />
</bean>
每一組資源文件被稱為一個(gè)資源束(Bundle),即那些主文件名相同但Locale不同的資源文件。主文件名即被認(rèn)為是資源束(Bundle)的名稱(BundleName)。例如當(dāng)我們要使用一個(gè)名為Test的Bundle時(shí),Dorado將依次到各SearchPath中根據(jù)BundleName(即Test)和通過LocaleResolver確定的Locale尋找匹配的資源文件,并直接使用找到的第一個(gè)。
每個(gè)Bundle中可以包含很多個(gè)資源項(xiàng),一個(gè)資源項(xiàng)通常就是一段文本。另外,我們也可以在這段文本中植入一些參數(shù),例如:
newMessageNotify=您收到了%d條新的消息!
關(guān)于此處參數(shù)的具體用法請(qǐng)參閱:http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/String.html?中的 String.format() 方法。
為了讓國(guó)際化的使用更加方便,Dorado允許下面的幾種對(duì)象擁有自己的私有國(guó)際化資源:
在Dorado推薦的開發(fā)方式中我們常常把一個(gè)View相關(guān)的攔截器方法、監(jiān)聽方法都定義在一個(gè)與View配置文件同名同位置的JavaBean中。在這種情況下,該View配置文件和JavaVean可以共享同一份私有國(guó)際化資源。這是完全符合通常的理解習(xí)慣的,同時(shí)也恰恰體現(xiàn)了這種開發(fā)方式的優(yōu)勢(shì)。
對(duì)于Model配置文件和View配置文件的私有國(guó)際化資源,Dorado還為他們提供一種非常重要的功能——資源的自動(dòng)注入,關(guān)于此功能介紹見本文后面的“國(guó)際化資源的自動(dòng)注入”一節(jié)。
使用國(guó)際化資源主要有以下兩種方式——EL表達(dá)式、ResourceManager
EL表達(dá)式主要用于在Model或View的配置文件中引用一段國(guó)際化資源。以這一段EL表達(dá)式為例:
${res.PageTitle}
假設(shè)我們是在View配置文件中定義了上面的這樣一段EL表達(dá)式。Dorado在處理時(shí)會(huì)首先到該View配置文件的私有國(guó)際化資源中查找名為“PageTitle”的資源項(xiàng),如果找到則直接使用它的值。如果沒找到,Dorado將繼續(xù)到名為“Default”的國(guó)際化資源束查找名為“PageTitle”的資源項(xiàng)(我們?cè)谇懊嫣岬竭^Default是一種默認(rèn)的BundleName)。 下面的兩種寫法與上面的寫法完全的等價(jià)。
${res["PageTitle"]}
${res.get("PageTitle")}
下面是一個(gè)稍微復(fù)雜了一點(diǎn)的例子:
${res["Test/PageTitle"]}
對(duì)于這段表達(dá)式,Dorado在處理時(shí)會(huì)首先到該View配置文件的私有國(guó)際化資源中查找名為“Test/PageTitle”的資源項(xiàng),如果找到則直接使用它的值。如果沒找到,Dorado將繼續(xù)到名為“Test”的國(guó)際化資源束查找名為“PageTitle”的資源項(xiàng)。
從這里我們可以知道,Dorado利用“/”來分隔bundleName和key的。這也提醒了我們,不要在定義資源項(xiàng)的key時(shí)使用“/”字符,否則非常容易一起混淆。
如果我們要使用的資源項(xiàng)是帶有參數(shù)的,可以按照下面的方法來定義EL表達(dá)式:
${res.get("NewMessageNotify", 5)} // 傳入一個(gè)參數(shù)
${res.get("Test/ResourceWith3Args", "arg1", 17, "arg3")} // 傳入3個(gè)參數(shù)
如果資源文件是放在SearchPath的子目錄中,例如當(dāng)你把一個(gè)Test.zh_CN.properties放置在SearchPath對(duì)應(yīng)目錄的名為core的子目錄中時(shí),它的BundleName就應(yīng)該是core.Test。可以按照下面的方法來定義EL表達(dá)式:
${res["core.Test/PageTitle"]}
對(duì)于這段表達(dá)式,Dorado在處理時(shí)會(huì)到SearchPath的core子目錄中名為“Test”的國(guó)際化資源束查找名為“PageTitle”的資源項(xiàng)。
com.bstek.dorado.core.resource.ResourceManager通常用于訪問JavaBean的私有國(guó)際化資源,下面是一段簡(jiǎn)單的例子:
@Component
public class MyBean {
@DataProvider
public Collection<Employee> findEmployeesByNamePattern(String pattern) throws Exception {
if (StringUtils.isEmpty(pattern)) {
// 獲得當(dāng)前Class的私有國(guó)際化資源束
ResourceManager resourceManager = ResourceManagerUtils.get(getClass());
throw new IllegalArgumentException(resourceManager.getString("PatternUndefined"));
}
... ...
}
}
ResourceManager中處理path的過程與EL表達(dá)式中的過程一致,這里不做累述。更的具體用法請(qǐng)參考Dorado的JavaDoc。
需要特別指出的是,EL表達(dá)式和ResourceManager并不只是簡(jiǎn)單的訪問私有國(guó)際化資源,通過他們都可以訪問公有國(guó)際化資源。只是在查找順序方面,私有國(guó)際化資源的優(yōu)先級(jí)高于公有國(guó)際化資源。
想象一下,如果我們的某個(gè)View中有一個(gè)包含了50個(gè)PropertyDef的DataType,而這50個(gè)PropertyDef的caption和tip屬性都需要進(jìn)行國(guó)際化。那么我們可能需要在這個(gè)View中填寫100此EL表達(dá)式,這么做無疑是令人崩潰的一件事!正是考慮到了這樣的使用場(chǎng)景,Dorrado為國(guó)際化資源提供了主動(dòng)注入的功能。 自動(dòng)注入功能目前適用于Model和View這兩種文件,基本的做法是只要按照特定的規(guī)則在私有國(guó)際化資源文件中定義資源項(xiàng),這些資源字符串就會(huì)在運(yùn)行時(shí)自動(dòng)的被注入到相應(yīng)的屬性中,不需要額外的定義EL表達(dá)式去引用。 所以支持自動(dòng)注入的資源項(xiàng)都必須以“#”作為其鍵值的開頭。以View配置文件為例,假設(shè)我們?cè)谄渌接匈Y源文件定義了如下的資源項(xiàng):
\#buttonSave=保存
由于#在Java的.properties文件中表示注釋,所以我們需要在第一個(gè)“#”字符之前增加“\”。Dorado在遇到這樣的資源項(xiàng)時(shí)會(huì)把“#”后面的內(nèi)容認(rèn)作View中某控件的id,并且自動(dòng)將“保存”設(shè)置到該控件(實(shí)質(zhì)為Button)的caption屬性中。 看到這里您可能會(huì)產(chǎn)生一個(gè)疑問,為什么“保存”被注入到了caption屬性中而不是其他的屬性?原因是在進(jìn)行自動(dòng)注入時(shí),如果沒有顯示的指定的屬性名,Dorado默認(rèn)會(huì)按照下面的規(guī)則來確定屬性,首先查找控件是否存在caption屬性,如何存在則直接設(shè)置caption屬性,如果沒有則進(jìn)一步查找label屬性,進(jìn)而查找title屬性。此查找規(guī)則可以被定義在具體Class上的Annotation改變,定義這種Annotation的方法此處不表。
如果您需要為View配置文件中的View對(duì)象本身注入國(guó)際化資源,那么可以通過“#view”完成,因?yàn)閂iew對(duì)象不允許我們?yōu)槠涠xid屬性。
下面的更多的資源注入的實(shí)例:
\#buttonSave.tip=保存當(dāng)前記錄 <-- 注入到id為buttonSave的按鈕的tip屬性
\#view=代碼維護(hù) <-- 注入到View的title屬性
\#Employee.#address=地址 <-- 查找名為Employee的DataType,并注入到其中address屬性描述的caption屬性中
\#Employee.#address.tip=請(qǐng)輸入您的地址 <-- 注入到上面屬性描述的tip屬性中
\#gridEmployee.#comment=備注 <-- 查找id為gridEmployee的DataGrid,并注入到其中comment列的caption屬性中
在某些系統(tǒng)中,用戶可能不希望以.properties文件的形式來管理國(guó)際化資源。例如將所有的國(guó)際化資源保存是數(shù)據(jù)庫中就是一種比較常見的場(chǎng)景。對(duì)于這種場(chǎng)景,我們可以對(duì)Dorado的公有國(guó)際化資源機(jī)制進(jìn)行一些擴(kuò)展來引入這些數(shù)據(jù)庫中的資源項(xiàng)。 要引入外部的資源,首先您需要定義一個(gè)com.bstek.dorado.core.resource.ResourceBundle的實(shí)現(xiàn)類,代碼大致如下:
public class DBResourceBundle implements ResourceBundle {
public String getString(String key, Object... args) throws Exception {
// 這里您要做的就是讀取自己的數(shù)據(jù)庫,并返回與key匹配的資源項(xiàng)的內(nèi)容。
// 強(qiáng)烈建議您在此處盡心一些緩存方面的處理,而不是每次調(diào)用該方法都讀取一次數(shù)據(jù)庫。
// 否則這里很可能會(huì)成為整個(gè)系統(tǒng)的性能瓶頸。
}
}
然后您需要擴(kuò)展GlobalResourceBundleManager,代碼大致如下:
public class MyGlobalResourceBundleManager extends DefaultGlobalResourceBundleManager {
@Override
protected ResourceBundle doGetBundle(String bundleName, Locale locale)
throws Exception {
if ("DB".equals(bundleName)) {
// 如果您在此處攔截了Default,那么今后訪問來自DBResourceBundle中的資源時(shí),您就可以不必指定bundleName。
// 如果您在此處攔截了所有的bundleName,那么系統(tǒng)原有的公有國(guó)際化資源就相當(dāng)于被完全屏蔽了,
// 那樣的話建議您直接繼承GlobalResourceBundleManagerSupport類
return new DBResourceBundle();
} else {
return super.doGetBundle(bundleName, locale);
}
}
}
最后將您自己定義的GlobalResourceBundleManager配置到Dorado中即告完成,例如在 home:context.xml 中添加如下配置:
<bean id="dorado.globalResourceBundleManager" class="xxx.MyGlobalResourceBundleManager">
<property name="cache" ref="dorado.globalResourceCache" />
</bean>
如果按照本例設(shè)定bundleName為"DB",則EL表達(dá)式使用時(shí)的參考范例:
${res["DB/PageTitle"]}
ResourceManager的用法:
@Component
public class MyBean {
@DataProvider
public Collection<Employee> findEmployeesByNamePattern(String pattern) throws Exception {
if (StringUtils.isEmpty(pattern)) {
// 獲得DBResourceBundle國(guó)際化資源束
ResourceManager resourceManager = ResourceManagerUtils.get("DB");
throw new IllegalArgumentException(resourceManager.getString("PatternUndefined"));
}
... ...
}
}
更多建議: