Creazione di un'applicazione multilingua
Posted by reggie, Thu Apr 26 22:13:00 UTC 2007

Ora che abbiamo affrontato a grandi linee alcuni degli aspetti fondamentali che ci permettono di gestire la globalizzazione all’interno di una Rails application, cerchiamo di approfondire il discorso costruendo un’applicazione di esempio, caratterizzata da una semplice gestione di vocabolari cartacei che vogliamo vendere descritti in diverse lingue. Questo ci permetterà di introdurre nuovi aspetti molto interessanti, che probabilmente incontreremo nel momento in cui costruiremo qualcosa di realmente funzionante.
Diamo il via alle danze
Iniziamo con la creazione di una semplicissima tabella nella quale inserire il nome, la descrizione ed il prezzo dei nostri vocabolari. Per la creazione della tabella utilizzeremo una delle caratteristiche più uniche che Rails possa offrire: la migration. Quindi la prima cosa da fare è creare una classe all’interno della quale poterla definire e per farlo ci viene in aiuto il generatore migrate.
1 2 3 4 5 6 |
# creazione della classe in cui definire la migrazione E:\dictionary> ruby script/generate migration Dictionary # file e path generati dalla migrazione E:\dictionary> create db/migrate E:\dictionary> create db/migrate/001_dictionary.rb |
Come possiamo vedere è stato generato il nuovo file 001_dictionary.rb ed è proprio qui che noi andremo a definire la struttura della nostra tabella. Sarà poi il comando rake a creare in modo definitivo la tabella all’interno del database.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# Estratto del file db/migrate/001_dictionaries_table.rb class Dictionary < ActiveRecord::Migration # definizione caricamento della tabella dictionaries def self.up create_table :dictionaries do |t| t.column :name, :string t.column :description, :text t.column :price, :integer end end # definizione rimozione della tabella dictionaries def self.down drop_table :dictionaries end end |
1 2 |
# caricamento della tabella dictionaries E:\dictionary> rake migrate |
Ora che abbiamo creato la nostra tabella creiamo, all’interno della nostra applicazione, una struttura che ci permetta di gestire l’inserimento dei dizionari.
1 2 |
# scaffolding della tabella dictionary E:\dictionary> ruby script/generate scaffold Dictionary |
Via alla traduzione
Quello che ora vogliamo fare è tradurre questi record in più lingue, o almeno tradurre una parte di essi. Apportiamo quindi le modifiche necessarie al modello dictionary.
1 2 3 4 5 6 7 8 9 10 11 12 |
# Estratto del file app/models/dictionary.rb class Dictionary < ActiveRecord::Base # definizione traduzione dei campi description e price translates :description, :price # gestione della traduzione del campo price # ponendo la sua gestione come campo valuta composed_of :price, :class_name => "Globalize::Currency", :mapping => [ %w(price cents) ] end |
Come possiamo vedere, abbiamo richiesto la gestione multilingua dei campi description e price. Abbiamo poi utilizzato anche un nuovo metodo composed_of, il quale ci permetterà di gestire campi di tipo valuta. All’interno di Rails questa tecnica viene chiamata aggregazione e permette di associare ad un campo della nostra tabella un oggetto Ruby più o meno complesso. Nel nostro esempio all’interno del campo price non facciamo altro che settare un oggetto di tipo Currency (definito dal plugin Globalize) che come vedremo gestirà in automatico la formattazione della valuta a seconda del locale attivo. Proviamo ora ad inserire qualche record nella nostra tabella per verificare che tutto funzioni correttamente.

