Tutte le stringe che vengono visualizzate in una applicazione dovrebbero essere rese traducibili. La traduzione si ottiene utilizzando GNU gettext. gettext è un semplice archivio di messaggi, che registra le coppie chiave-valore, dove la chiave è la stringa presente nel codice del programma e il valore è la stringa tradotta o semplicemente la chiave stessa (se non esiste la traduzione oppure la chiave è di per sè corretta).
Come sviluppatori, non rientra nelle vostre responsabilità fornire la traduzione, ma dovete assicurarvi che le stringhe siano traducibili, in modo tale che gli script di gettext possano estrarre le liste delle stringe che devono essere tradotte, inserendo una speciale funzione su ogni stringa, in modo che possa avvenire la catalogazione.
Gnome rende questa operazione molto banale, definendo le due macro mostrate in
Figura 2
. La macro _() marca la stringa sia per la traduzione sia per la ricerca durante l'inserimento nel catalogo. Dovete utilizzarla in qualunque contesto in cui il linguaggio C permetta una chiamata di funzione. La macro N_() non opera l'inserimento nel catalogo, ma marca unicamente per la traduzione. Potete utilizzarla dove il C non permette una chiamata di funzione; ad esempio, durante l'inizializzazione di un array statico. Se la stringa viene marcata per la traduzione con N_(), dovete eventualmente utilizzare _() su questa per effettuare l'inserimento nell'archivio.
Ecco un piccolo esempio:
|
#include <gnome.h>
static char* a[] = {
N_("Qualcuno mi traduca per favore"),
N_("Anche a me, grazie")
};
int main(int argc, char** argv)
{
bindtextdomain(PACKAGE, GNOMELOCALEDIR);
textdomain(PACKAGE);
printf(_("Stringhe tradotte:\n"));
printf(_(a[0]));
printf(_(a[1]));
return 0;
}
|
Notate che le stringe "Qualcuno mi traduca per favore" e "Anche a me, grazie" sono marcate in modo tale che gettext possa trovarle e produrre la lista delle stringe totali da tradurre. I traduttori del software andranno poi a modificare questa lista per effettuare la traduzione degli applicativi. In un secondo momento, la macro _() include una chiamata a funzione per eseguire la ricerca della stringa tradotta per ciascun membro dell'array. Dato che una chiamata a funzione è permessa quando la stringa "Stringhe tradotte" viene introdotta, l'operazione si svolge in un solo passo.
All'inizio della vostra applicazione dovete chiamare le funzioni bindtextdomain() e textdomain() come mostrato nell'esempio. In questo codice PACKAGE è una stringa che rappresenta il programma che si trova nel pacchetto, solitamente definita in config.h (
il capitolo Creare il proprio albero di sorgenti
). Dovrete poi successivamente definire GNOMELOCALEDIR, tipicamente in Makefile.am ($(prefix)/share/locale, oppure $(datadir)/locale, sono impostazioni standard). Le traduzioni sono contenute in GNOMELOCALEDIR.
Quando marcate le stringhe per la traduzione, dovete assicurarvi che queste siano traducibili. Evitate di generare stringhe a runtime attraverso una concatenazione. Ad esempio, evitate di fare questo:
|
gchar* message = g_strconcat(_("Rilevato errore nella periferica "),
device, NULL);
|
Il problema è che in alcune lingue può essere corretto mettere il nome della periferica in fondo alla frase, mentre in altre no. Se utilizzate g_snprintf() oppure g_strdup_printf() invece di una concatenazione, il traduttore può cambiare l'ordine delle parole. Ecco il modo giusto di farlo:
|
gchar* message = g_strdup_printf(_("Rilevato un errore nella periferica %s"),
device);
|
Adesso il traduttore può spostare %s dove necessario.
Stringhe generate molto complicate sono da evitare il più possibile. Ad esempio, tradurre questa parte di codice è un grosso problema:
|
printf(_("%s %d person%s\n"),
n_persone > 1 ? _("Ci sono") : _("C'è"),
n_persone,
n_persone > 1 ? _("e") : "a");
|
È consigliato spostare la verifica della condizione al di fuori della funzione printf():
|
if (n_persone > 0)
printf(_("Ci sono %d persone.\n"), n_persone);
else
printf(_("C'è 1 persona.\n"));
|
A ogni modo il manuale di gettext evidenzia come non sempre questo può funziona correttamente: alcune lingue distinguono più categorie oltre a "esattamente uno" e "più di uno" (ad esempio può esistere una parola per esprimere "esattamente due" in aggiunta alle forme singolari e plurali della lingua inglese). Il manuale consiglia di utilizzare in queste situazioni una tabella con i valori che intendete utilizzare nella vostra applicazione:
|
static const char* npersone_frasi[] = {
N_("Non c'è nessuno.\n"),
N_("C'è una persona.\n"),
N_("Ci sono due persone.\n"),
N_("Ci sono tre persone.\n")
};
|
Come potete vedere questa situazione diventa rapidamente molto difficile da gestire. Evitatela se potete. La documentazione di gettext fornisce ulteriori esempi, nel caso vi troviate in situazioni analoghe.
Il processo di internazionalizzazione include anche la visualizzazione di particolari tipi di dati, come la data e i numeri decimali. In generale, la libreria C fornisce strumenti sufficienti per risolvere queste problematiche; utilizzate strftime(), strcoll() e così via per gestire questi formati. Un buon libro sul linguaggio C oppure su POSIX vi spiegherà come utilizzarle al meglio. Lo strumento GDate fornito da glib gestisce il formato della data utilizzando internamente strftime().
Un errore comune da evitare: non utilizzate funzioni dipendenti della
lingua o dalla localizzazione quando leggete e scrivete su file. Ad esempio, printf() e scanf() aggiustano i numeri decimali a seconda della localizzazione, quindi non potete utilizzare questo formato nei file. Gli utenti europei non saranno in grado così di leggere i file creati negli Stati Uniti.