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