Come tradurre un’applicazione Pylons

Applicazioni web in PythonUno degli ultimi passaggi nello sviluppo di un’applicazione web è la traduzione e localizzazione in altre lingue, anche se per motivi tecnici sarebbe meglio progettare l’applicazione fin da subito per essere facilmente traducibile, anziché modificarla a progetto terminato.

Vediamo come rendere traducibile un’applicazione pylons.

Il modulo i18n

Il framework pylons mette a disposizione il modulo i18n per aiutarci nella traduzione dell’applicazione (i18n è un’abbreviazione che si può interpretare come “internationalization“, con la cifra 18 che rappresenta le 18 lettere omesse).

In questo modulo sono presenti diverse funzioni utili, ma quella che useremo nel 99% dei casi è ugettext(), o la sua forma abbreviata _().

Questo metodo “marca” una stringa come testo da tradurre e restituisce una stringa unicode (gettext() è la sua controparte non-unicode).

Il primo passo è importare le funzioni del modulo:

from pylons.i18n.translation import *

e l’uso è altrettanto semplice:

def index(self):
  return _("Traduci questo!")

Ovviamente da qualche parte sul nostro sito ci sarà la possibilità di scegliere la lingua e nel codice dovremo usare la funzione set_lang() per impostarla come attiva.
La funzione accetta una stringa rappresentante la lingua principale da usare o una lista di stringhe. In tal caso la prima della lista sarà quella principale e le altre saranno utilizzate come riserve se non viene trovata una traduzione valida.

set_lang(["it", "en"])

Ovviamente esiste anche get_lang() per recuperare la lingua selezionata.

Errori da evitare

Un errore in cui potreste incappare se non avete esperienza di traduzioni è inserire valori variabili all’interno della stringa da tradurre, ad esempio:

# Sbagliato
_("Sono stati trovati "+len(results)+" risultati")

# Sbagliato
_("Sono stati trovati %d risultati" % len(results))

# Corretto
_("Sono stati trovati %d risultati") % len(results)

O anche di tradurre stringhe parziali e poi concatenarle:

# Sbagliato
message =  _("Benvenuto sul nuovo")
message += _("sito di Artera")

# Corretto
message = _("Benvenuto sul nuovo"
            "sito di Artera")

Altre funzioni utili

Un’utile variante di ugettext è ungettext che serve a fornire la doppia traduzione per il singolare e il plurale in base a un valore passato come parametro.

def index(self):
  return ungettext("E' stato trovato %n risultato", "Sono stati trovati %n risultati", len(results)) % len(results)

Estrarre le stringhe da tradurre

Abbiamo visto come “marcare” le stringhe all’interno dell’applicazione ma per fornire le traduzioni effettive nelle varie lingue che desideriamo supportare, dobbiamo come prima cosa estrarle in un formato comodo, così da non doverci rigirare tutti i sorgenti.

Lo strumento che ci serve è babel, installiamolo con pip:

pip install babel

Babel provvederà a estrarre per noi le stringhe, in una cartella i18n che va creata manualmente:

$ mkdir myapp/i18n

il comando da eseguire per l’estrazione è:

$ python setup.py extract_messages
[...]
writing PO template file to myapp/i18n/myapp.pot

Il file .pot è quello che ci serviva e da questo dobbiamo generare un .po per ogni lingua:

$ python setup.py init_catalog -l en
running init_catalog
creating catalog 'myapp/i18n/en/LC_MESSAGES/myapp.po'
based on 'myapp/i18n/myapp.pot'

Il file .po conterrà 3 righe per ogni stringa da tradurre:

  • Un commento recante il file sorgente e la riga presso la quale è stata trovata la stringa.
  • Il msgid, ovvero la stringa originale
  • La msgstr da modificare inserendo la stringa tradotta

Modificati i file .po li compiliamo in .mo col comando:

$ python setup.py compile_catalog
running compile_catalog
1 of 1 messages (100%) translated in 'myapp/i18n/en/LC_MESSAGES/myapp.po'
compiling catalog 'myapp/i18n/en/LC_MESSAGES/myapp.po' to 'myapp/i18n/en/LC_MESSAGES/myapp.mo'

e l’applicazione è tradotta!

Ricordiamoci solo di aggiungere la cartella i18n al controllo di versione se ne usiamo uno.