Internazionalizzare un‘applicazione significa predisporla all‘uso da parte di utenti di diversi paesi e lingue. Analizziamo come raggiungere questo obiettivo con JSF.
Introduzione
Il problema dell‘internazionalizzazione di un‘applicazione è ben noto. Un‘applicazione può essere fruita da utenti di diversi paesi. Ciò significa che deve adattarsi alle preferenze in termini di lingua, e non solo, degli utenti che la utilizzano. Paesi diversi e lingue diverse non significano solamente la diversa traduzione di un testo o di una label, ma anche la gestione di numerosi altri aspetti quali la rappresentazione delle date, delle valute se non addirittura la modalità di visualizzazione stessa. Basti pensare a quelle lingue nelle quali il verso della scrittura è da destra a sinistra, da cui consegue la necessità di ribaltare l‘interfaccia dell‘applicazione come davanti ad uno specchio.
Il linguaggio Java come è noto mette a disposizione gli strumenti per affrontare questo problema. In Java l‘insieme delle preferenze legate ad un paese e ad una lingua viene chiamato locale. Internazionalizzare un‘applicazione significa predisporla a operare con molteplici locale. Localizzarla significa modificarla per uno specifico locale. Nei paragrafi seguenti vedremo come agire per localizzare la nostra applicazione JSF.
Locale e ResourceBundle
Nelle pagine di MokaByte si è parlato molte volte degli strumenti che Java mette a disposizione per l‘internazionalizzazione delle applicazioni [6], alla quale ci si riferisce nel linguaggio degli sviluppatori con la sigla i18n, ottenuta dalla prima e l‘ultima lettera della parola con in mezzo il numero delle lettere comprese tra queste.
Java mette a disposizione la classe java.util.Locale che rappresenta un Paese e una lingua e la classe astratta java.util.ResourceBundle che consente di definire delle risorse contenenti le informazioni utili a localizzare un‘applicazione. Un resource bundle contiene informazioni nella forma chiave/valore dove la chiave identifica uno specifico elemento localizzato e può essere implementato mediante una classe o un file di testo. Nella pratica a un resource bundle è quasi sempre associato un file di testo di tipo .properties.
Un locale è rappresentato da un codice composto da due parti: una individua il Paese e una individua la lingua. Ad esempio il codice en_GB rappresenta l‘inglese e la Gran Bretagna mentre il codice en_US rappresente l‘inglese e gli USA. La lingua è obbligatoria mentre il codice del paese è opzionale.
Tipicamente un resource bundle è implementato mediante un file properties specifico per ogni locale. Nell‘applicazione saranno quindi definiti un file properties di default e file properties aggiuntivi per ciascuna combinazione di paese e lingua per la quale la nostra applicazione necessita di essere localizzata.
Quindi, ad esempio, avremo un file LocalizationResources.properties contenente i valori di default per tutte le stringhe localizzate dell‘applicazione ed un file LocalizationResources_fr_FR.properties per la versione francese delle stesse.
L‘header della richiesta HTTP contiene l‘informazione sul locale in uso da parte del browser dell‘utente; questa informazione consente all‘applicazione di presentare all‘utente un‘interfaccia opportunamente localizzata. Ovviamente in un‘applicazione non è detto che tutte le lingue siano supportate. In tal caso viene preso in considerazione il resource bundle di default.
Internazionalizzare un‘applicazione quindi significa predisporre un‘applicazione a supportare molteplici locale . Localizzare significa modificare l‘applicazione per uno specifico locale.
Localizzazione delle stringhe statiche dell‘interfaccia
Come ogni framework anche JSF offre strumenti per internazionalizzare e localizzare un‘applicazione. Nella discussione si farà riferimento alla localizzazione di stringhe statiche di testo, quali le label presenti nelle pagine, e dei messaggi di errore generati dal framework.
Per poter utilizzare nella propria applicazione delle stringhe di testo localizzato, come prima cosa bisogna definire quali siano i locale supportati. Come intuibile questa definizione va inserita nel file di configurazione di JSF faces-config.xml.
Ecco la sezione che definisce i locale supportati:
it_IT
en_US
it.mokabyte.jsf.LocalizationResources
Nell‘esempio si dichiara come locale di default la combinazione italiano/Italia mentre, come altro locale supportato, inglese/USA. In base all‘informazione ricevuta dall‘header nella richiesta http ed in base alla lista dei locale suportati, il framework imposta il locale da utilizzare.
Nel file faces-config.xml è definito anche il resource bundle contenente le informazioni per la localizzazione. Nel nostro caso avremo il file it.mokabyte.jsf.LocalizationResources.properties come default e il file it.mokabyte.jsf.LocalizationResources_en_US.properties per il locale inglese/USA.
I file conterranno nella forma chiave = valore tutte le stringhe che è necessario localizzare. Le chiavi saranno sempre le stesse e i valori cambieranno ovviamente da locale a locale. Un file di questo tipo conterrà ad esempio delle righe come le seguenti nella versione italiana:
label.benvenuto=Benvenuto
label.button=Invia
e nella versione inglese:
label.benvenuto=Welcome
label.button=Submit
Una volta definite le lingue supportate e i resource bundle contenenti i valori delle stringhe localizzate, resta da vedere come acquisire questi valori localizzati. Per le pagine JSP il framework mette a disposizione il tag
si espongono mediante la variabile bundle tutti i valori delle chiavi presenti nei file .properties precedentemente definiti. Questi valori possono quindi essere recuperati con delle semplici espressioni. Ad esempio per un HtmlOutputText un valore localizzato può essere recuperato nel seguente modo:
Il testo visualizzato all‘utente dipenderà dal locale in uso. È quindi evidente che così facendo la nostra applicazione presenterà all‘utente un‘interfaccia opportunamente localizzata senza che sia necessario intervenire nel codice. Il sorgente è unico e indipendente dal locale in uso, ma si adatta automaticamente ad esso.
Vi è anche da notare che rendere tutte le stringhe statiche di testo esterne all‘applicazione ha senso anche se questa non necessita di localizzazione. Infatti così facendo si eviteranno inutili ripetizioni di stringhe identiche e sarà possibile modificare in un colpo solo una determinata label agendo esternamente al codice. Se ad esempio in molte pagine avessimo la label “Codice Avviamento Postale” e dovessimo cambiarla in “CAP”, saremmo costretti a modificare tutte la pagine che la contengono. Con l‘utilizzo della tecnica esposta, invece, dovremmo esclusivamente modificare un valore nel resource bundle ottenendo il risultato desiderato senza toccare una riga di codice. Quindi anche se non vi è l‘esigenza della localizzazione per ragioni “geografiche” è comunque meglio evitare di cablare nel codice delle stringhe statiche di testo.
Localizzazione dei messaggi applicativi e dei messaggi standard
L‘esigenza di reperire stringhe localizzate non è solo caratteristica dell‘interfaccia grafica. Anche nel codice Java dell‘applicazione può esserci la necessità di dover gestire stringhe localizzate ad esempio per inviare all‘utente messaggi applicativi nella lingua opportuna.
Il FacesContext mantiene l‘informazione sul locale in uso nella richiesta corrente. È sufficiente invocare il metodo getViewRoot().getLocale() per reperire questa informazione. Analogamente per impostare un diverso locale è sufficiente eseguire il metodo getViewRoot().setLocale().
Con le seguenti righe di codice è quindi possibile acquisire un riferimento al resource bundle dell‘applicazione, un riferimento al locale in uso e reperire una determinata stringa localizzata associata ad una chiave:
FacesContext context = FacesContext.getCurrentInstance();
Locale currentLocale = context.getViewRoot().getLocale();
String bundleName = context.getApplication().getMessageBundle();
ResourceBundle bundle = ResourceBundle.getBundle(bundleName, currentLocale);
String valore = bundle.getString(chiave);
JSF mette a disposizione messaggi standard associati agli errori di validazione e di conversione dei dati. JSF utilizza delle classi standard come resource bundle contenenti le coppie chiave-valore per i messaggi standard. Per localizzare e personalizzare questi messaggi, una volta definito come visto il message bundle nel faces-config.xml, è sufficiente fornire valori custom per le chiavi standard che identificano i messaggi.
Ad esempio per personalizzare il messaggio associato ad un errore di conversione di un componente di input è sufficiente fornire un valore nel proprio resource bundle per la chiave:
javax.faces.component.UIInput.CONVERSION
Una lista delle costanti utilizzate da JSF per la versione 1.2 è reperibile all‘indirizzo
http://java.sun.com/j2ee/javaserverfaces/1.2/docs/api/constant-values.html
Applicazione di esempio
Nell‘applicazione di esempio (nello ZIP che potete scaricare dal menu in alto a sinistra) sono stati creati due file di properties associati al resource bundle definito nel faces-config.xml come descritto nei paragrafi precedenti. In questi due file sono state inserite tutte le stringhe statiche utilizzate dall‘applicazione per l‘italiano e l‘inglese.
Sono stati inseriti nella pagina di login due link che consentono di impostare la lingua di default per l‘applicazione allo scopo di far vedere come sia possibile fare questa operazione. Inoltre sono stati personalizzati e localizzati i messaggi standard utilizzati. L‘applicazione di esempio associata all‘articolo è stata testata su Apache Tomcat 5.5.25 (jsfIIItomcat.war) e Jboss 4.2.1 – GA (jsfIIIjboss.war)
Conclusioni
Nell‘articolo sono state fornite le conoscenze di base per predisporre un‘applicazione JSF all‘internazionalizzazione. Si è parlato di stringhe statiche e messaggi di errore. È bene tener presente che questo non esaurisce il compito, poiché l‘esigenza dell‘internazionalizzazione esiste anche per i dati dinamici. In [3] e [4] è possibile trovare degli ottimi suggerimenti per la risoluzione di questo ulteriore problema.
Riferimenti
[1] David Geary – Cay Horstmann, “Core Java Server Faces”, Sun Microsystems Press, 2004
[2] Bill Dudney – Jonathan Lehr – Bill Willis – LeRoy Mattingly, “Mastering Java Server Faces”, Wiley Publishing Inc., 2004
[3] Kito D. Mann, “Java Server Faces In Action” Manning Publications Co., 2005
[4] Autori Vari, “The Java EE 5 Tutorial”, Sun Microsystems, Febbraio 2007
[5] The API Javadocs for the JavaServer TM Faces Specification
http://java.sun.com/javaee/javaserverfaces/1.2/docs/api/index.html
[6] Alfredo Larotonda, “Sviluppare applicazioni con Jakarta Struts. V Parte”, MokaByte Maggio 2004