MokaByte Numero  46  - Novembre 2000
Cartolina
Una servlet per l’invio
di cartoline virtuali
II parte: l'internazionalizzazione
delle cartoline
di 
Stefano Bussolon
Distribuire una applicazione su internet significa affrontare un mercato fatto di molte piattaforme software e di innumerevoli lingue. Java ci permette di costruire programmi capaci di funzionare su sistemi operativi diversi e di adattarsi alla lingua dell’utente.

Nello scorso numero abbiamo descritto Cartolina.java, una servlet deputata all’invio di cartoline virtuali. In questo articolo descriveremo l’uso di due classi: ResourceBundle e Locale. Queste classi verranno introdotte dapprima per permettere di adattare l’applicazione a server diversi senza dover modificare e ricompilare il codice. Poiché le due classi sono state progettate per creare applicazioni multilingue, le utilizzeremo per rendere internazionale la nostra servlet.

Introduzione
Uno degli aspetti che maggiormente contribuisce al successo di un software è legato alla sua semplicità d’uso, sia in termini di installazione che nell’utilizzo vero e proprio. Per quanto concerne l’uso del servizio da parte dell’utente finale, l’utilizzo di Cartolina.java risulta piuttosto intuitivo in quanto simile a “prodotti concorrenti”, ovvero ad altri servizi di e-card che l’utente può aver utilizzato in passato. Anche qualora l’utente non avesse mai inviato cartoline elettroniche l’interazione non dovrebbe costituire un grosso problema, in quanto le azioni che l’utente deve svolgere sono analoghe a quelle che una persona compie inviando una cartolina reale: si sceglie un’immagine, si immettono nome e indirizzo di mittente e destinatario, si scrive un breve messaggio e si invia.
Per quanto concerne la facilità di installazione intervengono altri problemi. Il principale è costituito dal fatto che per far funzionare una servlet è necessario che il server internet sul quale si intende far girare l’applicazione supporti la tecnologia, attraverso un’estensione del demone http o attraverso un’applicazione indipendente. Questo prerequisito risulta tuttora piuttosto limitante, in quanto i service providers che supportano l’utilizzo delle servlet sono tuttora una minoranza, e presuppone che l’amministratore di sistema abbia una buona familiarità sia con il sistema operativo sottostante (linux o NT) che con l’ambiente java. Un amministratore capace di far convivere linux, apache, jserv e jdk 1.2 (tanto per citare l’ambiente che io utilizzo) non dovrebbe spaventarsi di fronte all’ipotesi di metter mano ad alcune righe di codice java e ricompliare la classe in questione.
D’altro canto è comunque buona norma adottare, nei limiti del possibile, soluzioni che richiedano il minimo intervento da parte dell’utilizzatore, soprattutto qualora non si intenda distribuire il codice sorgente.
Uno dei punti di forza di java è la sua idipendenza di piattaforma: una stessa applicazione gira, a parità di macchina virtuale, su sistemi diversi. Cartolina.java può essere compilata su Windows 98 (il pc del mio ufficio) ed il file Cartolina.class gira perfettamente su Red Hat 6.2 (il mio server). I problemi sorgono nel momento in cui la servlet deve leggere o scrivere dei file, in quanto i nomi di percorso su macchine diverse - anche con lo stesso s.o. - probabilmente non coincidono. 
La soluzione più pratica consiste nel separare le variabili specifiche per ogni macchina dal codice della classe: l’applicazione andrà a leggere su di un file esterno i valori in questione. In questo modo sarà sufficiente mettere mano al file di testo senza dover ricompilare il programma.

Per far fronte a questa esigenza java mette a disposizione la classe ResourceBundle, finalizzata proprio a gestire in maniera efficace un file di risorse esterno da parte di una classe java.
Potete trovare un ottimo esempio dell’utilizzo di ResourceBundle in Stylepad, una delle applicazioni distribuite assieme al jdk (si trova in %javahome%/demo/jfc/Stylepad/). Le prime righe della classe Stylepad si occupano proprio di costruire l’oggetto ResourceBundle:

private static ResourceBundle resources;
  static {
   try {
    resources = 
       ResourceBundle.getBundle("resources.Stylepad",
         Locale.getDefault());
  }
  catch (MissingResourceException mre) {
      System.err.println("Stylepad.properties not found");
      System.exit(0);
  }
}

