Mostrerò in questo articolo come creare un componente software da utilizzare nelle applicazioni Java (server o client, web o Swing) per tradurre tutte le vostre etichette, messaggi e segnalazioni in qualsiasi lingua vogliate.
Questo post, con l'esposizione del codice di implementazione della classe Translator, conclude l'analisi affrontata precedentemente nell'articolo UML: Progettare un Traduttore Singleton.
Nel suddetto articolo sono esposte, anche grazie a diagrammi UML, tutte le specifiche richieste per il componente e la struttura delle classi coinvolte.
Sono presenti tutte le informazioni necessarie per comprendere l'attività ed il ruolo che ogni membro interno alle classi dovrà svolgere.
Ne consiglio la lettura prima di affrontare l'implementazione java del componente; aiuterà sicuramente a mettere a fuoco il contesto applicativo, struttura della tabella SQL compresa.
Vale la pena ricordare che tra i requisiti che il componente deve soddisfare vi sono:
- la necessità che esso sia mono istanza, ovvero aderisca al design pattern Singleton;
- la necessità che la base dati dell'intero dizionario multi-lingua sia collocata sul DB Server MS SQL Server;
Passo subito ad esporre il codice sorgente delle classi che costituiscono il mio sistema di traduzione, iniziando dalla classe di Model Label.
Il codice java esposto è molto ben commentato e segue le regole di formattazione javadocs.
package common;
import java.io.Serializable;
/**
* Classe di Model per il wrapping dei record
* della tabella dictionary.
* La classe dopo essere stata instanziata non
* permette di modficare lo stato delle proprietà
* key e lang.
*
* @author magrati
*/
public class Label implements Serializable {
private static final long serialVersionUID = 1L;
private String key;
private String lang;
private String desc;
/**
* Costruttore privato.
*
* @param k la chiave univoca che identifica
la nuova istanza.
* @param l la lingua in cui è tradotto il termine.
* @param d il termine tradotto,
*/
private Label(String k, String l, String d){
this.key = k;
this.lang = l;
this.desc = d;
}
/**
* Overload del metodo toString():
* è utile per tracciare lo stato dell'oggetto.
*
* @return una descrizione dello stato dell'oggetto.
*/
public String toString(){
return "Label : key=" + this.key + ", lang="
+ this.lang + ", desc=" + this.desc;
}
/* Getters e Setters */
public String getDesc() {return desc;}
public String getKey() {return key;}
public String getLang() {return lang;}
public void setDesc(String d){desc = d;}
/**
* Accesso pubblico al costruttore privato di classe.
*
* @param k la chiave univoca che identifica
la nuova istanza.
* @param l la lingua in cui è tradotto il termine
* @param d il termine tradotto
*
* @return una nuova istanza di classe.
*/
public static Label getInstance(String k, String l, String d){
return new Label(k, l, d);
}
}
Niente di particolare, la classe Label è fondamentalmente un semplice javabean tranne per la particolarità che non permette di modificare alcune sue proprietà a runtime.
Queta scelta è stata fatta per salvaguardare l'integrità relazionale del dato conservato all'interno della HashMap, che rappresenta il dizionario, e della chiave univoca con cui lo stesso dato può essere recuperato.
Passo ora a mostrare il codice della classe Translator, il componente che deve essere invocato per eseguire le traduzioni.
package traduzioni;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Hashtable;
import common.Label;
/**
* La classe incapsula un'istanza singleton
* del dizionario da utilizzare per la
* traduzione di termini specifici.
*
* Il componente software in caso non conoscesse
* la traduzione richiesta provvede
* ad inserire nella fonte dati il
* nuovo termine richiesto.
*
* @author magrati
*/
public class Translator {
/**
* Istruzione SQL per caricare tutto
* il database dalla base dati.
*/
private static final String SQL_LOADER =
"select language_id,description_code"
+ ",description_des from dictionary";
/**
* Istruzione SQL per inseire nella
* base dati i nuovi termini
* di cui non si conosce la traduzione.
*/
private static final String SQL_SETTER =
"insert into dictionary"
+ " (language_id, description_code,"
+ " description_des, context_des, uts_code)"
+ " values(?,?,null,?,current_timestamp)";
/**
* Istanza singleton del dizionario
* dei termini tradotti.
* Garantisco una gestione corretta
* degli accessi concorrenziali,
* quindi utilizzo una Map di tipo Hashtable.
*/
private static Hashtable globalDictionary =
new Hashtable();
/**
* Blocco static che viene chiamato
* all'avvio dell'applicazione.
*/
static{
Translator.init();
}
/**
* Inizializzatore delle proprietà della classe.
* Accede alla base dati;
* carica il dizionario;
* crea le chiavi di accesso alla
* hashmap globalDictionary;
* riempie la hashmap globalDictionary
* con tutte le traduzioni;
*/
private static void init(){
Connection cn = null;
try {
cn = DriverManager.getConnection(
"URL_DI_CONNESSIONE","loginName","Password");
PreparedStatement stm =
cn.prepareStatement(Translator.SQL_LOADER);
ResultSet rs = stm.executeQuery();
while ( rs.next() ){
/**
* la chiave di accesso alla HashMap
* è composta unendo
* "language_id" + "description_code"
*/
String desc = (rs.getString(3) != null)
? rs.getString(3).trim() : null;
Label l = Label.getInstance(
rs.getString(1).trim() + rs.getString(2).trim(),
rs.getString(1).trim().trim(), desc);
Translator.globalDictionary.put(l.getKey(),l);
}
rs.close();
stm.close();
}
catch(SQLException e){
System.err.println(e.getMessage());
}
finally {
if ( cn != null ) {
try{
cn.close();
}
catch(SQLException e){
System.err.println(e.getMessage());
}
}
}
}
/**
* Inserisce in tabella i nuovi termini di cui è stata
* richiesta una traduzione che invece non è stata trovata.
*
* @param lang lingua in cui deve essere tradotto il termine
* @param key chiave univoca di accesso al dizionario
* @param pathContext stringa per tenere traccia di
* informazioni aggiuntive
* @return true se l'inserimento è avvenuto con successo,
* altrimenti false
*/
public static boolean addNonExistingLabel(String lang,
String key, String pathContext){
boolean insertIsDone = false;
Connection cn = null;
//Aggiorno il dizionario HashMap
Label l = Label.getInstance(key.toUpperCase()
,lang.trim().toUpperCase(),null);
Translator.globalDictionary.put(
lang.trim().toUpperCase() + key, l);
//Aggiorno la tabella SQL 'dictionary'
try {
cn = DriverManager.getConnection(
"URL_DI_CONNESSIONE","loginName","Password");
PreparedStatement stm = cn.prepareStatement(SQL_SETTER);
stm.setString(1,lang.trim().toUpperCase());
stm.setString(2,key.toUpperCase());
stm.setString(3, pathContext);
insertIsDone = (stm.executeUpdate() == 1);
}
catch(SQLException e){
System.err.println("Key: '" + key + "';\n" + e.getMessage());
}
finally {
if ( cn != null ) {
try{
cn.close();
}
catch(SQLException e){
System.err.println(e.getMessage());
}
}
}
return insertIsDone;
}
/**
* Cerca la traduzione del termine richiesto e la restituisce.
*
* Se non fosse presente,
* effettua una chiamata al membro addNonExistingLabel
* e restituisce il termine originale da tradurre.
*
* @param lang lingua della traduzione
* @param name termine da tradurre
* @param pathContext note aggiuntive in caso non esistesse
* la traduzione.
* @return La traduzione richiesta o il termine originale.
*/
public static String translate(String lang
,String name, String pathContext){
String key = lang.trim().toUpperCase() + name.toUpperCase();
Label temp = (Label)Translator.globalDictionary.get(key);
if( temp != null && temp.getDesc() != null){
name = temp.getDesc();
}
else{
Translator.addNonExistingLabel(lang
,name.toUpperCase(),pathContext);
}
return name;
}
/**
* Svuota e ricarica il dizionario.
*/
public static void refreshDictionary(){
Translator.globalDictionary.clear();
Translator.init();
}
}
Il codice sorgente della classe Translator è molto efficiente senza essere troppo complesso.
Ottenere la traduzione di un termine è veramente cosa banale, si tratta solo di una riga di codice:
System.out.print(
Translator.translate("EN","Ciao Mondo","Test Hello World"));
Io utilizzo questa classe, incapsulandone il funzionamento in un custom tag,
nelle pagine JSP delle applicazioni che gestisco e progetto presso un cliente che offre servizi web alle proprie filiali estere. In questa maniera è stato possibile supportare il lavoro svolto all'estero fornendo lo stesso applicativo ma in lingua diversa.
Alla prossima,
MA.
0 commenti:
Posta un commento
Non ti è chiaro qualcosa?
No problem, posta il tuo dubbio ;)
..... e ricordati di firmarlo!