Personal tools
Internazionalizzazione e javascript a braccetto in Plone

Javascript & i18n? Plone ti da una mano!

Nov 21, 2012

Internazionalizzazione e javascript a braccetto in Plone

Ci sono un italiano, uno spagnolo, un francese e un inglese che... vogliono visitare il nostro sito multilingua in Plone. Come trattiamo le traduzioni se c'è tanto javascript?

Capita di dover sviluppare siti che sfruttano javascript per presentare dati e testi. Se il sito è multilingua, anche questi dati e questi testi dovranno essere tradotti coerentemente con la lingua in cui si sta visualizzando il sito.

Se le parole e le frasi da tradurre sono poche, questo compito può essere portato a termine tramite un dizionario. Supponiamo infatti di dover aprire un alert con un messaggio. Sarà sufficiente avere qualcosa di questo tipo:

// Tramite jQuery si prende la lingua corrente dall'attributo del tag html
var lang = jq('html').attr('lang');

// si dichiara il vocabolario con le traduzioni nelle varie lingue var messages = {'it': 'Benvenuti',
'en': 'Wellcome',
'es': 'Bienvenido',
'fr': 'Bienvenue'};

// apriamo l'alert scegliendo la traduzione opportuna. alert(messages[lang]);

Se la pagina web che stiamo creando ha molte parti caricate via javascript, e quindi molti testi e parole da presentare tradotte, questo approccio è scomodo; innanzitutto perché si rischia di dover mantere molto codice solo per le traduzioni.
Se siamo in un caso come questo, il sito sviluppato probabilmente è già multilingua e i dati verranno presentati correttamente. Per quanto riguarda i testi da mostrare in javascript, è meglio sfruttare un motore di traduzioni, e in Plone, grazie alla community, un'utility di questo tipo esiste già: jarn.jsi18n. Grazie a questo prodotto, infatti, è possibile sfruttare appieno il message factory di plone e tutti i suoi file di traduzioni.

Come usarlo

Il funzionamento è semplice. Una volta installato il prodotto nel sito, tutto quello che dobbiamo fare è caricare un catalogo e creare un message factory nel javascript che scriviamo:


// Tramite jQuery si ottiene la lingua corrente dall'attributo del tag html
var lang = jq('html').attr('lang');

// Dato un dominio di traduzioni e una lingua si ottiene un catalogo dal server;
// questo restituirà al browser un vocabolario tipo
// {...
"from_date": "Dal", "to_date": "Al", ...}
jarn.i18n.loadCatalog('fta.at', lang);


// Creiamo un message factory javascript che sfrutta il catalogo restituito dal server
mf = jarn.i18n.MessageFactory('fta.at');

Da qui in poi si potrà procedere utilizzando il message factory in modo molto 'plonish':


// Si può iniziare a tradurre stringhe
> mf('How to get here')
:
"Come arrivare"

E se abbiamo dei testi da costruire dinamicamente: 

 
// Si possono tradurre anche stringhe in modo più dinamico, esattamente come in plone.
> mf('other_results')
;
"... e altre ${results} voci"


> mf('other_results', {'results': 10})
;
"... e altre 10 voci"


Under the hood

Come funziona tutto questo? Guardando il codice, ci si può rendere conto di come cose utili e funzionali possano essere create in modo semplice. Il pacchetto si compone di una vista e di un javascript. La vista è una classica BrowserView Plone che restituirà una stringa json con i dati richiesti:

 
def __call__(self, domain, language):
...

# Si ottiene l'utility plone per le traduzioni
td = queryUtility(ITranslationDomain, domain)
if td is None or language not in td._catalogs:
return

# Si ottiene il catalogo per il dominio nella lingua richiesta
mo_path = td._catalogs[language][0]
catalog = td._data[mo_path]._catalog
if catalog is None:
td._data[mo_path].reload()
catalog = td._data[mo_path]._catalog
...

# si restituisce il catalogo in formato json.
# Qualcosa del tipo: '{..."from_date": "Dal", "to_date": "Al", ...}'
response.setBody(json.dumps(catalog))
return response

Il javascript si occupa della chiamata al server per ottenere il catalogo che verrà gestito interamente lato client: una cosa molto interessante in questa gestione è, nei browser che lo consentono, l'uso dello storage del browser, tramite cui si riesce a fare caching di questo catalogo e non ricaricarlo ogni volta che si chiama una pagina:

 
// si salva il catalog nello storage con una chiave associata
var key = domain + '-' + language;

// se abbiamo già quella chiave nello storage verificheremo se il catalogo è ancora valido.
if (key in localstorage) {
if ((Date.now() - parseInt(localstorage.getItem(key + '-updated'), 10)) < ttl) {
var catalog = JSON.parse(localstorage.getItem(key));
jarn.i18n._setCatalog(domain, language, catalog);
return;
}
}

Come sempre, conoscere bene l'architettura e i componenti di Plone permette di svolgere lavori più o meno complicati in modo semplice, ma soprattutto pulito.

Filed under: ,
comments powered by Disqus