L’oggetto resources viene creato attraverso l’invocazione del metodo static ResourceBundle.getBundle che, come possiamo notare, prevede due parametri. Il primo parametro si riferisce al nome del file in questione: la funzione getBundle cercherà un file chiamato Stylepad.properties nella sottodirectory resources (per una spiegazione più dettagliata si veda
http://java.sun.com/products/jdk/1.1/docs/api/java.util.ResourceBundle.html).
Il secondo parametro richiesto è un oggetto di tipo Locale. Ci soffermeremo su questo aspetto nel prossimo paragrafo, dedicato al supporto multilingue della servlet. Per ora soffermiamoci su come Cartolina.java utilizza ResourceBundle per definire le variabili d’ambiente.

  Locale lingua = Locale.getDefault();
  try {
   resources = ResourceBundle.getBundle("CustomCard", lingua);
   serverdirpath = resources.getString("serverdirpath");
   serverlogpath = resources.getString("serverlogpath");
   imageURL = resources.getString("imageURL");
   URLpath = resources.getString("URLpath");
   smtpserver = resources.getString("smtpserver");
   countfilename = serverlogpath + "customcount.log";
   logname = serverlogpath + "customlog.log";
 }
 catch (MissingResourceException mre) {
   System.err.println(mre);
 }
 

Questo codice, inserito nella funzione init, permette alla servlet di acquisire il percorso assoluto dove dovrà cercare o salvare i file. L’applicazione si aspetta di trovare un file denominato CustomCard.properties che definisce le variabili in questione. Se andiamo a leggere il file CustomCard.properties troveremo fra l’altro le seguenti righe:

# the path on your server where generated cards are stored
serverdirpath = /home/httpd/html/files/customcards/
# the path on your server where you want to save the logs
serverlogpath = /home/pippo/lavoro/
# the url were images are linked
imageURL=http://www.miosito.it/cartoline/
# the url where generated cards are linked
URLpath=http://www.miosito.it/files/customcards/
smtpserver=mail.mioserver.it

Queste variabili sono appropriate per funzionare sul server. Se voglio testare l’applicazione in locale sul mio PC (ad esempio usando jswdk) le variabili d’ambiente saranno le seguenti:

serverdirpath=C:\\Programmi\\java12\\jswdk\\examples\\cartoline\\
serverlogpath=C:\\Programmi\\java12\\jswdk\\examples\\cartoline\\
imageURL=http://127.0.0.1:8080/examples/cartoline/
URLpath=http://127.0.0.1:8080/examples/cartoline/
smtpserver=mail.mioprovider.it

Ricapitolando: io creo due versioni diverse del file CustomCard.properties: una da salvare sul server, una da mantenere in locale. Il sistema cercherà il file nella stessa directory della applicazione. Qualora volessi salvare il file nella sottodirectory pippo l’invocazione della classe da parte del metodo getBundle dovrà avvenire utilizzando la notazione getBundle (“pippo.CustomCard”, lingua). In questo modo io posso compilare la classe sul mio pc locale e questa funzionerà allo stesso modo sia su windows 98 e jswdk che su linux e apache jmod. Chiunque voglia usare la servlet dovrà adattare soltanto il file CustomCard.properties, in base alle impostazioni del proprio sistema. Facciamo incidentalmente notare che, poiché la servlet invia un messaggio di posta elettronica di notifica, dovrà utilizzare un server smtp. La variabile smtpserver=mail.mioprovider.it è deputata proprio ad identificare un server cui il sistema può accedere.
 
 
 

Localizzazione: creare una servlet multilingue
Ritorniamo alla funzione ResourceBundle.getBundle. Il secondo argomento consiste in un oggetto di tipo Locale.
La classe Locale è finalizzata ad identificare una regione geografica o culturale. In pratica la classe viene utilizzata per localizzare un’applicazione, ovvero per tradurre un’applicazione in differenti lingue. Se scrivo un programma per un mercato internazionale, è necessario che io predisponga delle versioni diverse per ognuna delle aree linguistiche dove prevedo che il prodotto venga utilizzato. In maniera del tutto simile a quella delle variabili di ambiente appena viste, la soluzione più elegante consiste nel separare il codice del programma da tutti gli aspetti di interfaccia connotati linguisticamente, quali le voci dei menu, dei dialoghi e così via. Prima dell’avvento di internet la necessità di tradurre un’applicazione in differenti lingue era circoscritta ad aziende di grandi o medie dimensioni, ma con il diffondersi del web e la possibilità di distribuire i propri lavori on line il problema della localizzazione diventa centrale. La soluzione più semplice consiste nello scrivere tutto direttamente in inglese, la lingua di riferimento per la rete. L’approccio più elegante ed efficace, per chi usa java, è quello di usare le due classi ResourceBundle e Locale.
Se si considera che una servlet è per definizione un’applicazione che gira su internet ecco che la necessità di creare una interfaccia multilingue potrebbe risultare addirittura comune: personalmente ho scritto Cartolina.java per pubblicizzare il sito di un albergo, ed è dunque auspicabile che il servizio venga utilizzato da utenti italiani, tedeschi, inglesi.
Il primo passo da compiere per avere una cartolina virtuale multilingue è creare un file CustomCard.properties per ognuna delle lingue supportate, per esempio italiano ed inglese. 
La classe ResourceBundle prevede che i file che identificano le risorse vengano salvati attraverso una precisa notazione: “nomerisorsa_lingua_paese.properties”. 
Ad esempio CustomCard_en_US.properties identifica l’inglese statunitense, CustomCard_en_uk.properties quello britannico. 
In realtà è possibile specificare con ancora maggior dettaglio la localizzazione, definendo una “variante” linguistica: per approfondimenti si consiglia di far riferimento alla documentazione concernente la classe Locale.

E’ possibile usare un file che specifichi la lingua ma non il paese (CustomCard_en.properties). 
Questa risorsa verrà utilizzata qualora non esista un file specifico per il paese, o non si ritenga opportuno specificare versioni diverse per i diversi paesi.
Nel nostro esempio, se intendiamo realizzare una versione delle cartoline in italiano ed una in inglese, dovremo creare i file CustomCard_it.properties e CustomCard_en.properties. Nei due file sono state inserite tutte le frasi che la servlet usa per interagire con il mittente, per inviare l’e-mail al destinatario e per generare la cartolina. Alcune righe di esempio chiariranno il concetto:
in CustomCard_it.properties troveremo, fra l’altro, le seguenti righe.

warning=Attenzione, parametri mancanti! tutti i campi devono essere compilati
imageField=l'immagine della cartolina
sendernamefield=inserisci il tuo nome
sendermailfield=la tua e-mail
receivernamefield=il nome del destinatario
receivermailfield=l'e-mail del destinatario
textfield=il testo della cartolina

In CustomCard_en.properties le corrispondenti righe sono:

warning=Warning, some fields are empty. you have to fill all the fields
imageField=the image of the e-card
sendernamefield=your name (or nick)
sendermailfield=your e-mail
receivernamefield=the name (nick) of your friend
receivermailfield=the e-mail of your friend
textfield=the text of the e-card

Queste variabili si riferiscono alla descrizione dei campi della form che l’utente utilizza per mandare la cartolina. Per conoscere la lingua dell’utente, si utilizza la variabile “Accept-Language” che il browser invia al server tramite http, attraverso il metodo request.getHeader("Accept-Language");
Poiché abbiamo creato soltanto 2 versioni linguistiche delle nostre properties, dovremo adottare una soluzione di compromesso:

String language = request.getHeader("Accept-Language");
  if (language == null) {
   lingua = Locale.ENGLISH;
   return;
  }
  if (language.equalsIgnoreCase ("en")) {
   lingua = Locale.ENGLISH;
   return;
  }
  if (language.equalsIgnoreCase ("it")) {
   lingua = Locale.ITALIAN;
   return;
  }
  lingua = Locale.ENGLISH;
 }

