Wie ich kürzlich bereits angekündigt habe, habe ich im Zuge meines Projektes “Humble Savegame Backup” wieder einiges gelernt, was ich gerne auch mit euch teilen möchte.
Als erstes soll dieser Artikel kurz erklären, wie in Python geschriebene Programme übersetzt bzw. übersetzbar gemacht werden können. Speziell soll es dabei um Programme gehen, die mit Hilfe von PyGObject (bzw. PyGTK) GTK-Oberflächen darstellen. Gerade beim Übersetzen der Texte, die in der von Glade erstellten Datei enthalten waren, hatte ich zunächst arge Probleme…
Benötigte Tools
Für die Lokalisierung wird das gettext
-Paket benötigt:
sudo apt-get install gettext
Zudem braucht man für die die Lokalisierung der Glade-Files noch ein weiteres Tool:
sudo apt-get install intltool
Ein einfaches Python-Beispiel
Zunächst ein (sehr) einfaches Python Beispiel namens hello.py
– ohne GTK-Oberfläche:
#!/usr/bin/python
print '------------'
print 'Hello World!'
print '------------'
Ein solches Programm übersetzbar zu machen, ist wirklich sehr einfach. Viele werden die Vorgehensweise kennen und im Netz findet man ja auch wirklich reichlich Informations-Material dazu. Der Vollständigkeit halber möchte ich die notwendigen Schritte aber erklären.
Schritt 1 – gettext einbinden und anwenden
Zunächst muss gettext
importiert und die Lokalisierungs-Funktion “installiert” werden. Dazu wird das Skript folgendermaßen erweitert:
#!/usr/bin/python
import os
import gettext
lang = os.environ.get('LANGUAGE', 'en')
trans = gettext.translation('hello', 'locale', [lang])
trans.install()
print '------------'
print 'Hello World!'
print '------------'
Schritt 2 – Kennzeichnung der Texte
Nun müssen alle Texte, die lokalisiert werden sollen, “gekennzeichnet” werden. Ich habe das Wort “gekennzeichnet” in Anführungszeichen gesetzt, weil es tatsächlich mehr als eine reine Kennzeichnung ist (Näheres dazu folgt gleich…).
Da nur die “Hello World!”-Ausgabe übersetzt werden soll, ist auch nur diese zu ändern:
print _('Hello World!')
Wie ihr seht ist nun der Text in Klammern eingeschlossen und der öffnenden Klammer ein Unterstrich vorangestellt. Das hat zur folge, dass der Text als Parameter an eine Funktion namens _
gegeben wird. Und der übersetzte Text wird dann zurückgegeben. Dafür ist aber erst noch etwas zu tun…
Schritt 3 – Texte extrahieren
Mit dem Tool xgettext
können nun die Texte extrahiert werden:
xgettext --from-code=UTF-8 --language=Python --keyword=_ --output=hello.pot hello.py
Dieser Aufruf erzeugt eine Datei namens hello.pot
, in der der “Hello World!”-Text wie folgt auftaucht:
#: hello.py:8
msgid "Hello World!"
msgstr ""
Die Datei enthält außerdem einige Angaben im Kopf, die ihr entsprechend anpassen könnt/solltet.
Schritt 4 – Übersetzung erzeugen
Nun erzeugen wir eine Quelldatei für die Übersetzung – hier beispielsweise für die deutsche:
msginit --input=hello.pot --locale=de
Durch diesen Aufruf (bei dem ihr nach eurer Mailadresse gefragt werdet) wird eine Datei de.po
erstellt, die der hello.pot
recht ähnlich sieht. In dieser Datei nimmt man nun die Übersetzung vor. Dazu füllt man den msgstr
mit dem Text in der Zielsprache. Das Ergebnis sieht so aus:
#: hello.py:8
msgid "Hello World!"
msgstr "Hallo Welt!"
Wenn der Text nicht übersetzt werden soll (also in msgid
der richtige Text für die Sprache enthalten ist), dann kann man msgstr
auch einfach leer lassen. Ist man mit der Übersetzung fertig, muss die Quelldatei kompiliert werden:
msgfmt --output hello.mo de.po
So erhält man eine Datei namens hello.mo
, die nun noch an die richtige Stelle verschoben werden muss, und zwar in das Unterverzeichnis locale/de/LC_MESSAGES
. Den Namen locale
haben wir in unserem Programm beim Aufruf von gettext.translation
vorgegeben. Der Name des Unterverzeichnisses de
steht dann für die Locale ist (je nach Sprache). Außerdem muss die Datei hello.mo
heißen, weil wir auch das beim Aufruf von gettext.translation
so angegeben haben (ohne die Endung .mo
).
Schritt 5 – Testen
Nun kann die Übersetzung getestet werden. Dazu muss vor dem Aufruf des Programms einfach die Umgebungsvariable LANGUAGE
umgesetzt werden, z.B. so:
LANGUAGE=es ./hello.py
Das Programm wird mit einem Fehler beendet, falls keine Übersetzung für die angegebene Sprache existiert. Für dieses Beispiel müsstet ihr also erst einmal noch eine Übersetzung für es
(spanisch) erzeugen (siehe Schritt 4).
Ein Beispiel mit GTK-Builder
Das zweite Beispiel hat nun eine kleine GTK-Oberfläche. Dazu haben wir neben dem Python-Programm (der Name sei wieder hello.py
) noch eine (z.B. mit Glade) erstellte XML-Datei, die die GTK-Oberfläche beschreibt (nennen wir sie hello.glade
). Hier der Inhalt von hello.py
, der im Gegensatz zum einfachen Beispiel oben nun um ein paar Zeilen für die Anzeige des GTK-Fensters erweitert wurde:
#!/usr/bin/python
import os
import gettext
from gi.repository import Gtk
lang = os.environ.get('LANGUAGE', 'en')
trans = gettext.translation('hello', 'locale', [lang])
trans.install()
print '------------'
print _('Hello World!')
print '------------'
b = Gtk.Builder()
b.add_from_file('hello.glade')
w = b.get_object('window1')
w.show()
Gtk.main()
Schritt 1 und 2
… überspringe ich hier. Wie ihr seht, sind diese Punkte schon im Code umgesetzt.
Schritt 3
Um die Texte aus der Glade-Datei nun auch wie oben beschrieben übersetzen zu können, muss man einen kleinen Umweg gehen und erst einmal die Texte in ein Zwischenformat extrahieren:
intltool-extract --type=gettext/glade hello.glade
Daraus entsteht eine Datei namens hello.glade.h
, die die Texte enthält. Diese Datei benötigen wird eigentlich nur temporär. Sie kann also nach dem kommenden Schritt wieder gelöscht werden.
Nun kann man die Texte in eine .pot
-Datei übernehmen. Dazu wird der Aufruf aus dem “einfachen Schritt 3″ (s.o.) etwas erweitert, damit sowohl die Texte aus der Python-Datei, als auch die Texte aus der hello.glade.h
gezogen werden:
xgettext --from-code=UTF-8 --language=Python --keyword=_ --keyword=N_ --output=hello.pot hello.py hello.glade.h
Mit der .pot
-Datei kann nun wie oben schon erklärt fortgesetzt werden (ab Schritt 4).
Schritt 4 und 5
Siehe einfaches Beispiel…
Schritt 6
Damit die Übersetzungen nun auch vom GTK-Builder gezogen werden, ist noch eine kleine Anpassung des Quellcodes notwendig. Hier der vervollständigte Code:
#!/usr/bin/python
import os
import gettext
import locale
from gi.repository import Gtk
lang = os.environ.get('LANGUAGE', 'en')
locale.bindtextdomain('hello', 'locale')
trans = gettext.translation('hello', 'locale', [lang])
trans.install()
print '------------'
print _('Hello World!')
print '------------'
b = Gtk.Builder()
b.set_translation_domain('hello')
b.add_from_file('hello.glade')
w = b.get_object('window1')
w.show()
Gtk.main()
Hinzugekommen ist das import locale
, das local.bindtextdomain(...)
und das b.set_translation_domain(...)
. Bei den beiden Aufrufen ist hello
wieder die Kennung des Programms und locale
das Verzeichnis mit den Übersetzungen – wie bei gettext.translation(...)
.
Weiterführendes
Wer sich intensiver mit diesem Thema beschäftigen will sollte die Online-Dokumentation zu gettext lesen. Besonders interessant dürfte das Kapitel 7 sein, das sich mit der Aktualisierung bestehender Sprachdateien beschäftigt – denn auf dieses “Problem” wird man zwangsläufig stoßen…