I file .PO e .MO sono generati nell’ambito del processo di internazionalizzazione di un sito, quando si applica l’approccio gettext.
Cos’è un file .PO
Un file con estensione .PO (Portable Object) è un dizionario bilingue contenente tutti i testi di un sito web (o di un’applicazione software) e le relative traduzioni.
Ad esempio, un file .PO potrebbe contenere i seguenti testi:
msgid "Your Name"
msgstr "Il tuo nome"
msgid "Your email"
msgstr "La tua casella di posta elettronica"
Dove le righe che iniziano con msgid indicano i testi nella lingua originale del sito (generalmente la lingua inglese) e le righe che iniziano con msgstr le loro traduzioni (nel caso specifico, in lingua italiana).
Un sito web avrà quindi tanti file .PO quante sono le lingue in cui viene tradotto (ovvero le sue “localizzazioni”).
Cos’è un file .MO
Il sito web non utilizza direttamente il file .PO, bensì una sua versione “compressa”, ovvero un file con estensione .MO (Machine Object), in cui i testi vengono ottimizzati per essere “letti” da un computer e non da un essere umano.
Il file .MO viene generato a partire dal file .PO, dopo che in quest’ultimo sono state inserite tutte le traduzioni.
Esiste quindi un file .MO per ogni file .PO del sito.
Cosa sono le utility gettext
Le utility GNU gettext sono un pacchetto di strumenti volti a semplificare le attività di internazionalizzazione delle le applicazioni software e dei siti web.
Gli strumenti gettext vengono utilizzati in più momenti del processo di internazionalizzazione del sito:
| Fase | Ruolo di gettext |
|---|---|
| Durante lo sviluppo di un sito web vengono individuati all’interno del codice sorgente i testi che dovranno essere tradotti a beneficio degli utenti finali. Ciascun testo viene sostituito da una funzione che, partendo dal testo nella lingua originale, restituisca la sua traduzione nella lingua dell’utente. | Gettext mette a disposizione un set di funzioni per la traduzione dei testi (vedere di seguito il paragrafo Cosa sono le funzioni gettext). |
| Al termine dello sviluppo, un apposito programma (un “parser” del codice sorgente) estrae i testi da tradurre e li inserisce in un file con estensione .POT (Portable Object Template), che farà da “modello” per tutti i successivi file .PO. | il comando di linea xgettext estrae i testi da tradurre genera il file .POT. |
| Il file modello viene utilizzato per predisporre i file che conterranno le traduzioni del sito, un file per ogni lingua. Questi nuovi file hanno estensione .PO (Portable Object) e al momento della loro creazione contengono i testi da tradurre, ma non le loro traduzioni (vengono “Inizializzati”). | il comando di linea msginit inizializza un file .PO partendo dal file .POT |
| La persona incaricata di tradurre il sito in una specifica lingua modifica il file .PO di competenza, inserendo le traduzioni. | |
| Al termine della traduzione, ciascun file .PO viene ottimizzato per poter essere utilizzato da un software (e non da un essere umano). Vengono quindi creati i corrispondenti file con estensione ,MO (Machine Object). | il comando di linea msgfmt crea un file .mo partendo dal file .po contenente le traduzioni |
| I diversi file .mo vengono uploadati sul sito web, dove saranno interrogati dalle funzioni gettext per recuperare le traduzioni. | |
| Qualora vengano successivamente eseguite delle modiche software, viene fatta una nuova estrazione dei testi da tradurre, che andrà ad integrare il precedente file .POT | il comando di linea xgettext -j integra un file .pot esistente |
| Viene quindi crea un nuovo file .PO, unendo le traduzioni già presenti in un precedente file .PO ai testi presenti nel nuovo file .POT (creato con il comando xgettext -j ). Si ripetono quindi le attività di creazione dei file .MO e di trasporto sul sito web. | il comando di linea msmerge integra i nuovi testi con le traduzioni già presenti in un file .PO precedente |
Tabella riepilogativa dei principali strumenti gettext
Riassumendo:
| strumento | Utilizzo |
|---|---|
| funzioni gettext | vengono inserite nel codice dell’applicazione e restituiscono un testo tradotto nella lingua dell’utente, (recuperano le traduzioni dai dizionari .mo) |
| comando xgettext | crea il file .pot partendo dal codice dell’applicazione |
| comando msginit | inizializza un file .po partendo dal file .pot |
| comando msgfmt | crea un file .mo partendo dal file .po contenente le traduzioni |
| comando xgettext -j | integra un file .pot esistente |
| comando msmerge | crea un file .po unendo le traduzioni già presenti in un precedente file .po ai testi presenti in un nuovo file .pot |
Gli strumenti gettext sono più numerosi di quelli qui indicati; per un elenco esaustivo, vedere l’appendice “Program index” del manuale GNU gettext.
Cosa sono le funzioni gettext
Nel corso dello sviluppo, quando il programmatore individua un testo destinato all’utente lo inserisce all’interno di una funzione gettext che, partendo dal testo nella lingua originale, restituisca la sua traduzione nella lingua dell’utente.
Le funzioni gettext sono state scritte originariamente per il linguaggio C, ma l’approccio gettext alla internazionalizzazione è stato poi adottato da numerosi linguaggi, tra cui il linguaggio PHP. Gli esempi di seguito riportati fanno riferimento al linguaggio C.
La funzione textdomain()
Un sito complesso potrebbe essere costituito da diverse componenti, ciascuna con la propria localizzazione, per evitare sovrapposizioni, ciascuna componente utilizza il proprio dizionario bilingue, definito “dominio dei nomi” (in inglese: "textdomain"). Anche il sito di per sé avrà un proprio dominio dei nomi, che costituisce il dominio predefinito.
La funzione textdomain() imposta il dominio predefinito al valore del parametro domain_name:
char *textdomain (const char *domain_name);
La funzione gettext()
La funzione gettext() restituisce la traduzione di un testo passato come parametro, prelevandola dal dominio predefinito:
char *gettext (const char *msgid);
La funzione gettext() può essere utilizzata anche nella sua forma abbreviata _(). L’istruzione C:
str = gettext ("Hello world");
equivale a:
str = _("Hello world");
La funzione dgettext()
La funzione dgettext() equivale alla gettext(), con la differenza che non utilizza il dominio dei nomi predefinito, bensì il dominio passato come parametro:
char *dgettext (const char *domain_name, const char *msgid);
Dove domain_name è il nome del dominio da utilizzare e msgid il testo da tradurre.
La funzione dcgettext()
La localizzazione di un sito non riguarda solo la traduzione di testi. Ogni nazione (e ogni lingua) può avere particolari convenzioni in fatto di formattazione dei numeri, valute o date (ad esempio, in lingua italiana il carattere separatore tra parte intera e parte decimale di un numero è una virgola, mente nelle lingue anglosassoni è un punto).
La funzione dcgettext(), oltre ad indicare il dominio, indica anche che il testo da tradurre appartiene ad una specifica categoria (messaggi, date, numeri, valute, ecc):
char *dcgettext (const char *domain_name, const char *msgid, int category);
Dove domain_name è il nome del dominio da utilizzare, msgid il testo da tradurre e category indica la categoria a cui appartiene il testo da tradurre.
Effetti sul file .POT
Le funzioni fin qui elencate, nel momento in cui viene generato il file .POT producono 4 record:
- una riga di commento (che inizia con i caratteri
#:) indicante il file e la riga in cui compare il testo da tradurre - una riga
msgidcon il testo da tradurre - una riga
msgstrvuota (conterrà il testo tradotto nella lingua appropriata su ciascun file .PO) - una riga vuota di separazione.
Ad esempio, l’istruzione:
description = _( "Validation errors occurred");
produrrà il seguente output sul file .POT:
#: percorso/nomefile:riga dov'è inserito il testo da tradurre
msgid "Validation errors occurred"
msgstr ""
Le funzioni che trattano il contesto
Soprattutto nel caso di singoli termini o brevi frasi, può succedere che uno stesso testo debba essere tradotto in modo diverso in base al contesto in cui viene utilizzato; le seguenti funzioni permettono di indicare il contesto a cui si deve fare riferimento.
La funzione pgettext()
La funzione pgettext() equivale alla gettext(), con la differenza che permette di indicare il contesto a cui fare riferimento:
const char *pgettext (const char *msgctxt, const char *msgid);
Dove msgctxt è il contesto da utilizzare e msgid il testo da tradurre.
La funzione dpgettext()
La funzione dpgettext() permette di indicare uno specifico dominio e il contesto a cui fare riferimento:
const char *dpgettext (const char *domain_name, const char *msgctxt, const char *msgid);
Dove domain_name è il dominio, msgctxt è il contesto da utilizzare e msgid il testo da tradurre.
La funzione dcpgettext()
La funzione dcpgettext() infine permette di indicare uno specifico dominio, il contesto a cui fare riferimento e la categoria a cui appartiene il testo da tradurre:
const char *dcpgettext (const char *domain_name, const char *msgctxt, const char *msgid, int category);
Dove domain_name è il dominio, msgctxt è il contesto da utilizzare, msgid il testo da tradurre e category la categoria a cui appartiene il testo da tradurre.
Effetti sul file .POT
Le funzioni che trattano il contesto introducono un nuovo record nel file .POT:
- una riga
msgctxtcon il nome del contesto.
Ad esempio, l’istruzione:
description = pgettext( "Constant Contact", "Validation errors occurred");
produrrà il seguente output sul file .POT:
#: percorso/nomefile:riga dov'è inserito il testo da tradurre
msgctxt "Constant Contact"
msgid "Validation errors occurred"
msgstr ""
Le funzioni che trattano la forma plurale
Quando un messaggio contiene un valore numerico, si presenta la necessità di gestirne sia la forma singolare che quella plurale. Le seguenti funzioni permettono di trattare questi casi.
La funzione ngettext()
La funzione ngettext() accetta i seguenti parametri:
- msgid1: il testo da tradurre se il numero passato è singolare
- msgid2: il testo da tradurre se il numero passato è plurale
- n: il numero da utilizzare per decidere quale testo da tradurre
La funzione restituirà la traduzione di msgid1 se nella specifica lingua n è considerato singolare e la traduzione di msgid2 nel caso contrario.
char * ngettext (const char *msgid1, const char *msgid2, unsigned long int n)
La funzione dngettext()
La funzione dngettext() è come la precedente, con in più la possibilità di specificare un dominio:
char * dngettext (const char *domain, const char *msgid1, const char *msgid2,
unsigned long int n)
La funzione dcngettext()
La funzione dcngettext() è come la precedente, con in più la possibilità di specificare la categoria:
char * dcngettext (const char *domain, const char *msgid1, const char *msgid2,
unsigned long int n, int category)
Effetti sul file .POT
Le funzioni che trattano la forma plurale producono 5 record sul file .POT:
- una riga di commento (che inizia con i caratteri
#:) indicante il file e la riga in cui compare il testo da tradurre - una riga
msgidcon il testo da tradurre nella forma singolare - una riga
msgid_pluralcon il testo da tradurre nella forma plurale - una riga
msgstr[0]vuota (conterrà il testo singolare tradotto nella lingua appropriata su ciascun file .PO) - una riga
msgstr[1]vuota (conterrà il testo plurale tradotto nella lingua appropriata su ciascun file .PO) - una riga vuota.
Ad esempio, l’istruzione :
ngettext (
'%s configuration error detected',
'%s configuration errors detected',
count_errors
)
produrrà il seguente output sul file .POT:
#: percorso/nomefile:riga dov'è inserito il testo da tradurre
msgid "%s configuration error detected"
msgid_plural "%s configuration errors detected"
msgstr[0] ""
msgstr[1] ""
Tabella riepilogativa delle principali funzioni gettext
Riassumendo:
| funzione | Utilizzo |
|---|---|
| gettext() | fornisce la traduzione del testo originale, utilizzando il dominio dei nomi predefinito |
| dgettext() | fornisce la traduzione del testo originale, utilizzando il dominio passato come parametro |
| dcgettext() | fornisce la traduzione del testo originale, utilizzando il dominio passato come parametro e indicando la categoria a cui appartiene il testo da tradurre |
| pgettext() | come gettext(), con la possibilità di indicare il contesto di riferimento |
| dpgettext() | come dgettext(), con la possibilità di indicare il contesto di riferimento |
| dcpgettext() | come dcgettext(), con la possibilità di indicare il contesto di riferimento |
| ngettext() | come gettext(), con la possibilità di gestire anche la forma plurale |
| dngettext() | come dgettext(), con la possibilità di gestire anche la forma plurale |
| dcngettext() | come dcgettext(), con la possibilità di gestire anche la forma plurale |
Il formato dei file .POT e .PO
La seguente tabella fornisce un riepilogo dei possibili record presenti nei file .POT (e .PO), la maggior parte di essi è già stata descritta nei paragrafi precedenti
| keyword del record | significato |
|---|---|
| # (translator-comments) | indica un commento inserito dal traduttore |
| #. (extracted-comments) | indica un commento estratto dal codice (vedi paragrafo successivo) |
| #: (reference) | indica il percorso/nome-file:riga del programma in cui compare il testo da tradurre |
| #, (flag) | permette di specificare alcune opzioni (flag), come, ad esempio, se la traduzione è ancora approssimativa (fuzzy) oppure no. Maggiori dettagli nel manuale GNU gettext, al capitolo 3. |
| #| msgctxt (previous-context) | indica un contesto presente in una precedente versione del file |
| msgctxt (context) | indica il contesto a cui si riferisce il testo da tradurre |
| #| msgid (previous-untranslated-string) | indica un testo da tradurre presente in una precedente versione del file |
| msgid (untranslated-string) | indica il testo da tradurre |
| msgstr (translated-string) | nel file .POT è vuoto, nel file .PO indica la traduzione |
| riga vuota | riga di separazione tra una coppia di record msgid/msgstr e la successiva |
| —–per i testi che prevedono la forma plurale:—– | |
| | msgid (previous-untranslated-string-singular) | indica un testo da tradurre presente in una precedente versione del file, nella forma singolare |
| | msgid_plural (previous-untranslated-string-plural) | indica un testo da tradurre presente in una precedente versione del file, nella forma plurale |
| msgid (untranslated-string-singular) | indica un testo da tradurre, nella forma singolare |
| msgid_plural (untranslated-string-plural) | indica un testo da tradurre, nella forma plurale |
| msgstr[0] (translated-string-case-0) … msgstr[N] (translated-string-case-n) | nel file .POT è vuoto, nel file .PO indica la traduzione per la forma singolare [0] o plurale. Sono gestite anche le lingue che prevedono più forme plurali. |
Il passaggio di commenti per i traduttori
A volte può essere necessario dare delle indicazioni (ai traduttori) su come dev’essere interpretato un testo da tradurre. Se il programmatore inserisce un commento subito prima di una funzione gettext(), questo verrà riportato nel file .POT.
Le istruzioni:
/*translators: this is a comment*/
description = _( "Validation errors occurred");
produrranno il seguente output sul file .POT:
#. translators: this is a comment
#: percorso/nomefile:riga dov'è inserito il testo da tradurre
msgid "Validation errors occurred"
msgstr ""
La formattazione di testi lunghi
Per convenzione, un testo inserito in un file .POT non deve superare complessivamente gli 80 caratteri. Eventuali testi più lunghi vengono suddivisi su più righe, di cui la prima rimane vuota:
msgid ""
"The downloadable file %1$s cannot be used as it does not have an allowed "
"file type. Allowed types include: %2$s"
msgstr ""
Il formato del file .MO
Si riporta di seguito la rappresentazione grafica della struttura di un file .MO, rimandando alla documentazione ufficiale per maggiori approfondimenti.

Risorse online
- Sito ufficiale di gettext (https://www.gnu.org/software/gettext/)
- Manuale GNU gettext in formato PDF (https://www.gnu.org/software/gettext/manual/gettext.pdf).
- gettext su wikipedia (https://it.wikipedia.org/wiki/Gettext)
- Quick gettext tutorial (https://www.labri.fr/perso/fleury/posts/programming/a-quick-gettext-tutorial.html)

0 commenti