Localizzare uno script bash

Debian-swirl.png Versioni Compatibili

Tutte le versioni supportate di Debian

Introduzione

Chi utilizza quotidianamente sistemi operativi *nix based (sia per lavoro che per diletto) lo sa: prima o poi avere a che fare con il linguaggio di scripting bash è una tappa obbligata.
Ci si rende presto conto che è molto più semplice e rapido compiere determinate operazioni direttamente da dentro un terminale che ricorrere a mouse, click, finestre e finestrelle varie.
In seguito ci si accorge anche che "concatenare" più comandi/istruzioni insieme può essere davvero utile visto che si riesce ad automatizzare rapidamente operazioni molto lunghe e noiose.
Guide in rete che trattano le tecniche di scripting se ne trovano tantissime ed alcune molto ben fatte.
Uno degli aspetti un po' meno "esplorati" è la localizzazione ovvero la tecnica con la quale rendere i propri script "comprensibili" anche a chi non parla la nostra lingua.


Operazioni preliminari

Prima di esplorare i vari metodi di approccio al problema è necessario capire "che lingua parla" il nostro sistema:

$ echo $LANG
it_IT.UTF-8                                                                                                    

Nonostante questa impostazione di default il sistema è capace di "riconoscere" anche altre lingue:

$ locate -w
locate: non è stato specificato nessun pattern da cercare

$ LANGUAGE=es_ES locate -w
locate: ningún patrón de busqueda especificado

$ LANGUAGE=de_DE locate -w
locate: Keine Muster zum Suchen für vorgegebene

$ LANGUAGE=en_US locate -w
locate: no pattern to search for specified

Come si può vedere la prima istruzione (riguardante un comando inesistente...) restituisce una risposta nella lingua impostata di default. Giocando un po' con la variabile $LANGUAGE le risposte restituite vengono tradotte "in automatico".


Tecniche di localizzazione

Una volta presa un po' di confidenza con le impostazioni di sistema relative alla lingua, è possibile cominciare subito a darsi da fare per rendere uno script "internazionale"!

Lo script che ci si propone di tradurre è abbastanza complesso...

#!/bin/bash

# scriptdifficile
# Disclaimer: questo script è molto complicato, se sei debole di cuore non leggere!

echo "Ciao!"
echo "Ti dovevo dire qualcosa ma non ricordo cosa..."
echo "Ci vediamo, a presto!"

Se lo si rende eseguibile e lo si lancia (ovviamente a proprio ed esclusivo rischio e pericolo) il risultato è:

$ chmod +x scriptdifficile
$ ./scriptdifficile
Ciao!
Ti dovevo dire qualcosa ma non ricordo cosa...
Ci vediamo, a presto!

Vediamo di creare (per lo meno) due localizzazioni di questo capolavoro: una in inglese e l'altra in spagnolo.


Metodo 1: l'arte di arrangiarsi

Per localizzare in maniera molto semplice lo script si può ricorrere al costrutto case (o anche a quello if... then... else, o anche...):

#!/bin/bash

# scriptdifficile
# Disclaimer: questo script è molto complicato, se sei debole di cuore non leggere!

case $LANG in
	it* )
			STRCIAO="Ciao!"
			STRMSG="Ti dovevo dire qualcosa ma non ricordo cosa..."
			STRBYE="Ci vediamo, a presto!"
			;;
	en* )
			STRCIAO="Hello!"
			STRMSG="I had something to say to you but I don't remember what..."
			STRBYE="See you, bye!"
			;;
	es* )
			STRCIAO="Hola!"
			STRMSG="Tenia algo que decirte pero non me acuerdo que..."
			STRBYE="Nos vemos, hasta pronto!"
			;;
	* )
			STRCIAO="Hello!"
			STRMSG="I had something to say to you but I don't remember it..."
			STRBYE="See you, bye!"
			;;
esac

echo $STRCIAO
echo $STRMSG
echo $STRBYE

Il che porta a questi risultati:

$ ./scriptdifficile
Ciao!
Ti dovevo dire qualcosa ma non ricordo cosa...
Ci vediamo, a presto!

$ LANG=en_US ./scriptdifficile
Hello!
I had something to say to you but I don't remember what...
See you, bye!

$ LANG=es_ES ./scriptdifficile
Hola!
Tenia algo que decirte pero non me acuerdo que...
Nos vemos, hasta pronto!

Come è facile vedere lo script "legge" la variabile $LANG e imposta le stringhe di conseguenza a seconda del valore viene passato runtime.

Questo metodo, quindi, funziona ma... ha sicuramente almeno due grossi lati negativi:

  • è brutto a vedersi (de gustibus...)
  • non è pratico, basti pensare ad uno script con qualche centinaio di stringhe da localizzare...


Metodo 2: Il carattere "$"

Questa tecnica ci consentirà di creare con pochi passaggi dei file "modello" (da poter distribuire per essere tradotti) e di compilarli per poi inserirli in una specifica directory di sistema in modo che sia possibile, in automatico, "godere" della localizzazione del nostro script relativamente a come sono impostati i locales nel sistema!

Per creare un file modello basterà "trasformare" lo script così:

#!/bin/bash

# scriptdifficile
# Disclaimer: questo script è molto complicato, se sei debole di cuore non leggere!

echo $"Ciao!"
echo $"Ti dovevo dire qualcosa ma non ricordo cosa..."
echo $"Ci vediamo, a presto!"

Quello che è cambiato rispetto alla versione originale è la presenza del carattere $ che precede le stringhe che si vogliono tradurre.
Creiamo il file "modello" e guardiamo il suo contenuto:

$ bash --dump-po-strings scriptdifficile > scriptdifficile.pot

