Ein sehr hilfreiches Tool zum Erstellen von Multilanguage Anwendungen ist gettext. Normalerweise für C-Anwendungen eingesetzt, gibt es auch ein Modul für PHP. Das ist allerdings zunächst einmal unbrauchbar, wenn die eigentlichen Texte nicht im PHP-Quellcode, sondern in Smarty Templates stehen.

Das Plugin smarty-gettext

Glücklicherweise hat sich jemand die Mühe gemacht, ein Smarty Plugin namens smarty-gettext zu schreiben, das die gettext-Funktionalität auch innerhalb von Templates zulässt, und zwar mit folgender Syntax:

{"No account yet? Register now!"|gettext}

Im Smarty Template werden also alle Texte zunächst auf englisch geschrieben.

Extrahieren der Strings aus den Smarty Templates

Aus diesen zu übersetzenden Texten muss eine Liste generiert werden - und genau das übernimmt ein Skript, das der Ersteller des smarty-gettext Plugins unter examples/i18n/smarty/index.php zur Verfügung stellt. In diesem Skript sind einige Einstellungen vorzunehmen, beschrieben im Wiki. Außerdem ist sicherzustellen, dass das Skript alle einzubindenden Dateien findet.

Führt man dieses Skript aus, erstellt es im System-Temp Verzeichnis einen neuen Ordner mit allen Smarty-Templates in kompilierter Form.

Generieren der Übersetzungsdatei

Jetzt kommt endlich gettext ins Spiel. Es generiert aus den kompilierten Smarty Templates eine Übersetzungsdatei mit der Endung .po. Praktisch ist es, den gettext Befehl gleich in das oben erwähnte Extraktions-Skript mit aufzunehmen, etwa so:

$cmd = 'xgettext --language=PHP --from-code=UTF-8 '.$directory.'*.tpl';
exec($cmd);

Selbstverständlich kann der Befehl aber auch manuell unter Angabe des Verzeichnisses ausgeführt werden.

gettext Übersetzungsdateien mergen

Die hierdurch erzeugte Datei result.po enthält alle englischen Texte und Platzhalter in Form von leeren Anführungszeichen für die deutschen Übersetzungen.

Für den ersten Durchlauf ist das auch genau richtig - für jeden weiteren aber wäre das eine etwas ärgerliche Situation. Die deutschen Übersetzungen müssten immer wieder neu eingefügt werden.

Sollten bereits übersetzte Daten vorhanden und in einer .po-Datei eingetragen sein, ist es hilfreich, die beiden Dateien zu mergen. Auch das kann fester Bestandteil des Extraktionsskriptes werden:

$cmd = 'msgmerge -N --no-wrap -o results.po /mein-website-pfad/locale/de_DE/LC_MESSAGES/messages.po messages.po';
exec($cmd);

Ist noch keine .po-Datei mit Übersetzungen vorhanden, braucht natürlich nicht gemerged zu werden. In diesem Fall kann mit dem nächsten Schritt fortgefahren werden.

Aufräumen

Das Extraktionsskript erstellt ein neues Verzeichnis mit zufälligem Namen. Sobald die .po-Datei erstellt wurde, kann dieses Verzeichnis gelöscht werden:

$cmd = 'rm '.$directory.' -R';
exec($cmd);

Übersetzen

Die .po-Datei wird zum Übersetzer geschickt. Sie enthält Abschnitte wie folgt:

...
#: /tmp/i18n_7555657/portal-signin.tpl:23
msgid "Password"
msgstr ""

#: /tmp/i18n_7555657/portal-signin.tpl:25
msgid "No account yet? Register now!"
msgstr ""
...

Der Übersetzer bearbeitet diese Datei, die danach so aussehen könnte:

#: /tmp/i18n_7555657/portal-signin.tpl:23
msgid "Password"
msgstr "Passwort"

#: /tmp/i18n_7555657/portal-signin.tpl:25
msgid "No account yet? Register now!"
msgstr "Noch keinen Account? Hier anmelden!"

Import der Übersetzungen

Die .po-Datei kann leider von PHP/Smarty nicht direkt verarbeitet werden. Folgender Befehl sorgt dafür, dass sie für gettext verwendbar ist:

msgfmt -o /mein-website-pfad/locale/de_DE/LC_MESSAGES/messages.mo /mein-website-pfad/locale/de_DE/LC_MESSAGES/messages.po

Wichtig ist, dass die Übersetzungsdateien in der richtigen Ordnerstruktur abgelegt werden, also locale/de_DE/LC_MESSAGES/, wobei de_DE natürlich durch die entsprechende Sprache ersetzt wird.

Perfekt, die Übersetzungsdatei ist jetzt da, wo sie hingehört.

PHP Code anpassen

PHP benötigt eine Information darüber, wo die Übersetzungen liegen, und welche Sprache angezeigt werden soll. Vor dem ersten Aufruf von Smarty muss dieser Code in der PHP-Applikation eingebunden werden:

putenv("LC_ALL=de_DE");
setlocale(LC_ALL, "de_DE");

bindtextdomain("messages", "/mein-website-pfad/locale/"); // << Pfad anpassen
bind_textdomain_codeset("messages","UTF-8");
textdomain("messages");

Test

Damit sollte nun auch schon alles funktionieren - und im Browser die deutschen Texte angezeigt werden. Falls nicht, liegt es womöglich daran, dass die de_DE Locale auf dem Server nicht installiert ist - das ist über die Kommandozeile schnell gemacht.