2010-08-13 7 views
7

Ho un file di proprietà per la localizzazione:JSF 2 localizzazione (bean gestito)

foo=Bar 
title=Widget Application 

Questo è legato a come resource-bundle nella faces-config:

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

posso accedere a questo solo bene nei facelets vista utilizzando EL:

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

Tuttavia, se ci sono cose come SQLExceptions, ho bisogno essere in grado di scrivere messaggi dal bean gestito. Questo è tutto il funzionamento anche:

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

Qui è il problema: voglio avere quei messaggi provenire dalle proprietà del file in modo che anche loro, possono essere modificati in base alle impostazioni internazionali. C'è un modo semplice per accedere al file delle proprietà usando l'iniezione?

risposta

16

ho fatto una domanda del tutto relativa su SO: How to inject a non-serializable class (like java.util.ResourceBundle) with Weld

E all'interno del Forum Seam: http://seamframework.org/Community/HowToCreateAnInjectableResourcebundleWithWeld

Riassumendo: mi sono reso conto di una ResourceBundle iniettabili con 3 produttori. Per prima cosa è necessario un prodotto FacesContext. Ho preso quello dalle sorgenti Alpha di Seam 3.

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

Quindi è necessario un LocaleProducer, che utilizza FacesContextProducer. L'ho preso anche da 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); 
    } 
} 

Ora avete tutto per creare un ResourceBundleProducer, che può apparire così:

public class ResourceBundleProducer { 
    @Inject  
    public Locale locale; 

    @Inject  
    public FacesContext facesContext; 

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

Ora è possibile @Inject la ResourceBundle nei vostri fagioli. Fai attenzione che deve essere iniettato in un attributo transitorio, altrimenti riceverai un'eccezione lamentando che ResourceBundle non è serializzabile.

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

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

Ecco un esempio di come fare questo: http://www.laliluna.de/articles/javaserver-faces-message-resource-bundle-tutorial.html

Volete avere uno sguardo alla parte ResourceBundle.getBundle().

Saluti, Lars

+0

Ho visto questo quando ho cercato su Google. Tuttavia, c'è un modo più elegante di iniettare il contenitore usando @Resource ("# {msgs}") o qualcosa del genere? Suppongo che, dal momento che sto usando CDI, potrei creare un produttore di '@ MessageBundle' o qualcosa del genere, e quindi restituire un oggetto' Properties' ... –

+0

Ho usato questo approccio in uno dei nostri ultimi progetti - avevamo il problema identico con gli errori di DB. Posso dare un'occhiata alla vecchia fonte di mercoledì la prossima settimana se questo è ancora irrisolto. – Lars

+0

Hai ragione che è un modo valido per farlo. Mi stavo chiedendo se c'era un modo per farlo in modo più elegante. Posso semplicemente usare CDI per iniettarlo. Funzionerà se non ci sono annotazioni incorporate. –

2

È possibile farlo solo con JSF.

Iniziare definendo una proprietà gestita sul bean di supporto. Nella configurazione JSF, è possibile impostare il valore della proprietà gestita su un'espressione EL che fa riferimento al gruppo di risorse.

Ho realizzato qualcosa come il seguente utilizzando Tomcat 6. L'unica avvertenza è che non è possibile accedere a questo valore dal costruttore del bean di backing, poiché JSF non lo ha ancora inizializzato.Utilizzare @PostConstruct su un metodo di inizializzazione se il valore è necessario all'inizio del ciclo di vita del bean.

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

Questo ha il vantaggio di rendere i vostri metodi di backing bean meno dipendente dalla tecnologia di presentazione, quindi dovrebbe essere più facile da testare. Inoltre, disaccoppia il tuo codice da dettagli come il nome dato al pacchetto.

Alcuni test che utilizzano Mojarra 2.0.4-b09 mostrano una piccola incoerenza quando un utente cambia locale a metà sessione. Le espressioni EL in-page utilizzano le nuove impostazioni internazionali, ma al bean di supporto non viene assegnato il nuovo riferimento ResourceBundle. Per renderlo coerente, è possibile utilizzare il valore della proprietà del bean nelle espressioni EL, ad esempio utilizzando #{backingBean.messages.greeting} al posto di #{msgs.greeting}. Quindi la pagina EL e il bean di supporto utilizzano sempre le impostazioni locali che erano attive all'avvio della sessione. Se gli utenti avevano per passare a metà sessione localmente e ottenere i nuovi messaggi, si potrebbe provare a creare un bean con ambito di richiesta e fornirgli riferimenti sia al bean di sessione che al bundle di risorse.

+0

+1 Di gran lunga la soluzione più semplice – klonq

+2

In JSF2 puoi semplicemente usare '@ManagedProperty (" # {msgs} ") private ResourceBundle msgs;' (con setter). – BalusC

0

Questa è una vecchia domanda ma sto aggiungendo un altro modo per farlo. Stavo cercando qualcos'altro e mi sono imbattuto in questo. I metodi qui sembrano tutti contorti per qualcosa che non mi sembra tanto difficile. Giocare con glitter perché è carino se me lo chiedi.

Dato un file:

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

All'interno del file:

SOME_ERROR_STRING=Your App Just Cratered 

ho creare un metodo "getBundle()" dato che mi piace prendere il tempo di esecuzione e aggiungere un messaggio significativo quindi mi capire da dove viene. Non è difficile e può essere d'aiuto se si ottiene il capriccio di giocare con i file delle proprietà per qualche motivo e di non aggiornare tutto correttamente. A volte lo faccio privato perché a volte è un metodo di aiuto all'interno di una classe (per me). Ciò mantiene il trambusto di cattura fuori dal codice significativo.

L'utilizzo del percorso completo del file consente di posizionarlo in un luogo diverso dalla directory/posizione predefinita se si hanno altre idee sull'organizzazione.

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"); 
    ... 
}