Progetti multilingua in Python (parte 3/9).

Attenzione!

Queste guide non sono più aggiornate, e in alcuni punti sono proprio superate.
Il mio libro Python in Windows tratta questi temi, e molto altro, in modo molto più completo e aggiornato. 

Produrre il template .pot del catalogo.

Quando avete marcato “abbastanza” stringhe per la traduzione, è ora di iniziare a produrre i file che darete ai traduttori. Non è necessario completare il codice prima di tradurlo: potete cominciare a tradurre, e poi produrre integrazioni e revisioni man mano che il lavoro va avanti. D’altra parte, i traduttori potrebbero stancarsi di star dietro alle vostre revisioni, quindi dovreste fare attenzione a non cominciare troppo presto il lavoro di produzione dei cataloghi.
L’obiettivo è produrre un file di traduzione per ciascuna lingua (oltre all’originale con cui avete scritto il codice). Il primo passo però è quello di produrre un template da cui ricavare poi i singoli file.
Tradizionalmente il template ha estensione .pot, e i singoli file per ciascuna lingua hanno lo stesso nome del template ma con estensione .po. Alcuni tool producono subito un template con estensione .po. In ogni caso, si tratta sempre di semplici file di testo.
Python dispone di un piccolo script che scansiona il vostro codice e produce un template .pot. In Windows si trova in .../Python36/Tools/i18n/pygettext.py. Per testarlo, scriviamo prima di tutto un modulo Python che riassume alcuni tipi di stringhe di cui abbiamo parlato finora:
# -*- coding: utf-8 -*-
_ = lambda i: i   # TODO eliminare in seguito...
ngettext = lambda i, j, k: i   # TODO eliminare in seguito...

def main():
    print(_('stringa semplice versione originale'))
    print(_('stringa non-ascii: èéàòì versione originale'))
    print(_('stringa interpolata {} versione originale').format(123))
    print(_("""stringa
            multilinea
            versione originale"""))
    num = 12
    s = ngettext('Stringa singolare {} versione originale', 
                 'Stringa plurale {} versione originale', num)
    print(s.format(num))

    # translators: questo e' un commento
    print(_('stringa commentata versione originale'))

if __name__ == '__main__':
    main()
Chiamate questo file test.py, fatelo girare e controllate che in effetti funziona: senza gettext né traduzioni ancora installate, semplicemente i vari print producono le stringhe originali, interpolate dove occorre.
Quindi, dalla shell invocate pygettext.py:
> py "C:/Program Files/Python36/Tools/i18n/pygettext.py" -d myapp test.py
Se non volete scrivere ogni volta tutto il percorso potete aggiungere la directory alla PYTHONPATH, creare un collegamento o semplicemente copiare lo script nella directory corrente. L’opzione -d specifica il “dominio” del catalogo da creare: in Windows come vedremo questo non è molto importante, ma per compatibilità con il mondo Linux conviene specificarlo. In pratica potete passare il nome della vostra applicazione (qui myapp): l’effetto concreto è che il catalogo prodotto sarà myapp.pot. Infine, passate come argomenti i nomi dei file che volete scansionare. Se avete marcato più di un file, non vi conviene generare cataloghi separati per ciascuno: passate invece tutti i nomi dei file, in successione (pygettext.py -d myapp foo.py bar.py baz.py). Ci sono altre opzioni che potete scoprire con la documentazione online (pygettext.py --help).
Il template myapp.pot sarà creato nella directory corrente. Apritelo con un editor e trovere qualcosa del genere:
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR ORGANIZATION
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2018-03-31 19:06+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=cp1252\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: pygettext.py 1.5\n"


#: test.py:7
msgid "stringa semplice versione originale"
msgstr ""

#: test.py:8
msgid "stringa non-ascii: èéàòì versione originale"
msgstr ""

#: test.py:9
msgid "stringa interpolata {} versione originale"
msgstr ""

#: test.py:10
msgid ""
"stringa\n"
"            multilinea\n"
"            versione originale"
msgstr ""

#: test.py:21
msgid "stringa commentata versione originale"
msgstr ""
Il vostro compito adesso è modificare questo template, prima di copiarlo e distribuirlo ai traduttori. Nelle prime righe di commento, sostituite il titolo del progetto e i metadati relativi: potete aggiungere righe di commento a piacere. Il resto del file è composto da coppie msgid / msgstr che contengono rispettivamente le stringhe originali estratte dal vostro codice, e le traduzioni da inserire. Le stringhe sono delimitate da virgolette doppie (escapabili all’occorrenza: \"). Una stringa multilinea si crea dalla concatenazione di più stringhe successive (senza righe bianche in mezzo).
La prima coppia msgid / msgstr è destinata per convenzione a “tradurre” la stringa vuota "": la “traduzione” contiene ancora dei metadati, alcuni dei quali spetta al traduttore completare. La cosa importante da notare è la riga:
"Content-Type: text/plain; charset=cp1252\n"
In effetti pygettext produce file .pot nell’encoding del sistema (per un Windows italiano, cp1252 appunto) e non in utf-8. Questo succede anche se la dichiarazione di encoding nei moduli Python è per utf-8. Potete controllare con il vostro editor che in effetti il file .pot è salvato in cp1252.
Qui si pone un problema, specialmente in Windows. Se il traduttore siete voi stesso, fate un po’ come credete. Ma se distribuite il file a un traduttore esterno, dovete capire che probabilmente lo aprirà con il Blocco Note, se non addirittura con Word. In questo caso, l’encoding cp1252 tutto sommato potrebbe andar bene: è difficile spiegare a un non-tecnico come aprire e salvare file utf-8 con il Blocco Note. Tenete conto però che se mandate il file a un traduttore all’estero, non è detto che il suo Blocco Note usi cp1252 di default (già nei Paesi dell’Est Europa, per esempio, non è così).
È una questione di convenienza, vostra e dei traduttori. Se preferite avere utf-8, non vi resta che cambiare l’encoding del file con il vostro editor, e ri-salvarlo. In questo caso, correggete per scrupolo anche l’indicazione del charset dentro il file. Dovete però spiegare ai vostri traduttori come usare un file encodato in utf-8 (o forse, meglio ancora, dire loro di installare un editor come Sublime Text o NotePad++ che lavorano nativamente in utf-8, e lasciar perdere il Blocco Note). Va detto comunque che esistono anche degli editor grafici specializzati, riservati ai traduttori, che permettono di lavorare sui cataloghi senza aprire direttamente il file: ne riparleremo.
Le successive coppie msgid / msgstr contengono le stringhe da tradurre, precedute da una riga di commento che indica il file e la riga di provenienza (ai traduttori non importa, ma a voi può far comodo). Notate che, come abbiamo detto, pygettext non supporta i commenti “translator” che avete inserito nel codice. Inoltre pygettext non supporta le stringhe plurali.
Per il momento non dovete toccare nulla oltre ai metadati. Una volta apportate le modifiche opportune, salvate e chiudete il template .pot.

Commenti

Post più popolari