In queste righe di codice si interroga la variabile “Accept-Language”. Se l’utente è italiano si utilizza il locale italiano, in tutti gli altri casi si risponde in inglese (lingua internazionale). Qualora approntassimo un file CustomCard_de.properties, basterebbe aggiungere tre righe di codice per permettere alla servlet di utilizzare la versione tedesca:

  if (language.equalsIgnoreCase ("de")) {
   lingua = Locale.GERMAN;
   return;
  }

Infine si interroga il file di proprietà selezionato per ricavarne le frasi da utilizzare nell’interfaccia:
   warning = resources.getString("warning");
   imageField = resources.getString("imageField");
   sendernamefield = resources.getString("sendernamefield");
   sendermailfield = resources.getString("sendermailfield");
   receivernamefield = resources.getString("receivernamefield");
   receivermailfield = resources.getString("receivermailfield");

Nel visualizzare la form verranno utilizzate le variabili così ottenute, generando una pagina di volta in volta in italiano, o in inglese, o in tedesco:

  out.println("<tr><td>" + sendernamefield + "</td>");
  out.println("<td><input name=\"nomemitt\" type=\"text\" value=\"\"></td></tr>");
  out.println("<tr><td>" + sendermailfield +"</td>");
  out.println("<td><input name=\"mailmitt\" type=\"text\" value=\"\"></td></tr>");
  out.println("<tr><td>" + receivernamefield + "</td>");
  out.println("<td><input name=\"nomedest\" type=\"text\" value=\"\"></td></tr>");
  out.println("<tr><td>" + receivermailfield + "</td>");
  out.println("<td><input name=\"maildest\" type=\"text\" value=\"\"></td></tr>");

