MokaByte 85 - Maggio 2004 
Sviluppare applicazioni J2EE con Jakarta Struts
V parte: internazionalizzazione
di
Alfredo Larotonda
intro

Introduzione
Nei precedenti articoli abbiamo esaminato l'architettura di Struts e abbiamo descritto gli elementi fondamentali del framework e come questo implementi il pattern MVC. Con questo articolo iniziamo a descrivere alcune problematiche tipiche di una web application e come queste possano essere risolte con Struts. Iniziamo con il tema dell'internazionalizzazione di una applicazione , molto importante per chi si trova a dover progettare applicazioni che devono essere fruite in diverse lingue ed in diverse località geografiche.

 

Internazionalizzazione e Java
Internazionalizzare un'applicazione significa predisporla a supportare lingue diverse e regioni geografiche diverse senza dover intervenire con modifiche all'architettura.
Le specifiche standard riguardanti l'internazionalizzazione enunciano che un'applicazione internazionalizzata deve rispondere ai seguenti requisiti:

  • Lingue diverse devono essere supportate senza modifiche al codice
  • Testi e immagini devono essere memorizzati esternamente al codice
  • Date, numeri , valute devono essere correttamente formattate in base alle regole della regione geografica nella quale l'applicazione è eseguita
  • Sono supportati caratteri non standard
  • L'applicazione si adatta rapidamente a supportare una nuova lingua o una nuova regione

Il problema non è di poco conto poiché gli elementi che sono affetti da cambiamenti al variare della lingua e/o della regione sono molteplici all'interno di una applicazione. Spesso l'errore è di focalizzarsi solo sul primo punto che è il più evidente; si pensa infatti che internazionalizzare un'applicazione significhi rendere l'applicazione multilingua in quanto è immediato pensare alle label presenti nelle interfacce grafiche come unico elemento critico. In realtà anche date, numeri , valute sono elementi che variano al variare della lingua e del paese e quindi sono da tenere in conto. E' sicuramente molto importante sapere in fase di progettazione dell'applicazione che l'applicazione deve essere internazionalizzata poiché un intervento a posteriori sarebbe sicuramente estremamente oneroso.
Internazionalizzare una applicazione quindi dal punto di vista tecnico si traduce nello scrivere del codice che si comporti correttamente qualora la lingua o il paese cambino ma senza che sia necessario apportare modifiche allo stesso. E' ovvio che tutta una serie di risorse dell'applicazione saranno diverse a seconda della lingua. La label 'benvenuto' nella pagina iniziale dell'applicazione visitata da un utente di lingua italiana dovrà apparire come 'welcome' per un utente di lingua inglese. Per far si che un'applicazione internazionalizzata abbia un comportamento simile è necessario localizzarla.
Il processo di adattare un'applicazione internazionalizzata ad una specifica lingua e regione si definisce quindi localizzazione.
Per la localizzazione di una applicazione predisposta all'internazionalizzazione Java fornisce una serie di classi che consentono una gestione del problema estremamente flessibile ed elegante.
La localizzazione si basa sull'utilizzo di due classi fondamentali: java.util.Locale e java.util.ResourceBundle, e su una serie di altre classi che fanno riferimento a queste.
La classe java.util.Locale è quella che consente all'applicazione di conoscere in quale lingua e in quale paese viene eseguita. E' un identificatore per ogni combinazione di lingua e regione geografica, una istanza di questa classe identifica una combinazione language/country. Per istanziare un oggetto della classe Locale bisogna passare al costruttore un identificativo della lingua e della regione. Ad esempio il locale che identifica la lingua italiano e la regione Italia va costruito nel seguente modo:

Locale locale = new Locale("it", "IT").