Figura 4: Errore durante la creazione di un record all’interno della tabella dictionary
Qualcosa è andato storto. Come possiamo vedere dalla figura 4 risulta esserci qualche problema, quindi cerchiamo di capire quale possa essere la causa esaminando il messaggio che viene visualizzato nel browser. Questo consiste principalmente in una stringa che afferma la mancata definizione del metodo cents per la stringa “1111”.
1 2 |
# messaggio di errore principale undefined method 'cents' for "1111":String |
In effetti noi non vorremmo richiamare il metodo cents su di un oggetto String (come mostrato dall’errore), ma su di un oggetto di tipo Currency, quindi dobbiamo creare un metodo che faccia questa conversione prima della creazione di un nuovo oggetto Dictionary (il modello che rappresenta il nostro record).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
# Estratto del file app/controllers/dictionary_controller.rb class Dictionary < ActiveRecord::Base # chiamata del metodo set_currency prima di andare # ad eseguire i metodi create ed update before_filter :set_currency, :only => [:create, :update] # passaggio del campo price da String a Currency prima della creazione def create ... end # passaggio del campo price da String a Currency prima della modifica def update ... end private # definizione dell'oggetto Currency sul campo price def set_currency params[:dictionary][:price] = Currency.new(params[:dictionary][:price]) end end |
Siamo quindi pronti a vedere i primi record inseriti all’interno della nostra tabella, e se tutto è stato fatto correttamente, il risultato ottenuto sarà simile a quello di figura 5, in cui possiamo vedere come il prezzo sia stato formattato inserendo automaticamente il simbolo dell’euro e la virgola per separare le due cifre decimali.

Figura 5: Lista dei dizionari inseriti in lingua italiana
Traduciamo le nostre informazioni in lingue diverse
Quello che manca ora è generare delle traduzioni in altre lingue, ad esempio in inglese. Nulla di più semplice. Ci basterà entrare all’interno dell’azione index del controller dictionary, settare il locale desiderato, per poi andare a modificare i record che desideriamo tradurre. Il risultato ottenuto sarà molto interessante, tenendo presente che tutte le associazioni tra i contenuti dei record e le traduzioni saranno gestiti in modo automatico dal magico Globalize.
1 2 3 4 5 6 7 |
# Estratto del file app/controllers/dictionary_controller.rb ... def list Locale.set('en-US') # cambio del locale di default @dictionary_pages, @dictionaries = paginate :dictionaries, :per_page => 10 end ... |
Se andassimo ad aggiornare ora la lista dei vocabolari inseriti nella nostra applicazione noteremmo subito la variazione della formattazione del prezzo (anche se il prezzo rimane sempre lo stesso), mentre le descrizioni rimarrebbero uguali a quelle inserite in lingua italiana. Quello che vogliamo fare ora è quindi dare una descrizione ed un prezzo proprio per la lingua inglese, e questo per ogni record inserito. Per farlo dobbiamo semplicemente andare in modifica del record e inserire la nuova descrizione ed il nuovo prezzo. Nel momento in cui torneremo nella pagina principale, vedremo le descrizioni in lingua inglese ed i nuovi prezzi(figura 6).

Figura 6: Lista dei dizionari inseriti in lingua inglese
Se a questo punto andassimo a cambiare il locale inglese con quello italiano, ci ritroveremmo di nuovo con le traduzioni in lingua italiana. Abbiamo quindi raggiunto il nostro scopo. In questo piccolo esempio abbiamo fatto la traduzione in sole due lingue, ma nulla ci vieta di fare traduzioni in tutte le lingue che desideriamo, semplicemente seguendo i due passi sopra descritti, cioè:
- settare il locale nella lingua che vogliamo tradurre;
- andare a modificare i record.
Se andiamo un pochino a fondo, anche in questo caso il funzionamento che sta alla base della traduzione risulta essere decisamente semplice. Come possiamo vedere dalla figura 7, la traduzione multilingua si basa principalmente sui seguenti campi:
facets: indica la colonna della tabelladictionariesda tradurre;item_id: è l’identificativo del record da tradurre;text: contiene la traduzione nella lingua definita dal campolanguage_id.

