WordPress und die Localization

Im Grunde genommen ist es eine feine Sache das man in WordPress Themes und Plugins mit relativ wenig Aufwand in eine andere Sprache übersetzen kann. Man muss lediglich darauf achten beim schreiben des Themes bzw. Plugins die Texte in einem bestimmten Format auszugeben und schon kann man unter zuhilfenahme von Programmen wie z.B. poEdit das Theme oder Plugin in eine andere Sprache übersetzen ohne es neu zu schreiben.

Im Grunde genommen bedeutet hier aber, dass es nur auf den ersten Blick so einfach zu sein scheint. Wer sich schon mal mit den Thema Übersetzung von Plugins oder Themes beschäftigt hat, der wird früher oder später die vielen kleinen Haken zu spüren bekommen. Einer dieser kleinen Haken ist der, dass WordPress einen grundlegenden Unterschied bei der Einbindung von Übersetzungen macht. load_theme_textdomain() ist, wie der Name vermuten lässt, für Themes zuständig, während load_plugin_textdomain() für Plugins zuständig ist. Es ist schon mal auffällig das es für die gleiche Aufgabe zwei verschiedene Funktionen gibt. Also schauen wir doch mal in die Core-Datei wo der Unterschied bei diesen beiden Funktionen liegt.

Beide Funktionen finden wir in wp-includes/l10n.php. Beide Funktionen erwarten bei ihren Aufruf die Übergabe einer Domain und eines Pfades. Im Falle von load_plugin_textdomain() können sogar zwei verschiedenen Arten von Pfaden angegeben werden, wobei der absolute relative Pfad veraltet ist und nicht mehr verwendet werden sollte.
Bei genauerer Betrachtung fällt aber auf, dass load_theme_textdomain() bei der Pfadangabe deutlich toleranter ist als load_plugin_textdomain(). Ist kein Pafd angegeben, so verwendet load_theme_textdomain() das Verzeichnis des aktuellen Templates als Pfad. Ansonsten könnte man den Pfad auch sonstwohin zeigen lassen, es würde noch funktionieren.
load_plugin_textdomain() ist da etwas strikter. Egal ob man einen Pfad angibt oder nicht, es wird auf jeden Fall WP_PLUGIN_DIR, also der Pfad zum Plugin-Verzeichnis, im Pfad eingefügt.
Mit load_theme_textdomain() könnte man also auch eine Sprachdatei benutzen die in wp-content steht. Mit load_plugin_textdomain() wäre dies unmöglich.

Nun sind Pfadangaben in WordPress eine Sache für sich an die man sich gewöhnen und mit denen man umzugehen lernen kann. Deutlich mehr Kopfschmerzen bereitete mir in der Vergangenheit ein Umstand, den ich irgendwie nicht verstehe. Und zwar das Format nach dem die Sprachdateien benannt werden müssen.
load_theme_textdomain() erwartet das die Sprachdatei lediglich aus der Locale und der Endung .mo besteht. Also z.B. de_DE.mo oder en_EN.mo. load_plugin_textdomain() hingegen erwartet zu der Locale noch zusätzlich die Domaine als Teil des Dateinamens. Die Datei muss also das Format domain-locale.mo haben.
Für mich recht unverständlich und letzten Endes kann dies auch zu Problemen führen. Denn ändert sich im Plugin die Domaine, z.B. nach einem Update, dann können auch die (alten) Sprachdateien nicht mehr geladen werden. Man müsste also entweder alle Domainen im Plugin ändern oder alle Sprachdateien umbennenen.

Aber man sollte auch immer die Vorteile im Auge behalten. Durch die Verwendung der Domaine im Dateinamen der Sprachdatei kann man auf recht einfache Weise z.B. zwischen der Du- und der Sie-Form wechseln. Etwas was für deutsche Übersetzungen ja nicht ganz unerheblich ist.
Dazu legt man einfach z.B. eine Sprachdatei mit den Namen pluginname_du_form-de_DE.mo und eine mit den Dateinamen pluginname_sie_form-de_DE.mo an. Im Plugin selber kann man mittels define('MeinePluginDomain', 'plugin_xx_form'); eine Konstante definieren die dann in den Übersetzungsfunktionen verwendet wird (z.B. __('This example text', MeinePluginDomain)). Je nachdem ob xx nun ‘du’ oder ‘sie’ ist, schaltet man zwischen Du- und Sie-Form um.

