Java程序的多國(guó)語言

2024-03-07 18:37 更新

定義Locale

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ī)則

資源文件的基本命名規(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ó)際化資源

公有國(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è)。

  • Default是一種特殊的BundleName,當(dāng)我們需要訪問名為Default的Bundle中資源項(xiàng)時(shí)可以不指定BundleName。
  • 資源文件并非只能放在SearchPath的根路徑中,您也可以把它們放到子目錄中。例如當(dāng)你把一個(gè)Test.zh_CN.properties放置在SearchPath對(duì)應(yīng)目錄的名為core的子目錄中時(shí),它的BundleName就應(yīng)該是core.Test。

每個(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ó)際化資源

為了讓國(guó)際化的使用更加方便,Dorado允許下面的幾種對(duì)象擁有自己的私有國(guó)際化資源:

  • Model配置文件?。∨cModel配置文件存放在同一路徑,且主文件名相同的資源文件被Dorado視為Model配置文件的私有國(guó)際化資源。
  • View配置文件 - 與View配置文件存放在同一路徑,且主文件名相同的資源文件被Dorado視為View配置文件的私有國(guó)際化資源。
  • JavaBean?。∨cJava文件存放在同一路徑,且主文件名相同的資源文件被Dorado視為該JavaBean的私有國(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ó)際化資源

使用國(guó)際化資源主要有以下兩種方式——EL表達(dá)式、ResourceManager

EL表達(dá)式

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)。

ResourceManager

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ó)際化資源。

國(guó)際化資源的自動(dòng)注入

想象一下,如果我們的某個(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的方法此處不表。

  • 在注入的過程中如果Dorado發(fā)現(xiàn)對(duì)象的目前屬性的內(nèi)容不是空,那么Dorado會(huì)跳過該資源的注入。也就是說直接定義在View配置文件的內(nèi)容的優(yōu)先級(jí)是高于資源自動(dòng)注入的。
  • 另外還有一個(gè)需要提及的規(guī)則是,#后面的內(nèi)容其實(shí)并不僅僅表示控件的id,也可以是DataType的名稱。具體代表控件還是DataType,將有Dorado在運(yùn)行時(shí)自動(dòng)判斷。
  • 如果您需要為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"));
        }
        ... ...
    }
}
以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)