Le classi del linguaggio che cambiano il proprio comportamento in funzione del locale corrente si dicono locale-sensitive. Rientrano ad esempio. in questa categoria le classi per la formattazione delle date e degli importi che vengono utilizzate nella scrittura di una applicazione internazionalizzata. Mediante queste classi è possibile scrivere codice che ha comportamento diverso in base al locale corrente e che quindi è predisposto ad essere eseguito per lingue diverse e in paesi diversi.
La classe java.util.ResourceBundle è invece un contenitore di oggetti cosiddetti locale-specific ovvero oggetti che assumono valori differenti in base al locale. Ad esempio una risorsa locale-specific è sicuramente la stringa che rappresenta una label di una interfaccia grafica; l'applicazione internazionalizzata reperirà la stringa dal ResourceBundle specifico per il locale corrente e quindi sarà predisposta al cambiamento della lingua o della regione. In realtà la classe java.util.ResourceBundle è una classe astratta. Una implementazione concreta è la java.util.PropertyResourceBundle che gestisce le risorse specifiche per un dato locale memorizzandole in file di properties. I file di properties sono file di testo contenenti un elenco di coppie chiave/valore, dove la chiave è un identificativo associato ad una determinata stringa e valore è la stringa stessa per quel determinato locale.

 

I componenti di Struts per la gestione dell'internazionalizzazione
Struts fornisce supporto all'internazionalizzazione essenzialmente per ciò che riguarda il reperimento di testo e immagini localizzate. Per gli altri aspetti, quali formattazione di date, importi etc. bisogna fare ricorso alle classi Java standard per le quali rimandiamo alla documentazione ufficiale presente sul sito della Sun (http://java.sun.com).
Struts gestisce l'internazionalizzazione fornendo gli strumenti per reperire risorse localizzate da opportuni resource bundle in base al locale corrente.
In una web-application, e quindi anche in quelle costruite con Struts, è possibile reperire l'informazione relativa al locale dell'utente mediante il metodo public java.util.Locale getLocale() dell'oggetto HttpServletRequest.
Infatti l'informazione del locale utilizzato dall'utente è inviata al container in ogni request; Struts come default memorizza questa informazione nella sessione, ma è possibile variare questo comportamento impostando il valore dell'attributo locale del tag <controller…/> nello struts-config.xml. Il valore di default è false.
Con l'informazione del locale presente in sessione l'applicazione è quindi in grado di reperire dal resource bundle appropriato la risorsa localizzata.

Per la gestione dei resource bundle in Struts viene usata la classe org.apache.struts.util.MessageResources che segue la stessa logica della java.util.ResourceBundle arricchendola con alcune funzioni di utilità.
Anche la classe org.apache.struts.util.MessageResources è una classe astratta, e la sua concreta implementazione è fornita dalla classe org.apache.struts.util.PropertyMessageResources che consente di leggere stringhe localizzate alle quali è associata una chiave reperendole da file di properties, esattamente come fa la java.util.PropertyResourceBundle.
Il primo elemento da definire quindi per localizzare una applicazione Struts sono proprio i resource bundle ovvero i file di properties contenenti la lista in formato nome/valore di tutte le label dell'applicazione. Esisterà un file di properties per la lingua di default della propria applicazione chiamato ad esempio ApplicationResources.properties
Che avrà una serie di elementi del tipo :

...
button.aggiorna=Aggiorna
button.conferma=Conferma
button.elimina=Elimina
button.inserisci=Inserisci
button.salva=Salva
...


Dovranno poi essere definiti tanti altri altri file di properties per tutte le combinazioni lingua/regione per le quali si vuole che l'applicazione sie predisposta.
Ad esempio

ApplicationResource_en_En.properties per inglese/regnoUnito
ApplicationResource_en_US.properties per inlese/Stati Uniti
ApplicationResources_fr_FR.properties per francese/Francia

e così via. Il file ApplicationResource_en_En.properties sarà ad esempio del tipo :

...
button.aggiorna=Update
button.conferma=Confirm
button.elimina=Delete
button.inserisci=Insert
button.modifica=Modifica
button.salva=Save
...
...

Nello struts-config.xml va indicato qual è il resource bundle utilizzato dall'applicazione con il tag
<message-resources parameter="it.prova.ApplicationResources"/>
indicando il nome radice della famiglia di resource bundle che si riferiscono alle stesse risorse localizzate.
I file di properties così definiti vanno quindi installati nella cartella /WEB-INF/classes dell'applicazione rispettando la struttura di package dichiarata nella definizione precedente.

 