Jetzt hat man aber ein Problem mit Sprachen die nicht zwischen Du und Sie unterscheiden. Mit Englisch zum Beispiel. Denn man müsste nun für jede Sprache zwei Sprachdateien anfertigen. Einmal plugin_du_form-en_EN.mo und einmal plugin_sie_form-en_EN.mo.
Das ganze gestaltet sich an diesen Punkt als etwas zu aufwendig um schön zu sein. Es gibt aber auch eine Lösung für das Problem. Wir benötigen ja eigentlich nur eine Funktion die in einem Verzeichnis nachschaut ob es eine Sprachdatei nach dem Format domaine-locale.mo gibt und diese lädt. Gibt es eine solche Sprachdatei nicht, wird halt eine Sprachdatei nach den Format locale.mo geladen. Gibt es diese auch nicht, wird halt nichts übersetzt.
Nebenbei erledigt sich mit solch einer Funktion das leidige Pfad-Problem. Die Sprachdateien können nun quasi an einen beliebigen Ort abgelegt werden. Ob dies Sinn macht, sei an dieser Stelle mal dahin gestellt.

Hier erst einmal die Funktion:

function flexible_loadtextdomain( $path = false, $domain = 'default' ){

  if( ! $path || ! is_dir( $path ) )

    return false;

  global $l10n;

  $local = get_locale();

  $mofile = $path . $domain . '-' . $local . '.mo';

  // try to find a matching translation

  // first try domain-locale.mo

    if( ! file_exists( $mofile ) ){

    // if not found, try only locale.mo

    $mofile = str_replace( $domain . '-', '', $mofile );

      if( ! file_exists( $mofile ) ){

        // if even this failed, throw an error or do something wired

        return false;

      }

    }

  load_textdomain( $domain, $mofile );

  return isset( $l10n[ $domain ] );

}

Die Funktion erwartet zwei Parameter. Zum einen eine Pfadangabe und zum anderen den Namen der Domain. Die erste If-Abfrage sorgt dafür das überhaupt ein Pfad übergeben wird und das es auch ein Verzeichnis ist. Anstatt einfach nur false zurück zu geben, kann man hier natürlich noch weitere Spielereien unter bringen indem man z.B. einen Pfad vorgibt (WP_PLUGIN_DIR oder get_template_directory).
Der Rest ist das was ich oben schon beschrieben habe. Erst domain-locale.mo suchen, wenn nicht gefunden locale.mo suchen, wenn das auch nicht gefunden irgendwas verrücktes machen, zum Beispiel false zurück geben.
In der letzten Zeile wird dann noch geprüft ob die Domain erfolgreich geladen wurde. Ab WP 3.0 kann man dies durch return load_textdomain(...) abkürzen. In älteren Versionen muss man diesen kleinen Umweg gehen da load_textdomain keine Erfolgsmeldung zurück gibt.

Der Code der Funktion ist ein wenig abgespeckt. Will man es ganz sauber programmieren, dann würde man noch ein paar Sachen anders machen, z.B. nicht $locale = get_locale(); verwenden, sondern $locale = apply_filters( 'xyz', get_locale(), $domain ); (xyz = theme_locale oder plugin_locale, je nachdem ob man die Funktion in einem Theme oder Plugin verwendet).

Vielleicht ist dieser Beitrag ja eine kleine Anregung für alle die Themes oder Plugins schreiben.

Keine Trackbacks

2 Kommentare

  1. Selena

    Ich habe ein benutzerfreundliche, webbasierte Lokalisierungstool um WordPress Themen zu übersetzen gefunden – https://poeditor.com/ Es gibt sogar ein Plugin, das integriert die POEditor API in Ihre WordPress – http://wordpress.org/extend/plugins/poeditor/ Ich empfehle es.

    Veröffentlicht 6. November 2013 am 14:51 | Permalink
  2. Super Anregung. Genau das habe ich gesucht. Man findet komischerweise keine wirklich guten Tutorials über Plugin-Übersetzungen. Aber dein Beitrag hat mir schon mal sehr weitergeholfen. Danke.

    Veröffentlicht 22. Mai 2015 am 08:59 | Permalink