0251 / 590 837 15
info@a-coding-project.de

Tutorial: Lokalisierung mit Gettext und PHP

Gettext ist eine Bibliothek, mit dem man Software lokalisieren lassen kann. Neben dem stumpfen Übersetzten von festgelegten Sätzen bietet Gettext auch Unterstützung von Plural (z.B. 1 Wort / 2 Wörter) oder nationalen Zahlenformaten. Das Besondere: Gettext gibt es in sehr vielen Programmiersprachen und ist so schon fast ein Standard für die Lokalisierung von Software.

Installation von Gettext

Zum Generieren der Dateien für Gettext gibt es mehrere Tools. Die Basis geht über die Kommandozeile, es gibt aber auch grafische Tools, die vor allem für die Übersetzer gedacht sind. Da in den meisten Dokus von Gettext auf die Befehle in der Kommandozeile berichtet wird, nehme ich auch diese zum Einstieg. Später zeige ich aber auch noch ein grafisches Tool.

Installation auf einem Windows-System

Gettext kommt aus dem GNU-Projekt und ist somit unter Linux zu Hause. Aber auf der Suche nach einer Alternative für Windows bin ich auf GnuWin gestoßen. Dort kann man sich einen Setup für Windows herunterladen.

Installation unter Linux

Bei Linux-Systemen kann man Gettext zum Beispiel über apt bekommen. Sieht dann so aus:

sudo apt-get install gettext

Integration von Gettext in PHP

Gettext ist in der Regel schon mit PHP dabei. Wer sich nicht sicher ist, kann einfach mal eine phpinfo erstellen und nach gettext suchen. Sollte es nicht vorhanden sein, muss es vermutlich nur in die php.ini eingetragen werden.

Hello World – Unser erster Text mit Gettext

Zum Start des Codings direkt mal ein kleines Beispiel in PHP:

<?php
	$language = 'de_DE';
	putenv("LANG=$language"); 
	setlocale(LC_ALL, $language);

	$domain = 'gettext_test';
	bindtextdomain($domain, "C:projekteplaygroundgettextlocale"); 
	textdomain($domain);

	echo _("Hello World!");
?>

Als Ergebnis wird einfach der Text „Hello World!“ ausgegeben. Aber was genau haben wir hier gemacht?

  • putenv setzt die Sprache für den Server
  • setlocale setzt die Sprache für PHP (zur Formatierung von Datum & Co)
  • bindtextdomain setzt den Pfad zu den Sprachdateien. Die Domain ist hierbei quasi der Name der Anwendung.
  • textdomain sagt, dass wir die mit bindtext deklarierte Domain jetzt nutzen wollen.
  • _ – Einer der wohl kürzesten Funktionsnamen in PHP. Liefert den übersetzten Text zurück, die lange Variante davon ist „gettext“

Der letzten Funktion wird der Text in der Regel auf Englisch übergeben, weil an Hand dessen die Übersetzer ihre Übersetzungen einfügen können. Im Gegensatz zu Tokens, müssen sich die Übersetzer so nicht erst die andere Übersertzung dazu raussuchen, sondern können 1 zu 1 übersetzen.

Portable Object (.po) erstellen

Im nächsten Schritt, müssen wir die Sprachdateien erstellen. Dazu haben wir uns eben das Programm installiert. Mit Hilfe von xgettext kann man sich automatisch die Sprachkürzel auf dem PHP-Code herausziehen und diese Datein erstellen. Dazu muss man die Konsole öffnen (unter Windows „cmd“ ausführen), mit cd in das Verzeichnis des PHP-Scripts wechseln und dann folgendes ausführen:

"C:Program Files (x86)GnuWin32binxgettext" -n  *.php

Den Pfad zur xgettext-Anwendung habe ich absolut angegeben, die Anwendung liegt jeweils im Bin-Verzeichnis eurer Installation. Mit „-n *.php“ erstellt ihr dann das .po-File für eure PHP-Dateien im atkuellen Verzeichnis.
Heraus kommt eine .po-Datei im gleichen Verzeichnis. Sie hat folgenden Inhalt:

# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR , YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSIONn"
"Report-Msgid-Bugs-To: n"
"POT-Creation-Date: 2013-10-03 20:37+0200n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONEn"
"Last-Translator: FULL NAME n"
"Language-Team: LANGUAGE n"
"MIME-Version: 1.0n"
"Content-Type: text/plain; charset=CHARSETn"
"Content-Transfer-Encoding: 8bitn"

#: index.php:10
msgid "Hello World!"
msgstr ""

Bei der deutsche Übersetzung müsst ihr diese Datei nun in das Verzeichnis /locale/de_DE/LC_MESSAGES schieben und den Dateiname auf gettext_test (die Domain) ändern. In der Datei könnt ihr innerhalb des „msgstr“ jetzt eure Übersetzung eintragen. Sieht dann bei uns so aus:

# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR , YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSIONn"
"Report-Msgid-Bugs-To: n"
"POT-Creation-Date: 2013-10-03 20:37+0200n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONEn"
"Last-Translator: FULL NAME n"
"Language-Team: LANGUAGE n"
"MIME-Version: 1.0n"
"Content-Type: text/plain; charset=utf-8n"
"Content-Transfer-Encoding: 8bitn"

#: index.php:10
msgid "Hello World!"
msgstr "Hallo Welt!"

Zusätzlich habe ich noch das Charset auf utf-8 gesetzt.

Message Object erzeugen (.mo-Datei)