La lettura dei resource bundle in Struts
I file di properties vengono letti allo start-up dell'applicazione, e usati per valorizzare istanze della classe org.apache.struts.util.PropertyMessageResources memorizzate poi nel ServletContext. E' quindi possibile accedere ai resource bundle dalle Action, dagli ActionForm o dalle pagine JSP.
Nelle Action il reperimento può essere fatto utilizzando i metodi della classe org.apache.struts.util.MessageResources come nell'esempio seguente relativo ad una Action:

...
//acquisizione del locale corrente mediante il metodo
//getLocale(HttpServletRequest request)
//della classe org.apache.struts.action.Action che restiuisce il locale corrente //dell'utente
Locale locale = this.getLocale(request);

// acquisizione del MessageResources
MessageResources messages = servlet.getResources();

// acquisizione della stringa localizzata corrispondente al locale corrente e
// alla chiave memorizzata nel parametro key
String value = messages.getMessage(locale,key);

oppure più frequentemente mediante i costruttori delle classi ActionMessages e ActionErrors utilizzate nella gestione dei messaggi di errore, che ricevono come parametro la chiave della label da reperire e acquisiscono in automatico dal resource bundle del locale corrente il valore della label stessa localizzata. Un esempio è il metodo validate di una ActionForm nel quale viene utilizzata la classe ActionError per rappresentare un messaggio di errore il cui valore localizzato viene reperito passando al costruttore la chiave corrispondente.

public ActionErrors validate(ActionMapping mapping,HttpServletRequest request) {
ActionErrors errors = new ActionErrors();
if ((username == null) || (username.length() < 1))
errors.add ("username",new ActionError("errore.username.obbligatorio"));
if ((password == null) || (password.length() < 1))
errors.add("password",new ActionError("errore.password.obbligatoria"));
return errors;
}

Per reperire le stringhe localizzate nelle pagine JSP si utilizza invece un custom-tag della libreria struts-bean, precisamente il tag <bean:message>.
L'utilizzo è banale , basta fornire come attributo la chiave corrispondente alla label che si vuole acquisire come nell'esempio seguente:

<bean:message key="label.username" />

In questo modo è molto semplice scrivere pagine JSP nelle quali non sono presenti label direttamente scritte nel codice , e quindi utilizzare lo stesso sorgente della pagina per visualizzare informazioni in lingue differenti.

 

Conclusioni
In questo articolo abbiamo esaminato gli strumenti che Struts ci mette a disposizione per l'internazionalizzazione e di una applicazione. Utilizzando queste funzionalità è veramente molto semplice scrivere una applicazione nella quale tutte le stringhe sono esterne al codice e indipendente al variare del locale. Va ancora ribadito che Struts risolve solo una parte delle esigenze di un applicazione con supporto all'internazionalizzazione. La scrittura di codice per la corretta formattazione di date e importi al variare di lingua e paese va fatta mediante le classi standard del linguaggio Java e non è risolta da componenti specifici di Struts.

 

Bibliografia
[1] Chuck Cavaness - "Programming Jakarta Struts", O'Reilly, 2003
[2] James Goodwill, Richard Hightower - Professional Jakarta Struts" - Wrox 2004


Alfredo Larotonda, laureato in Ingegneria Elettronica, lavora da diversi anni nel settore IT. Dal 1999 si occupa di Java ed in particolare dello sviluppo di applicazioni web J2EE. Dopo diverse esperienze di disegno e sviluppo ora si occupa in particolare di aspetti architetturali per progetti rivolti al mercato finanziario ed industriale. E' Web Component Developer certificato SUN per la piattaforma J2EE e Programmer per la piattaforma Java.

MokaByte® è un marchio registrato da MokaByte s.r.l. 
Java®, Jini® e tutti i nomi derivati sono marchi registrati da Sun Microsystems.
Tutti i diritti riservati. E' vietata la riproduzione anche parziale.
Per comunicazioni inviare una mail a info@mokabyte.it