SeamFramework.orgCommunity Documentation
Seam rende facile la costruzione di applicazioni internazionali. Prima di tutto verranno percorse le varie fasi necessarie per rendere internazionale e tradotta un'applicazione. In seguito si darà un'occhiata al modo in cui Seam gestisce i gruppi di stringhe associate ai componenti (resource bundle).
Un'applicazione JEE consiste di molti componenti ed ognuno di essi deve essere configurato opportunamente affinché l'applicazione venga tradotta.
Partendo dalla base, il primo passo è assicurarsi che il server e il client del database utilizzino la codifica di caratteri corretta per la traduzione. Di solito si vorrà utilizzare UTF-8. Come fare questo non è oggetto di questa guida.
Per essere sicuri che l'application server riceva i parametri delle richieste dai client nella codifica corretta occorre configurare il connettore Tomcat. Se si usa Tomcat o JBoss AS, aggiungere l'attributo URIEncoding="UTF-8"
alla configurazione del connettore. Per JBoss AS 4.2 modificare ${JBOSS_HOME}/server/(default)/deploy/jboss-web.deployer/server.xml
:
<Connector port="8080" URIEncoding="UTF-8"/>
C'è un'alternativa che è probabilmente migliore. E' possibile dire a JBoss AS che la codifica dei parametri della richiesta deve essere ricavata dalla richiesta:
<Connector port="8080" useBodyEncodingForURI="true"/>
Ci sarà bisogno di tradurre le stringhe per tutti i messaggi dell'applicazione (per esempio le etichette dei campi nelle pagine). In primo luogo occorre assicurarsi che il resource bundle sia codificato utilizzando la giusta codifica di carattere. Per default viene usato ASCII. Benché la codifica ASCII sia sufficiente per molte lingue, essa non fornisce i caratteri per tutte le lingue.
I resource bundles devono essere creati in ASCII, oppure devono utilizzare una notazione Unicode per rappresentare i caratteri Unicode. Poiché un file .properties
non viene compilato in byte-code, non c'è modo di dire alla JVM quale codifica caratteri utilizzare. Perciò occorre usare caratteri ASCII oppure usare la notazione Unicode per i caratteri che non fanno parte dell'insieme ASCII. E' possibile rappresentare un carattere Unicode in un file Java usando la notazione \uXXXX, dove XXXX è la rappresentazione esadecimale del carattere.
E' possibile scrivere la traduzione delle etichette (Sezione 16.3, «») nei resource bundles con la codifica del proprio sistema e poi convertire il contenuto del file nel formato con le notazioni Unicode attraverso lo strumento native2ascii
fornito con JDK. Questo strumento converte un file scritto nella codifica originale in uno dove i caratteri non-ASCII sono rappresentati come sequenze di notazioni Unicode.
L'uso di questo strumento è descritto qui per Java 5 oppure qui per Java 6. Ad esempio, per convertire un file da UTF-8:
$ native2ascii -encoding UTF-8 messages_cs.properties > messages_cs_escaped.properties
Occorre essere sicuri che le pagine mostrino i dati tradotti e i messaggi utilizzando il corretto insieme di caratteri e che anche i dati inviati usino usino la codifica corretta.
Per impostare la codifica dei caratteri per le pagine occorre utilizzare la tag <f:view locale="cs_CZ"/>
(in questo modo diciamo a JSF di usare il locale Ceco). Si può voler modificare la codifica del documento XML stesso se si vuole includere stringhe tradotte all'interno dell'XML. Per fare questo occorre modificare l'attributo encoding nella dichiarazione XML <xml version="1.0" encoding="UTF-8">
con il valore desiderato.
Anche JSF/Facelets dovrebbe inviare tutte le richieste utilizzando la codifica caratteri specificata, ma per essere sicuri che tutte le richieste che non specificano un valore di codifica abbiamo il valore corretto è possibile forzare la codifica delle richieste utilizzando un filtro Servlet. Questo si configura in components.xml
:
<web:character-encoding-filter encoding="UTF-8"
override-client="true"
url-pattern="*.seam" />
Ogni sessione utente registrata ha associata un'istanza di java.util.Locale
(disponibile nell'applicazione come un componente chiamato locale
). In condizioni normali non sarà necessario fare alcuna configurazione particolare per impostare la lingua. Seam delega a JSF il compito di determinare la lingua attiva:
Se c'è un linguaggio associato con la richiesta HTTP (il linguaggio del browser), e questo linguaggio è presente nella lista delle lingue gestite in faces-config.xml
, allora questa lingua verrà usata per il resto della sessione.
Altrimenti, se in faces-config.xml
è specificata una lingua di default, questa lingua verrà usata per il resto della sessione.
Altrimenti viene usata la lingua di default del server.
E' possibile impostare la lingua manualmente tramite le proprietà di configurazione di Seam org.jboss.seam.international.localeSelector.language
, org.jboss.seam.international.localeSelector.country
e org.jboss.seam.internationale.localeSelector.variant
, ma non c'è una vera buona ragione per farlo.
E' comunque utile consentire all'utente di impostare la lingua manualmente tramite l'interfaccia utente. Seam fornisce una funzionalità per sovrascrivere il linguaggio determinato dall'algoritmo descritto sopra. Tutto ciò che è necessario fare è aggiungere il seguente brano ad una form in una pagina JSP o Facelets:
<h:selectOneMenu value="#{localeSelector.language}">
<f:selectItem itemLabel="English" itemValue="en"/>
<f:selectItem itemLabel="Deutsch" itemValue="de"/>
<f:selectItem itemLabel="Francais" itemValue="fr"/>
<f:selectItem itemLabel="Italiano" itemValue="it"/>
</h:selectOneMenu>
<h:commandButton action="#{localeSelector.select}"
value="#{messages['ChangeLanguage']}"/>
Oppure, se si vuole mostrare una lista delle lingue gestite da faces-config.xml
, si può usare:
<h:selectOneMenu value="#{localeSelector.localeString}">
<f:selectItems value="#{localeSelector.supportedLocales}"/>
</h:selectOneMenu>
<h:commandButton action="#{localeSelector.select}"
value="#{messages['ChangeLanguage']}"/>
Quando l'utente seleziona una voce dal menu a discesa e poi fa click sul bottone di comando, la lingua di Seam e di JSF viene sovrascritta per il resto della sessione.
Tutto ciò porta a domandarsi dove siano definite le lingue gestite. Tipicamente nell'elemento <locale-config>
del file di configurazione JSF (/META-INF/faces-config.xml) si indica una lista di lingue per le quali si dispone dei corrispondenti resource bundle. Ad ogni modo si è imparato ad apprezzare che il meccanismo di configurazione dei componenti Seam è più completo di quello fornito in Java EE. Per questa ragione è possibile configurare le lingue gestite e la lingua di default del server usando il componente org.jboss.seam.international.localeConfig
. Per usarlo occorre prima dichiarare il namespace XML per il pacchetto international di Seam nel descrittore dei componenti Seam, quindi definire la lingua di default e le lingue gestite come segue:
<international:locale-config default-locale="fr_CA" supported-locales="en fr_CA fr_FR it_IT"/>
Ovviamente se c'è la dichiarazione che una certa lingua è gestita, sarà meglio fornire il resource bundle corrispondente! Nel prossimo capitolo si imparerà come si definiscono le etichette per una lingua specifica.
JSF gestisce l'internazionalizzazione delle etichette e del testo descrittivo nell'interfaccia utente tramite l'uso di f:loadBundle>
. Questo approccio è possibile nelle applicazioni Seam. In alternativa è possibile sfruttare i vantaggi offerti dal componente Seam messages
per mostrare label costruite tramite modelli con espressioni EL.
Seam fornisce un java.util.ResourceBundle
(disponibile all'applicazione come un org.jboss.seam.core.resourceBundle
). Occorre rendere disponibili le nostre etichette tradotte tramite questo speciale resource bundle. Per default il resource bundle usato da Seam si chiama messages
così che occorre definire le etichette in file chiamati messages.properties
, messages_en.properties
, messages_en_AU.properties
, ecc. Questi file di solito risiedono nella cartella WEB-INF/classes
.
Quindi, in messages_en.properties
:
Hello=Hello
E in messages_en_AU.properties
:
Hello=G'day
E' possibile indicare un nome diverso per il resource bundle impostando la proprietà di configurazione Seam org.jboss.seam.core.resourceLoader.bundleNames
. E' possibile persino specificare un elenco di nomi di resource bundle sui quali devono essere ricercati i messaggi (a partire dall'ultimo).
<core:resource-loader>
<core:bundle-names>
<value>mycompany_messages</value>
<value>standard_messages</value>
</core:bundle-names>
</core:resource-loader>
Se si vuole definire un messaggio solo per una particolare pagina, è possibile specificarlo in un resource bundle con lo stesso nome dell'identificativo della view JSF, omettendo il /
iniziale e l'estensione del file finale. Così è possibile mettere il nostro messaggio in welcome/hello_en.properties
se si desidera mostrare il messaggio solo in /welcome/hello.jsp
.
E' anche possibile specificare esplicitamente un nome di resource bundle in pages.xml
:
<page view-id="/welcome/hello.jsp" bundle="HelloMessages"/>
Quindi possiamo usare i messaggi definiti in HelloMessages.properties
in /welcome/hello.jsp
.
Se si definiscono le etichette utilizzando il resource bundle di Seam è possibile usarle senza dover scrivere <f:loadBundle... />
in ogni pagina. E' possibile invece scrivere semplicemente:
<h:outputText value="#{messages['Hello']}"/>
oppure:
<h:outputText value="#{messages.Hello}"/>
Ancora meglio, i messaggi stessi possono contenere espressioni EL:
Hello=Hello, #{user.firstName} #{user.lastName}
Hello=G'day, #{user.firstName}
E' possibile anche usare i messaggi nel codice:
@In private Map<String, String> messages;
@In("#{messages['Hello']}") private String helloMessage;
Il componente facesMessages
è un modo super-conveniente per mostare messaggi di conferma o di errore all'utente. La funzionalità che è stata appena descritta funziona anche per i messaggi faces:
@Name("hello")
@Stateless
public class HelloBean implements Hello {
@In FacesMessages facesMessages;
public String sayIt() {
facesMessages.addFromResourceBundle("Hello");
}
}
Questo mostrerà Hello, Gavin King
oppure G'day, Gavin
, a seconda della lingua dell'utente.
C'è anche un'istanza a livello sessione di java.util.Timezone
, chiamata org.jboss.seam.international.timezone
, e un componente Seam per cambiare il fuso orario chiamato org.jboss.seam.interanational.timezoneSelector
. Per default il fuso orario è il fuso orario di default del server. Purtroppo le specifiche JSF dicono che tutte le date e orari devono essere considerati come UTC e mostrati come UTC a meno che un fuso orario non sia esplicitamente specificato usando <f:convertDateTime>
. Questo è un comportamento di default estremamente sconveniente.
Seam modifica questo comportamento e imposta il fuso orario di tutte le date e orari al fuso orario di Seam. In più, Seam fornisce la tag <s:convertDateTime>
che esegue sempre questa conversione nel fuso orario di Seam.
Seam fornisce anche un converter di date di default per convertire una valore stringa in una data. Questo risparmia di dover specificare un converter sui campi d'input che sono semplicemente catturati come data. Il pattern viene selezionato in accordo con il locale dell'utente e la timezone viene selezionata come descritto sopra.
Le applicazioni Seam sono anche molto facilmente personalizzabili nell'aspetto. Le API per i temi sono molto simili alle API per la traduzione, ma ovviamente questi due concetti sono ortogonali e alcune applicazione gestiscono sia le traduzioni che i temi.
Prima di tutto occorre configurare l'insieme dei temi gestiti:
<theme:theme-selector cookie-enabled="true">
<theme:available-themes>
<value>default</value>
<value>accessible</value>
<value>printable</value>
</theme:available-themes>
</theme:theme-selector>
Notare che il primo tema elencato è il tema di default.
I temi sono definiti in file di proprietà con lo stesso nome del tema. Ad esempio, il tema default
è definito come un insieme di voci in default.properties
. Ad esempio default.properties
potrebbe definire:
css ../screen.css template /template.xhtml
Di solito le voci nel resource bundle di un tema saranno percorsi a fogli di stile CSS o immagini e nomi di modelli facelets (a differenza dei resource bundle per le traduzioni che normalmente contengono testo).
Ora è possibile usare queste voci nella pagine JSP o facelets. Ad esempio, per gestire con un tema il foglio di stile di una pagina facelets:
<link href="#{theme.css}" rel="stylesheet" type="text/css" />
Oppure, quando la definizione della pagina risiede in una sottocartella:
<link href="#{facesContext.externalContext.requestContextPath}#{theme.css}"
rel="stylesheet" type="text/css" />
In modo più flessibile, facelets consente di gestire con i temi il modello usato da un <ui:composition>
:
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
template="#{theme.template}">
Così come per selezionare la lingua, c'è un componente che consente all'utente di cambiare liberamente il tema:
<h:selectOneMenu value="#{themeSelector.theme}">
<f:selectItems value="#{themeSelector.themes}"/>
</h:selectOneMenu>
<h:commandButton action="#{themeSelector.select}" value="Select Theme"/>
Le selezioni della lingua, del tema e del fuso orario gestiscono tutte la registrazione della scelta in un cookie. Basta impostare la proprietà cookie-enabled
in components.xml
:
<theme:theme-selector cookie-enabled="true">
<theme:available-themes>
<value>default</value>
<value>accessible</value>
<value>printable</value>
</theme:available-themes>
</theme:theme-selector>
<international:locale-selector cookie-enabled="true"/>