Das Message Obejct ist die Datei, die ihr später mit den PHP-Code ausliefern müsst. Sie ist binär und muss erst durch die Konsole erstellt werden. Dazu müsst ihr mit dem CMD in das Verzeichnis der po-Datei wechseln. Dort führt ihr dann folgenden Befehl aus:
C:Program Files (x86)GnuWin32binmsgfmt" gettext_test.po
Danach erwartet euch eine messages.mo, die dann zur gettext_test.mo umbeannt werden muss. Jetzt haben wir es fast geschafft.

Leider wird jetzt noch nicht der übersetzte Text angezeigt. Nach einer längeren Suche habe ich festgestellt, dass durch die Einstellung des Encodings unser PHP-Script noch mit dem Befehl bind_textdomain_codeset erweitert werden muss:

<?php
	$language = 'de_DE';
	putenv("LANG=$language"); 
	setlocale(LC_ALL, $language);

	$domain = 'gettext_test';
	bindtextdomain($domain, "C:projekteplaygroundgettextlocale"); 
	textdomain($domain);
	bind_textdomain_codeset($domain, 'UTF-8');
	echo _("Hello World!");
?>

Zu guter letzt muss noch der Apache neu gestartet werden. Ruft ihr nun euer Script im Browser auf, werdet ihr bereits die deutsche Übersetzung sehen.

PO-Datei erweitern

Lasst uns doch mal unser Script um einen weiteren Text erweitern:

<?php
	$language = 'de_DE';
	putenv("LANG=$language"); 
	setlocale(LC_ALL, $language);

	$domain = 'gettext_test';
	bindtextdomain($domain, "C:projekteplaygroundgettextlocale"); 
	textdomain($domain);
	bind_textdomain_codeset($domain, 'UTF-8');
	echo _("Hello World!")."<br />";
	echo _("This is a working translation.");
?>

Nun können wir mit xgettext wieder eine neue po-Datei erstellen. Doch diese hat wieder leere Übersetzungen. Kein Problem, wir können die alte und neue Datei zusammenbringen. Zuvor aber am besten das Encoding wieder auf „utf-8“ setzen.

msgcat kann die alte PO-Datei einfach updaten:

"C:Program Files (x86)GnuWin32binmsgcat" messages.po locale/de_DE/LC_MESSAGES/gettext_test.po -o locale/de_DE/LC_MESSAGES/gettext_test.po

Das wars auch schon. Nun können wir in der po-Datei die neuen Übersetzungen einfügen und das Message Object mit msgfmt neu erstellen.

Plural in PHP nutzen

Im Anfang habe ich erwähnt, das Gettext auch den Plural untersützt. Begrüßen wir doch gleich 3 Welten:

<?php
	$language = 'de_DE';
	putenv("LANG=$language"); 
	setlocale(LC_ALL, $language);

	$domain = 'gettext_test';
	bindtextdomain($domain, "C:projekteplaygroundgettextlocale"); 
	textdomain($domain);
	bind_textdomain_codeset($domain, 'UTF-8');
	printf(ngettext("Hello %d World!", "Hello %d Worlds!",3),3);
	echo "<br />";
	echo _("This is a working translation.");
?>

Mit ngettext kann man sich also den Plural ausgeben lassen. Im ersten Parameter wird der Text für die Einzahl, im Zweiten der für den Plural angegeben. Der dritte Parameter ist die Zahl, die man tatsächlich hat. In unserem Fall 3. Durch printf wird das %d durch die Zahl selbst ersetzt.

Die bereits übersetzte PO-Datei könnte jetzt so aussehen:

# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR , YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSIONn"
"Report-Msgid-Bugs-To: n"
"POT-Creation-Date: 2013-10-03 21:40+0200n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONEn"
"Last-Translator: FULL NAME n"
"Language-Team: LANGUAGE n"
"MIME-Version: 1.0n"
"Content-Type: text/plain; charset=utf-8n"
"Content-Transfer-Encoding: 8bitn"
"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;n"

#: index.php:10
#, php-format
msgid "Hello %d World!"
msgid_plural "Hello %d Worlds!"
msgstr[0] "Hallo %d Welt!"
msgstr[1] "Hallo %d Welten!"

#: index.php:12
msgid "This is a working translation."
msgstr "Das ist eine funktioniernde Übersetzung."

Grafische Oberfläche – Poedit

Zu guter Letzt, gibt es wie versprochen noch eine grafische Oberfläche, um Übersetzungen zu bearbeiten. Das bekannteste Tool hierfür ist Poedit. Damit kann man einfach unsere PO-Datei öffnen:

Poedit

Poedit

Kommentare

Astr schrieb am 23.02.2014:

Wie kann man nun den Cache leeren?!

Stefan Wienströer schrieb am 23.02.2014:

Apache neu starten dürfte klappen.

Astr schrieb am 25.02.2014:

Danke das klappt, aber ich möchte eigentlich den Cache leeren ohne den Server restarten zu müssen

Stefan Wienströer schrieb am 26.02.2014:

Schau mal: http://stackoverflow.com/questions/13625659/how-to-clear-phps-gettext-cache-without-restart-apache-nor-change-domain

MS schrieb am 25.11.2014:

Hallo, die Lösung ist schon Klasse, leider hatte ich dann auch das Cache-Problem... Der Lösungsvorschlag aus dem letzten Link scheint nicht überall zu funktionieren. Bei mir leider auch nicht. Allerdings gibt es hier eine Lösung: <a href="http://blog.ghost3k.net/articles/php/11/gettext-caching-in-php" title="http://blog.ghost3k.net/articles/php/11/gettext-caching-in-php" rel="nofollow"> Bitte aus die Kommentare beachten... Schöne Grüße...

mario schrieb am 22.02.2015:

Ich bekomme keine PO Files generiert. Müssen die PHP Datei besonders formatiert sein, oder was müssen diese enthalten. hat jemand vielleicht eine beispiel php ?