$ cat scriptdifficile.pot
#: scriptdifficile2:6
msgid "Ciao!"
msgstr ""
#: scriptdifficile:7
msgid "Ti dovevo dire qualcosa ma non ricordo cosa..."
msgstr ""
#: scriptdifficile:8
msgid "Ci vediamo, a presto!"
msgstr ""

Come è facilmente intuibile il comando bash --dump-po-strings non ha fatto altro che effettuare un "parsing" del file scriptdifficile, individuare le stringhe "marcate" con $ e riportarle nel file modello dando loro l'identificativo msgid. Ad ogni msgid è stata "accoppiata" una stringa vuota identificata dal marcatore msgstr. Sarà proprio in corrispondenza dei vari msgstr che bisognerà inserire le stringhe tradotte.

Creiamo due sottodirectory destinate a contenere i file tradotti rispettivamente in inglese e in spagnolo e copiamoci dentro il file modello:

$ mkdir en es
$ cp scripdifficile.pot > en/scripdifficile.po
$ cp scripdifficile.pot > es/scripdifficile.po

Fatto questo i due file en/scripdifficile.po e es/scripdifficile.po dovranno essere editati a manina...:

$ cat en/scripdifficile.po
#: scriptdifficile2:6
msgid "Ciao!"
msgstr "Hello!"
#: scriptdifficile:7
msgid "Ti dovevo dire qualcosa ma non ricordo cosa..."
msgstr "I had something to say to you but I don't remember what..."
#: scriptdifficile:8
msgid "Ci vediamo, a presto!"
msgstr "See you, bye!"

$ cat en/scripdifficile.po
#: scriptdifficile2:6
msgid "Ciao!"
msgstr "Hola!"
#: scriptdifficile:7
msgid "Ti dovevo dire qualcosa ma non ricordo cosa..."
msgstr "Tenia algo que decirte pero non me acuerdo que..."
#: scriptdifficile:8
msgid "Ci vediamo, a presto!"
msgstr "Nos vemos, hasta pronto!"

...e successivamente compilati direttamente nelle directory relative alla lingua corrispondente:

$ msgfmt -o /usr/share/locale/en/LC_MESSAGES/scriptdifficile.mo en/scriptdifficile.po
...
$ msgfmt -o /usr/share/locale/es/LC_MESSAGES/scriptdifficile.mo es/scriptdifficile.po

Si "informerà" a questo punto il nostro script del path dove andare a "cercare" (se esistenti) eventuali sue localizzazioni.
Per far questo basterà trasformare la prima riga dello script da così:

#!/bin/bash

a così:

#!/bin/bash
TEXTDOMAINDIR=/usr/local/share/locale
TEXTDOMAIN=scriptdifficile

E il gioco è fatto!!

Da questo momento in poi lo script "leggerà " le impostazioni di sistema relative alla lingua; se una traduzione per quella lingua è stata compilata e messa nel path corretto, restituirà le stringhe tradotte, altrimenti quelle originali (in questo caso in italiano)!


Modifiche

Questo metodo ha (tra gli altri) il vantaggio di rendere successive eventuali modifiche allo script originale facili da integrare nel file .po.
Per fare un esempio, se si aggiunge una riga a scriptdifficile:

#!/bin/bash

# scriptdifficile
# Disclaimer: questo script è molto complicato, se sei debole di cuore non leggere!

echo $"Ciao!"
echo $"Ti dovevo dire qualcosa ma non ricordo cosa..."
echo $"Ci vediamo, a presto!"
echo $"Questa riga è stata aggiunta"

per integrare le modifiche basterà creare il nuovo file .pot:

$ bash --dump-po-strings scriptdifficile > scriptdifficile-new.pot

e fare il merge:

$ msgmerge --update en/scriptdifficile.po scriptdifficile-new.pot
$ msgmerge --update es/scriptdifficile.po scriptdifficile-new.pot
...
$ cat en/scriptdifficile.po
#: scriptdifficile2:6
msgid "Ciao!"
msgstr "Hello!"
#: scriptdifficile:7
msgid "Ti dovevo dire qualcosa ma non ricordo cosa..."
msgstr "I had something to say to you but I don't remember what..."
#: scriptdifficile:8
msgid "Ci vediamo, a presto!"
msgstr "See you, bye!"
#: scriptdifficile:9
msgid "Questa riga è stata aggiunta"
msgstr ""

L'istruzione msgmerge evita di dover intervenire sui file .po "manualmente" per aggiungere/rimuovere/modificare le stringhe di testo.

Tip

Può capitare che all'interno di uno script ci siano delle stringhe identiche ripetute più volte.
In questo caso è possibile "eliminarle" dal file .pot avvalendosi di strumenti come awk, sed, etc.
Ad esempio:

$ awk '/^msgid/&&!seen[$0]++;!/^msgid/' en/scriptdifficile.pot > en/scriptdifficile-new.pot

Conclusioni

Il metodo trattato in questa guida , sebbene semplice ed efficace, viene oggi considerato deprecato.
L'alternativa è quella di evitare il marcatore $ in favore di gettext.
Seguendo questo approccio l'istruzione bash --dump-po-strings deve essere rimpiazzata dall'istruzione xgettext, altrimenti nel creare il file .po si cercherebbe comunque la stringa $...
Purtroppo xgettext, allo stato attuale, sembra soffrire di alcuni bug che ne complicano pesantemente l'utilizzo.

Happy translating!




Guida scritta da: pmate 11:36, 17 ott 2011 (CEST)   Debianized 20%
Estesa da:
Verificata da:

Verificare ed estendere la guida | Cos'è una guida Debianized