基本
java.util.ResourceBundle定義了用于訪問Java中翻譯的標準化方法。 它們包含特定于語言環境的資源。 資源束屬于其成員具有相同基本名稱的族,但是其名稱還具有標識
他們的語言環境。 族中的每個資源束都包含相同的項目,但是這些項目已針對該資源束所代表的語言環境進行了翻譯。 這些是鍵/值對。 這些鍵唯一地標識捆綁軟件中特定于語言環境的對象。
最基本的示例使用以下知識:
Messages.properties
Messages_de.properties Messages_en.properties
如果您需要在應用程序中查詢包,則只需調用
ResourceBundle bundle = ResourceBundle.getBundle("Messages");
方法并查詢返回的包:
bundle.getString("welcome.message");
如果您想在此處使用哪種語言環境,那是對的。 String構造函數隱式使用Locale.getDefault()解析語言。 那可能不是您想要的。 所以你應該ResourceBundle bundle =
ResourceBundle.getBundle("Messages", locale);
檢索捆綁軟件后,您將無法設置語言環境。 每個ResourceBundle都有一個定義的語言環境。
命名的東西
?
關于命名的一些想法。 用其內容命名捆綁屬性。 您可以通過簡單地將它們命名為“消息”和“錯誤”等來采用更通用的方式。但是,每個子系統或組件也可以具有捆綁軟件。 無論您需要什么。 要維護內容,要輸入大量條目并不容易。 因此,任何類型的上下文拆分都會使開發人員感到高興。 捆綁軟件屬性文件等效于類。 相應地命名。 進一步,您應該找到一個用于命名密鑰的通用系統。 根據為屬性文件選擇的拆分,還可能在密鑰中引入某種子系統或組件名稱空間。 頁面前綴也是可能的。 明智地考慮一下,并加以解決。 您的目標是盡量減少密鑰重復。
封裝
?
如您所見,您經常使用包的字符串表示形式。 這些實際上是文件名(或更好的類名),您可以通過一個簡單的枚舉來更好地封裝所有內容:
public enum ResourceBundles {MESSAGES("Messages"),ERRORS("Errors");private String bundleName; ResourceBundles(String bundleName) {this.bundleName = bundleName;}public String getBundleName() {return bundleName;}@Overridepublic String toString() {return bundleName;}
}
有了這個你就可以寫
ResourceBundle bundle = ResourceBundle.getBundle(MESSAGES.getBundleName());
Java Server Faces和ResourceBundle
?
要在基于jsf的應用程序中使用資源包,您只需在faces-config.xml中定義它們,并使用xhtml文件中的快捷方式。
<resource-bundle>
<base-name>Messages</base-name>
<var>msgs</var>
<h:outputLabel value="#{msgs['welcome.general']}" />
JSF負責其余的工作。 那么參數替換呢? 考慮如下的鍵值對:
welcome.name=Hi {0}! How are you?
您可以通過f:param標簽傳遞參數:
<h:outputFormat value="#{msgs['welcome.name']}"><f:param value="Markus" /></h:outputFormat>
要更改語言,您必須為當前的FacesContext實例設置特定的語言環境。 最好通過值更改偵聽器執行此操作:
public void countryLocaleCodeChanged(ValueChangeEvent e) {String newLocaleValue = e.getNewValue().toString();//loop country map to compare the locale codefor (Map.Entry<String, Object> entry : countries.entrySet()) {if (entry.getValue().toString().equals(newLocaleValue)) {FacesContext.getCurrentInstance().getViewRoot().setLocale((Locale) entry.getValue());}}}
EJB中的資源包
?
JSF顯然很容易集成。 在EJB中使用這些捆綁包怎么辦? 基本上是一樣的。 您可以使用相同的機制來使用和使用捆綁包。 您應該記住一件事。 您可能不想始終使用默認語言環境。 因此,您必須找到一種從UI向下傳遞語言環境的方法。 如果您想通過@Produces批注@Injecting MessageBundle,則必須考慮多次。 尤其是在使用@Stateless EJB時。 這些實例將合并,您必須將語言環境傳遞給需要了解當前語言環境的任何業務方法。 通常,您可以使用參數對象或某種用戶會話配置文件來執行此操作。 不要將語言環境全部添加為方法簽名。
來自數據庫的資源包
?
在大多數情況下,我看到您需要從數據庫中提取密鑰。 鑒于ResourceBundle的內部工作原理(每個語言環境一個“類”),您最終不得不在自己的ResourceBundle實現中實現邏輯。 您在網絡上找到的大多數示例都是通過重寫handleGetObject(String key)方法來實現的。 我不喜歡這種方法,尤其是因為我們有一個更好的方法來使用ResourceBundle.Control機制。 現在,您可以覆蓋newBundle()方法并返回自己的ResourceBundle實現。 您所要做的就是將自己的Control設置為DatabaseResourceBundle的父級:
public DatabaseResourceBundle() {setParent(ResourceBundle.getBundle(BUNDLE_NAME,FacesContext.getCurrentInstance().getViewRoot().getLocale(), new DBControl()));}
DBControl返回MyResourceBundle,它是一個ListResourceBundle:
protected class DBControl extends Control {@Overridepublic ResourceBundle newBundle(String baseName, Locale locale, String format, ClassLoader loader, boolean reload)throws IllegalAccessException, InstantiationException, IOException {return new MyResources(locale);}/*** A simple ListResourceBundle*/protected class MyResources extends ListResourceBundle {private Locale locale;/*** ResourceBundle constructor with locale** @param locale*/public MyResources(Locale locale) {this.locale = locale;}@Overrideprotected Object[][] getContents() {TypedQuery<ResourceEntity> query = _entityManager.createNamedQuery("ResourceEntity.findForLocale", ResourceEntity.class);query.setParameter("locale", locale);List<ResourceEntity> resources = query.getResultList();Object[][] all = new Object[resources.size()][2];int i = 0;for (Iterator<ResourceEntity> it = resources.iterator(); it.hasNext();) {ResourceEntity resource = it.next();all[i] = new Object[]{resource.getKey(), resource.getValue()};values.put(resource.getKey(), resource.getValue());i++;}return all;}}}
如您所見,這由一個entitymanager和一個簡單的ResourceEntity作為后盾,該ResourceEntity具有構建不同捆綁軟件所需的所有字段和NamedQueries。
@Id@GeneratedValue(strategy = GenerationType.AUTO)private Long id;@Column(name = "i18n_key")private String key;@Column(name = "i18n_value")private String value;@Column(name = "i18n_locale")private Locale locale;
通過將捆綁包放入私有Map <String,String>值= new HashMap <String,String>(); 在首次構建捆綁包之后,您還可以使用一種很好的方法來緩存結果。
這仍然不是最好的解決方案,因為ResourceBundles具有緩存的方式。 但我稍后可能會更詳細地探討這一點。 到現在為止,此捆綁包將被永久緩存(或至少直到下一次重新部署為止)。
改寫為語言切換
?
最后要提到的是,您還可以在此處添加一些精美的插件。 如果您已經有了JSF語言切換魔術,則可以輕松地將ocpsoft的重寫添加到您的應用程序中。 這是一種將網址中的語言編碼的簡單方法,例如http://yourhost.com/Bundle-Provider-Tricks/en/index.html 您要做的就是通過添加兩個簡單的依賴關系來向游戲添加重寫:
<dependency><groupId>org.ocpsoft.rewrite</groupId><artifactId>rewrite-servlet</artifactId><version>1.1.0.Final</version></dependency><dependency><groupId>org.ocpsoft.rewrite</groupId><artifactId>rewrite-integration-faces</artifactId><version>1.1.0.Final</version></dependency>
重寫需要您添加自己的ConfigurationProvider,這是保存重寫規則的中心位置。 執行以下操作:
public class BundleTricksProvider extends HttpConfigurationProvider {@Overridepublic Configuration getConfiguration(ServletContext context) {return ConfigurationBuilder.begin()// Locale Switch.addRule(Join.path("/{locale}/{page}.html").to("/{page}.xhtml").where("page").matches(".*").where("locale").bindsTo(PhaseBinding.to(El.property("#{languageSwitch.localeCode}")).after(PhaseId.RESTORE_VIEW)));}@Overridepublic int priority() {return 10;}
}
接下來是將一個名為“ org.ocpsoft.rewrite.config.ConfigurationProvider”的文件添加到您的META-INF / services文件夾,并在其中放置您的ConfigurationProvider實現的標準名稱。 最后要調整的是LanguageSwitch bean中的邏輯。 重寫不能觸發ValueChangeEvent(據我所知:)),因此您必須在調用setter時添加一些魔術來更改Locale。 就是這樣..非常簡單!
參考:來自JCG合作伙伴 Markus Eisele的Resource Bundle技巧和最佳實踐 ,位于Enterprise Software Development with Java博客上。
翻譯自: https://www.javacodegeeks.com/2012/09/resource-bundle-tricks-and-best.html