2010-08-13 73 views
7

我有一個屬性文件的本地化:JSF 2本地化(託管bean)

foo=Bar 
title=Widget Application 

這是在faces-配置捆綁作爲一個resource-bundle

<resource-bundle> 
    <base-name>com.example.messages.messages</base-name> 
    <var>msgs</var> 
</resource-bundle> 

我可以訪問此只在使用EL的facelets視圖中很好:

<title>#{msgs.title}</title> 

但是,如果有像SQLExceptions這樣的東西,我需要能夠從託管bean寫入消息。這是所有工作也:

FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR, "There was an error saving this widget.", null); 
FacesContext.getCurrentInstance().addMessage(null, message); 

這裏的問題:我想有這些消息都來自屬性文件,這樣他們也可以根據區域設置改變。有沒有一種簡單的方法來使用注入來訪問屬性文件?

回答

16

我問的SO相當相關的問題: How to inject a non-serializable class (like java.util.ResourceBundle) with Weld

而且Seam的論壇裏面: http://seamframework.org/Community/HowToCreateAnInjectableResourcebundleWithWeld

總結: 我意識到可注射的ResourceBundle 3生產者。 首先你需要一個FacesContextProducer。我從Seam 3 Alpha來源獲取了一個。

public class FacesContextProducer { 
    @Produces @RequestScoped 
    public FacesContext getFacesContext() { 
     FacesContext ctx = FacesContext.getCurrentInstance(); 
     if (ctx == null) 
     throw new ContextNotActiveException("FacesContext is not active"); 
     return ctx; 
    } 
} 

然後,您需要一個LocaleProducer,它使用FacesContextProducer。我也從Seam 3 Alpha拿下了它。

public class FacesLocaleResolver { 
    @Inject 
    FacesContext facesContext; 

    public boolean isActive() { 
     return (facesContext != null) && (facesContext.getCurrentPhaseId() != null); 
    } 

    @Produces @Faces 
    public Locale getLocale() { 
     if (facesContext.getViewRoot() != null) 
     return facesContext.getViewRoot().getLocale(); 
     else 
     return facesContext.getApplication().getViewHandler().calculateLocale(facesContext); 
    } 
} 

現在你擁有了一切創造ResourceBundleProducer,它可以是這樣的:

public class ResourceBundleProducer { 
    @Inject  
    public Locale locale; 

    @Inject  
    public FacesContext facesContext; 

    @Produces 
    public ResourceBundle getResourceBundle() { 
    return ResourceBundle.getBundle("/messages", facesContext.getViewRoot().getLocale()); 
    } 
} 

現在你可以@Inject資源包進入你的豆類。注意它必須注入一個transient屬性,否則你會得到一個異常,抱怨ResourceBundle不可序列化。

@Named 
public class MyBean { 
    @Inject 
    private transient ResourceBundle bundle; 

    public void testMethod() { 
    bundle.getString("SPECIFIC_BUNDLE_KEY"); 
    } 
} 
0

下面是關於如何做到這一點的例子: http://www.laliluna.de/articles/javaserver-faces-message-resource-bundle-tutorial.html

你想看看在ResourceBundle.getBundle()一部分。

問候, 拉爾斯

+0

我看到了這個,當我GOOGLE了它。然而,有沒有一種更優雅的方式讓容器使用@Resource(「#{msgs}」)或類似的東西來注入?我想,因爲我使用的是CDI,所以我可以創建一個'@ MessageBundle'的生產者,然後傳回一個'Properties'對象... – 2010-08-13 15:15:08

+0

我在最後一個項目中使用了這種方法 - 我們有DB錯誤的相同問題。如果這個問題仍然沒有解決,我可以在下週三看看這個老源。 – Lars 2010-08-13 15:27:05

+0

你是對的,這是一個有效的方法。我只是想知道是否有辦法更優雅地做到這一點。我可以使用CDI來注入它。如果沒有內置的註釋,這將起作用。 – 2010-08-13 15:44:10

2

您可以單獨使用JSF來執行此操作。

首先在您的支持bean上定義託管屬性。在JSF配置中,可以將託管屬性的值設置爲引用資源包的EL表達式。

我使用Tomcat 6做了類似如下的事情。唯一需要注意的是,您無法從支持bean的構造函數中訪問此值,因爲JSF尚未初始化它。如果在bean的生命週期中早期需要該值,則在初始化方法上使用@PostConstruct

<managed-bean> 
    ... 
    <managed-property> 
    <property-name>messages</property-name> 
    <property-class>java.util.ResourceBundle</property-class> 
    <value>#{msgs}</value> 
    </managed-property> 
    ... 
</managed-bean> 

<application> 
    ... 
    <resource-bundle> 
    <base-name>com.example.messages.messages</base-name> 
    <var>msgs</var> 
    </resource-bundle> 
    ... 
</application> 

這樣做的好處是使您的支持bean方法更少地依賴於演示技術,因此它應該更容易測試。它還將您的代碼從類似於捆綁軟件的名稱等細節中分離出來。

使用Mojarra 2.0.4-b09進行的一些測試在用戶更改區域設置中會話時確實顯示出小的不一致性。頁面內的EL表達式使用新的區域設置,但是輔助bean沒有被賦予新的ResourceBundle引用。爲了保持一致,您可以在EL表達式中使用bean屬性值,例如使用#{backingBean.messages.greeting}代替#{msgs.greeting}。然後頁面EL和輔助bean將始終使用會話開始時處於活動狀態的語言環境。如果用戶在會話中間切換語言環境並獲取新消息,則可以嘗試製作請求範圍的bean,併爲會話bean和資源包提供引用。

+0

+1到目前爲止最簡單的解決方案 – klonq 2012-02-29 09:54:17

+2

在JSF2中,您可以使用'@ManagedProperty(「#{msgs}」)private ResourceBundle msgs;'(with setter)。 – BalusC 2012-09-05 11:35:25

0

這是一個老問題,但我增加了另一種方式來做到這一點。我正在尋找別的東西,並且碰到了這個。這裏的方法似乎令人費解,因爲所有那些困難都沒有引起我的注意。如果你問我的話,就會閃閃發光,因爲它很漂亮。

給定一個文件:

/com/full/package/path/to/messages/errormessages.properties 

裏面的檔案:

SOME_ERROR_STRING=Your App Just Cratered 

我創建了一個「的getBundle()」方法,因爲我喜歡抓運行,並添加一個有意義的消息,所以我會了解它來自哪裏。不難,如果您因爲某種原因而隨意使用屬性文件並且沒有正確更新所有內容,可以提供幫助。我有時會把它變成私人的,因爲它有時是班級內的輔助方法(對我來說)。這樣可以避免有意義代碼中的try catch混亂。

如果您有關於組織的其他想法,使用文件的完整路徑允許您將它放在默認位置/目錄以外的某個位置。

public/private ResourceBundle getMessageResourceBundle(){ 
    String messageBundle = "com.full.package.path.to.messages.errormessages"; 
    ResourceBundle bundle = null; 
    try{ 
     bundle = ResourceBundle.getBundle(messageBundle); 
    }catch(MissingResourceException ex){ 
     Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, 
        "Unable to find message bundle in XYZ Class", ex); 
     throw ex; 
    } 

} 

public void doSomethingWithBundle(){ 

    ResourceBundle bundle = getMessageResourceBundle(); 
    String someString = bundle.getString("SOME_ERROR_STRING"); 
    ... 
}