Völker hört die Signale: Internationalisierung ist für Desktop-Programme üblich, bei von Menschen bedienten Skripten nicht. Dabei erfordert die Völkerverständigung nur wenig Mehrarbeit.
Wem Emacs reicht, um die Welt zu verändern, hält sich nicht mit einem Wort auf, das 20 Buchstaben umfasst: Internationalization. Informatiker kürzen den Begriff darum als Numeronym ab: I18n. Ob kurz oder lang, Entwickler verstehen unter Internationalisierung das Anpassen ihrer Programme an die Bedürfnisse verschiedensprachiger Nutzer. Dazu zählen insbesondere die Sprache selbst, aber auch Dinge wie Zahlen-, Währungs- und Datumsdarstellung. Texte, zum Beispiel Fenstertitel oder Beschriftungen, zieht das Programm dann zur Laufzeit aus sprachspezifischen Dateien.
Skriptprogrammierer scheuen offenbar den zusätzlichen Aufwand, den ihnen I18n beschert. Sie meinen, viele Skripte seien sowieso nur einfache Wrapper oder verrichten ihre Arbeit in aller Stille, gewissermaßen nach dem Motto: keine Ausgabe, kein Problem. Ein kleiner Praxistest in »/usr/bin« zeigt aber, dass in Wirklichkeit viele Skripte durchaus echte Anwendungsprogramme sind und mit dem Nutzer interagieren. Auf dem System des Autors sind aber nur zwei Skripte, »ldd« und »susehelp« , internationalisiert.
In nur drei Schritten
Das ist insofern unverständlich, als es die Bash dem Shellprogrammierer bei der Internationalisierung sehr einfach macht – allerdings nur bezüglich der Texte. Im ersten Schritt ersetzt der Programmierer alle String-Konstanten von
"Das ist ein String."
nach:
$"Das ist ein String."
Mit einem guten Editor ist das selbst nachträglich schnell erledigt und das Skript läuft ohne sichtbare Änderung wie bisher.
Als Nächstes extrahiert der Skriptautor alle Strings in eine eigene Datei (siehe Listing 1, Zeile 7). Die zweite Variante (Zeile 8) ändert nichts Substanzielles an der Ausgabedatei, formatiert sie aber etwas schöner. Theoretisch spielt es keine Rolle, in welcher Sprache man die Stringkonstanten abfasst. Rein praktisch erhöht eine englische Basisversion die Wahrscheinlichkeit, dass sich in der Community Übersetzer finden.
Die so erzeugte Datei enthält pro so genannter Msgid einen Msgstr (Abbildung 1). Die Msgid ist nichts anderes als das extrahierte Literal, der Msgstr der übersetzte String. Ist dieser leer, gibt die Bash die ursprüngliche Stringkonstante aus.
Listing 1
Schritte zum internationalisierten Skript
01 #!/bin/bash 02 03 pgm=doecho 04 05 # Eingabedateien vorbereiten 06 mkdir -p po/en_US po/de_DE 07 #bash --dump-po-strings "bin/$pgm" > "$pgm.pot" 08 xgettext -L shell -o "$pgm.pot" "bin/$pgm" 09 msginit -l en_US -i "$pgm.pot" -o "po/en_US/$pgm.po" 10 msginit -l de_DE -i "$pgm.pot" -o "po/de_DE/$pgm.po" 11 12 # Manuelles Editieren der sprachspezifischen po-Dateien 13 14 # Übersetzung der Dateien 15 mkdir -p "locale/en_US/LC_MESSAGES/" "locale/de_DE/LC_MESSAGES/" 16 msgfmt -o "locale/en_US/LC_MESSAGES/$pgm.mo" "po/en_US/$pgm.po" 17 msgfmt -o "locale/de_DE/LC_MESSAGES/$pgm.mo" "po/de_DE/$pgm.po" 18 19 # Späteres Update mit neuer pot-Datei 20 msgmerge -U po/en_US/$pgm.po "$pgm.pot"
Übersetzen und weiterverarbeiten
Hinsichtlich der Übersetzung und Kompilation der Texte unterscheidet sich die Bash nicht von normalen Programmiersprachen. Details zum Vorgehen bietet etwa der Artikel [1]. Im Kurzdurchgang: »msginit« bereitet die Datei für die Übersetzung vor und »msgfmt« übersetzt sie in eine Binärform, die schnellen Zugriff gewährleistet. Spätere Updates arbeitet »msgmerge« in bestehende Übersetzungen ein. Um nicht alle Befehle erneut einzugeben, hält der Programmierer dafür ein kleines Makefile vor.
Damit die Bash die übersetzten Texte auch findet, setzt der Programmierer in seinem Skript zwei Variablen: »TEXTDOMAIN« und »TEXTDOMAINDIR« . Der Wert der ersten Variablen ist der Dateiname der Texte (ohne Extension ».mo« ), die zweite Variable gibt das Verzeichnis an, unter dem die Textdateien liegen (etwa »/usr/share/locale« ). Leider unterstützt Gettext hier nicht die Angabe von mehreren, durch Doppelpunkt getrennten Verzeichnissen wie etwa »PATH« .
Bash Bashing
Der Ansatz der Bash für die Internationalisierung ist elegant und nicht invasiv, das Skript bleibt weiterhin gut lesbar. Doch gibt es Kritik an diesem Lösungsansatz. Die Macher von Gettext [2] führen zwei Punkte an: Es sei keine kompatible Lösung, zudem entstünden bei diesem Vorgehen Sicherheitslücken.
Der erste Vorwurf wirkt etwas weit hergeholt. Kein Shellprogrammierer würde einem Perl-Programmierer vorwerfen, dass dessen Skripte nicht kompatibel seien. Manche Leute meinen trotzdem, dass Bash-Skripte prinzipiell mit allen anderen Shellinterpretern – unter dem Verzicht auf elegante Syntax – laufen müssten. Diese oft ideologisch unterfütterte Forderung erweist sich in der technischen Praxis als meist unbegründet.
Der zweite Vorwurf betrifft die Sicherheitslücken, die dem Bash-Ansatz inhärent sind. Internationalisierte Strings verhalten sich grundsätzlich genau so wie normale Zeichenketten innerhalb von doppelten Anführungszeichen. Das bedeutet insbesondere, dass die Bash Variablen- und Kommandosubstitution durchführt. Ein böswilliger Übersetzer könnte so Schadcode über die Internationalisierungsdateien einfach einschleusen.
Dieser Vorwurf ist im Grunde richtig, aber letztlich nicht Bash-spezifisch. Binäre Textdateien sind ebenso sorgfältig zu behandeln wie andere Programme – wer hier ohne geeignete Vorkehrungen handelt, riskiert die Verseuchung des Rechners. Verantwortliche Sysadmins misstrauen jeder Datei aus fremden Quellen.
Die Internationalisierung ist gleichwohl realisierbar, auch wenn der Shellinterpreter nicht die Bash ist. Hierzu stellt das Gettext-Paket eine Reihe von Shellfunktionen bereit und der String-Extraktor »xgettext« beziehungsweise »gettext« berücksichtigt die besonderen Konstrukte. Der Shellcode gerät allerdings recht unübersichtlich, insbesondere wenn es sich nicht um einfache Textkonstanten handelt. Das lässt sich durch einige Beispiele aus dem legendären “Advanced Bash Scripting Guide” [3] gut zeigen:
cd $var || error "`eval_gettext \"can\'t cd to \\\$var.\"`"read -p "`gettext \"Enter the value: \"`" var echo -e "`gettext \"\\0-h display help and exit\"`"
Jede der Varianten des Programmcodes lässt sich deutlich schwerer lesen und die vielen Escape-Zeichen machen die ganzen Konstrukte fehleranfällig. Wer ausschließlich für die Bash programmiert, verkompliziert seinen Code damit wirklich unnötig.
Fazit
Internationalisierte Skripte machen mehr Arbeit. Hierbei ist mit der Bash weniger der Skriptcode das Problem, sondern die Organisation der Übesetzungsdateien. Aus einem einfachen Skript, das der Admin nach »/usr/bin« kopiert, wird ein Programmpaket mit mehreren Dateien, die sauber installiert sein wollen. Der Maintainer braucht dafür Makefiles und muss im Idealfall Installationspakete in den gebräuchlichen Formaten schnüren. Diese Zusatzarbeit scheint (zu) viele Skriptautoren abzuschrecken. Bei ihnen sollte das Bewusstsein wachsen, dass viele Skripte eher Anwendungen sind und somit übersetzt gehören. (jk)
Infos
- Tim Schürmann, “Programme von Welt – Software internationalisieren mit Gettext”: Linux-Magazin 12/05, S. 112, https://www.linux-magazin.de/Ausgaben/2005/12/Programme-von-Welt
- GNU Gettext: http://www.gnu.org/software/gettext/
- Advanced Bash Scripting Guide: http://www.tldp.org/LDP/abs/html/