Come vediamo, nel generare il codice html della form vengono utilizzate le variabili acquisite dai file di risorsa.
 
 
 

Cartoline internazionali
Ne “Il computer invisibile” Donald Norman afferma che una tecnologia, per essere efficace e di facile usabilità, dovrebbe essere invisibile all’utente, e funzionare senza che questi se ne accorga. La strategia di localizzazione descritta in queste righe funziona proprio in questo modo: l’utente chiede un servizio, inconsapevole che il proprio browser comunica al server le sue preferenze linguistiche. Il server elabora la pagina richiesta, in maniera congruente con le informazioni che dispone, e la presenta all’utente. Questi non sa, o non sospetta, che esistano differenti versioni della stessa pagina: la tecnologia è dunque efficace ma invisibile. Il problema linguistico emerge soltanto nel momento in cui un utente italiano invia, ad esempio, una cartolina ad un amico inglese. Poiché l’applicazione da noi sviluppata tiene conto soltanto della lingua del mittente, i (pochi) elementi linguistici della cartolina saranno in italiano. Per risolvere questo problema si dovrebbe ricorrere all’uso di un database: la servlet salva le informazioni sotto forma di record, evita di creare una versione statica della cartolina ma la genera dinamicamente nel momento in cui viene letta. In questo modo può “tradurre” l’interfaccia nella lingua del destinatario. Una simile strategia risulta sproporzionata per le esigenze di una cartolina virtuale, ma risulterebbe utilissima in siti come e-bay, dove persone di differenti paesi vendono e comperano i propri beni. In questo caso il server potrebbe tradurre non soltanto l’interfaccia linguistica ma anche la valuta: posso comperare in lire un portatile usato che uno statunitense vende in dollari, sperando che l’euro, nel frattempo, non precipiti! :-(.
 
 
 

Conclusioni
Siamo abituati ad apprezzare java come linguaggio multipiattaforma. Attraverso un accorto utilizzo dei package che java ci mette a disposizione, possiamo creare, senza grosse difficoltà, applicazioni multilingue: la stessa applicazione può girare su macchine diverse ed essere fruita da utenti di differenti estrazioni linguistiche.
 
 
 

Bibliografia
The java tutorial: Intenationalization 
Donald Norman: The Invisible Computer 


Stefano Bussolon, laureato in psicologia sperimentale. Partecipa, nella veste di "cultore della materia", all'attività didattica dei corsi di Intelligenza Artificiale e di Ergonomia della Facoltà di Psicologia di Padova. Per il corso di Ergonomia ha condotto un seminario sull’usabilità del www. Fa parte del laboratorio di realtà virtuale del dipartimento di Psicologia Generale di Padova. E-mail: bussolon@psy.unipd.it
 

Chi volesse mettersi in contatto con la redazione può farlo scrivendo a mokainfo@mokabyte.it