Figura 7: Traduzione dei models all’interno della tabella globalize_translations
Settaggio del locale in un’applicazione reale
Arrivati a questo punto siamo abbastanza soddisfatti, ma desideriamo fare qualcosina in più. In effetti dover cambiare di volta in volta il locale all’interno della nostra azione index non è la soluzione più comoda, quindi forniremo alla nostra applicazione un’interfaccia ed un insieme di metodi che permettano un cambio veloce della lingua desiderata. Iniziamo quindi la creazione del metodo set_locale, il quale, come si può intuire dal nome, ci permetterà di cambiare il locale attivo in modo semplice ma completo.
1 2 3 4 5 6 7 8 9 10 11 |
# Estratto del file app/controllers/application_controller.rb class ApplicationController < ActionController::Base # settaggio di un nuovo locale attivo def set_locale if params[:locale] Locale.set params[:locale] end # Ridirezione alla pagina principale dell'applicazione redirect_to(:controller => "dictionaries", :action => "index") end end |
Fatto questo, dobbiamo semplicemente creare due link che vadano a richiamare questo nuovo metodo, permettendo così il cambio della lingua della nostra applicazione. Inoltre visualizzeremo con un’altro messaggio quale sia il locale attivo in un determinato momento. Tutte queste modifiche le inseriremo all’interno del layout.
1 2 3 4 5 6 7 |
# Estratto del file app/views/layouts/dictionary.rhtml ... <body> <%= link_to 'Italiano', :action => 'set_locale', :locale => 'it-IT' %> | <%= link_to 'English', :action => 'set_locale', :locale => 'en-US' %> | <span style="color: green"> Locale attivo: <%= Locale.active.code %> </span> ... |
Non dimenticate di cancellare Locale.set dal metodo list. Vediamo ora il risultato a cui siamo arrivati (figura 8) in cui troviamo i due nuovi link Italiano ed English che ci permetteranno di passare da un linguaggio all’altro.

Figura 8: Risultato delle traduzioni multilingua
Ma se voglio differenziare singolare e plurale
Prima di concludere introduciamo un ultimo comando che potrebbe essere utile durante lo sviluppo della nostra applicazione. Per la nostra applicazione di esempio potrebbe risultare utile inserire un messaggio nel quale vengano elencati quanti siano i vocabolari messi a disposizione. In questo caso non ci basta effettuare solo la traduzione, ma dobbiamo differenziare la notazione singolare (c’è 1 vocabolario) dalla notazione plurale (ci sono n vocabolari). Chiariamo l’idea con un esempio, non completo, ma che ci fa capire il meccanismo nella pratica
1 2 3 |
# Estratto del file app/views/layouts/dictionary.rhtml <body> <%= "Ci sono %d vocabolari nella tua pagina" / @number_of_dictionaries %> ... |
Naturalmente per ottenere il risultato desiderato dobbiamo anche effettuare la traduzione della lingua dall’italiano all’inglese, e gestire anche le differenze tra la forma singolare e la forma plurale. Come avvenuto in precedenza anche in questo caso useremo il metodo set_translation, però con una piccola variazione.
1 2 3 4 5 |
# Estratto del file app/controllers/dictionary.tb Locale.set_translation("Ci sono %d vocabolari nella tua pagina", Language.pick('en-US'), # lingua in cui tradurre "There is one dictionary in your page", # vers. singolare "There are %d items in your page")# vers. plurale |
Fatto questo il risultato ottenuto nel caso in cui avessimo un solo item sarà simile al seguente.
There is one item in your page |
Conclusioni
Riassumendo abbiamo dato un’occhiata a come sia possibile sfruttare la potenza di Rails e di Globalize per creare un’applicazione multilingua reale. Naturalmente si tratta di un semplice esempio e sono molte le cose che non abbiamo affrontato. Per chi desiderasse approfondire qui di seguito ci sono i link che ho utilizzato e nei quali ho trovato tutte le informazioni per scrivere questo piccolo tutorial.
Per chi desidera approfondire la conoscenza di Globalize Per chi desidera chiarire un pochino di terminologiaLicenza
Questo articolo è coperto dalla licenza Creative Commons Attribution-NonCommercial-NoDerivs 2.5 Italy.