Wie wir die Core-Dateien selbst übersetzen und sicher vor Updates machen habe ich bereits in diesem Artikel beschrieben:
Updatesichere Übersetzunge der WordPress Core Dateien
Doch wie schützen wir bei Plugins unsere eigenen Übersetzungen vor dem Überschreiben bei Updates?
Hier die Lösung für Leute in Eile:
add_filter( 'load_textdomain_mofile', 'load_custom_plugin_translation_file', 10, 2 ); /* * Replace 'textdomain' with your plugin's textdomain. e.g. 'woocommerce'. * File to be named, for example, yourtranslationfile-de_DE.mo * File to be placed, for example, wp-content/lanaguages/textdomain/yourtranslationfile-de_DE.mo */ function load_custom_plugin_translation_file( $mofile, $domain ) { if ( 'textdomain' === $domain ) { $mofile = WP_LANG_DIR . '/textdomain/yourtranslationfile-' . get_locale() . '.mo'; } return $mofile; }
Schauen wir einmal wo überall Übersetzungen, also Sprachdateien vorkommen. Drei Möglichkeiten sind hier standardmäßig vorgesehen:
/wp-content/plugins/plugin-name/[i18n/]languages/
/wp-content/languages/plugins/
bzw. allgemeinerWP_LANG_DIR/plugins
/wp-content/languages/plugin-name/
Die Sprachdatei selbst trägt immer den Namen der Text Domain + Locale (gebildet aus sprache_LAND) und hat die Endung .mo
z.B.: loco-translate-de_DE.mo
, wp-jquery-lightbox-de_DE.mo
,paypal-for-woocommerce-de_DE.mo
usw…..
(Eine Erklärung zu den verschiedenen Dateiendungen und was sie bedeuten ist in einem eigenen Artikel beschrieben: Die WordPress Übersetzungsdateien)
WordPress hat sowohl für die Text Domain
als auch für das Verzeichnis der Sprachdateien, den Domain Path
eigene Metanamen für den Header vorgesehen.
Im Folgenden die Header von Loco Translate und WooCommerce Germanized (WCGZD) als Beispiele:
Gut, aber wo sind diese Sprachdateien nun abgelegt?
Beispielsweise hat das WCGZD als Domain Path: /i18n/languages/
eingetragen. Das bedeutet, dass in diesem Unterverzeichnis des Plugin-Verzeichnisses auch die Übersetzungen bei der Erstinstallation zu finden sind.
Ab WordPress 3.7 werden nun nach dem ersten Update der Language-Packs des WCGZD-Plugins, aktualisierte Sprachdateien in das /wp-content/languages/woocommerce-germanized
Verzeichnis heruntergeladen. Sprachdateien aus diesem Verzeichnis werden von WordPress vorrangig zur Übersetzung geladen.
Schauen wir uns das WCGZD im Query Monitor *) etwas genauer an, sehen wir die Reihenfolge:
Wenn wir nun beispielsweise die Sprachdatei im
/wp-content/languages/woocommerce-germanized
mit eigenen Übersetzungen bearbeiten, gehen diese beim nächsten Update verloren, bearbeiten wir die Dateien im Plugin-Verzeichnis ( /wp-content/plugins/plugin-name/[i18n/]languages/) bringt das auch nichts, da sie letztgereiht eingelesen werden.
Was können wir also tun, um unsere eigenen Übersetzungen updatesicher zu verwenden?
Einerseits könnten wir die automatischen Updates deaktivieren. Dazu notieren wir folgenden Code in die functions.php
unseres Themas:
add_filter( 'auto_update_translation', '__return_false' );
Dann können wir vor jedem manuell durchgeführtem Update unsere eigenen Übersetzungen sichern und nach dem Update zurückspielen. Ganz schön umständlich…..
Wie wäre es, wenn wir WordPress so konfigurieren, dass unsere Updates (an einem sicheren Ort abgelegt) als erstes eingelesen werden?
Fündig bin ich geworden bei einem Artikel von Geert De Deckere:
Loading WordPress language files the right way
add_action('load_textdomain', 'load_custom_language_files_for_my_plugin', 10, 2); function load_custom_language_files_for_my_plugin($domain, $mofile) { // Note: the plugin directory check is needed to prevent endless function nesting // since the new load_textdomain() call will apply the same hooks again. if ('my-plugin' === $domain AND plugin_dir_path($mofile) === WP_PLUGIN_DIR.'/my-plugin/languages/') { load_textdomain('my-plugin', WP_LANG_DIR.'/my-plugin/'.$domain.'-'.get_locale().'.mo'); } }
Nun werden zuerst die eigenen Übersetzungen geladen (wenn welche im Verzeichnis ‚/wp-content/languages/my-plugins‘ vorhanden sind) und danach die Original-Übersetzungen des Plugins. Fehlen eigene Übersetzungen für einzelne Zeichenketten, werden diese um die Original-Übersetzungen ergänzt.
Fehlen diese auch dort, wird der (zumeist englische) Text, wie er in der pot-Datei steht genommen. (aus dem /wp-content/plugin-name/languages-Verzeichnis)
Der Artikel wurde jedoch vor der WordPress 3.7 Version verfasst, d.h., dass das von ihm vorgeschlagene Verzeichnis WP_LANG_DIR/languages wird nun von WordPress selbst für die Language Packs verwendet.
Lösung 1:
Wir benötigen daher ein eigenes Verzeichnis, beispielsweise: WP_LANG_DIR/languages/custom-translations in das wir nun all unsere eigenen Übersetzungen schreiben.
Nun notieren wir folgenden Programmcode in der functions.php
:
add_action('load_textdomain', 'load_custom_language_files_for_plugin', 10, 2); function load_custom_language_files_for_plugin($domain, $mofile){ // Note: the plugin directory check is needed to prevent endless function nesting // since the new load_textdomain() call will apply the same hooks again. if ('plugin-text-domain' === $domain AND plugin_dir_path($mofile) === WP_PLUGIN_DIR.'/plugin-name/languages/') { load_textdomain('plugin-text-domain', WP_LANG_DIR.'/plugins/custom-translations/'.$domain.'-'.get_locale().'.mo'); } }
Könnten wir nicht alle eigenen Übersetzungen auf einmal automatisch laden?
Finale Lösung:
Die Frage ist nun, ob wir einen WordPress Hook finden, der uns rechtzeitig die load_textdomain
durchführt sodass wir ALLE eigenen Übersetzungen in unserem WP_LANG_DIR/plugins/custom-translations
finden und automatisch VOR allen anderen Übersetzungen laden können.
Schauen wir uns mal die Reihenfolge an wie WordPress die Kerndateien ladet:
Plugin API/Action Reference -> Actions Run During a Typical Request
Ein frühzeitig vom Theme angesprochener hook wäre demnach der ‚after_setup_theme
‚
Wir können nun in die Theme functions.php
notieren:
add_action( 'after_setup_theme', 'load_custom_language_files_for_plugins' );
und als aufgerufene Funktion:
function load_custom_language_files_for_plugins(){ if ( ! function_exists( 'get_plugins' ) ) { require_once ABSPATH . 'wp-admin/includes/plugin.php'; } $all_plugins = get_plugins(); $locale = get_locale(); foreach( $all_plugins as $plugin => $field) { // we want to load translations for active plugins only if ( is_plugin_active( $plugin ) ) { // check if there is a Text Domain noted in the plugins header if ('' != $field['TextDomain'] ) { $mofile = WP_LANG_DIR . '/custom-translations/' . $field['TextDomain'] . '-' . $locale . '.mo'; if (file_exists( $mofile ) ) { // if there is a custom language file, load it load_textdomain($field['TextDomain'], $mofile); } } } } }
Wenn die Entwickler der Plugins ihre Text Domains richtig geladen haben, nämlich im init
-Teil, wie von Geert De Deckere beschrieben, dann werden nun alle vorgefundenen, eigenen Sprachdateien im Verzeichnis WP_LANG_DIR/plugins/custom-translations
vor den Default Sprachdateien geladen.
Falls nicht nur die Sprachdateien der aktiven Plugins sondern zu allen installierten Plugins geladen werden sollen, muss nur die entsprechende IF-Bedingung if ( is_plugin_active( $plugin ) ) {
im Code oben gestrichen werden.
Ich hoffe, dass die vorgestellte Variante zu Sprachdateien verhilft, die nun nicht mehr von WordPress Language Pack-Updates überschrieben werden und freue mich auf Anregungen, Kommentare und Hinweise.
Aktuelle WordPress Version zum Zeitpunkt der Artikelerstellung: 4.4.1
Sonstige Seiten zum Thema:
Automatische Hintergrund-Aktualisierungen konfigurieren
*) Query Monitor von John Blackbourn ist ein für Entwickler äußerst hilfreiches Plugin
Update: 20.3.2016:
Funktionsnamen in add_action() im letzten Codeblock korrigiert:
add_action( ‚after_setup_theme‘, ‚load_custom_language_files_for_plugins‘ );
Update 27.6.2016:
Es scheint, dass jedes Plugin etwas anders reagiert.
Für WooCommerce gilt beispielsweise folgende Reihenfolge der Suche nach Übersetzungen:
1.) wp-content/languages/woocommerce/
2.) wp-content/plugins/woocommerce/i18n/languages/
3.) wp-content/languages/plugins/
1.) und 2.) sind updatesicher.
In 3.) werden Updates abgelegt. Leider greift auch das Loco Translate Plugin nur auf den 3. Ordner zu. Um also eigene Übersetzungen mit Loco Translate zu übersetzen und danach mo-Files zu kompilieren müssen die eigenen Sprachdateien hin- und herkopiert werden.
Update 21.12.2018:
Um eine Übersetzung – unabhängig von einem Plugin – updatesicher und frühzeitig zu laden:
add_filter( 'load_textdomain_mofile', 'load_custom_plugin_translation_file', 10, 2 ); /* * Replace 'textdomain' with your plugin's textdomain. e.g. 'woocommerce'. * File to be named, for example, yourtranslationfile-de_DE.mo * File to be placed, for example, wp-content/lanaguages/textdomain/yourtranslationfile-de_DE.mo */ function load_custom_plugin_translation_file( $mofile, $domain ) { if ( 'textdomain' === $domain ) { $mofile = WP_LANG_DIR . '/textdomain/yourtranslationfile-' . get_locale() . '.mo'; } return $mofile; }
Artikel um Übersetzungen für WooCommerce updatesicher abzulegen siehe auch:
https://docs.woothemes.com/document/woocommerce-localization/
Hallo,
vielen Dank für die gute Übersicht zum Thema. Eine Frage habe ich: Die Größe der Sprachdateien ist ja manchmal nicht ganz gering (Woocommerce z.B. knapp 800 kb). Wenn WordPress mehrere Sprachdateien von verschiedenen Orten liest, also z.B. von
wp-content/languages/woocommerce/
wp-content/plugins/woocommerce/i18n/languages/
wp-content/languages/plugins/
werden dann jedes Mal diese knapp 800 kb geladen? Das wäre ja aus Performancegründen tunlichst zu vermeiden. Wäre die Praxis, dass, wenn man eigene Übersetzungen anlegt, dann jeweils wirklich nur die eigenen Strings in eine neue .po/.mo Datei schreibt, damit diese klein bleiben?
Hallo Martin,
Im Prinzip hast du recht.
Gibt es mehrere *.mo-Dateien zu einem Plugin, werden sie alle eingelesen.
Ich denke ein Weg wäre, nur die Teile die übersetzt werden sollen in die *.po-Datei aufnehmen und diese zu einer *.mo zu kompilieren.
Der andere, vermutlich einfachere Weg ist, eine komplette *.mo-Datei zu haben, alle anderen zu löschen und die Sprachupdates zu deaktivieren.
Damit wird zu jedem Plugin nur mehr eine *.mo eingelesen.
Gruß
Ronnie
Hallo,
vielen dank für diesen tollen Beitrag, leider überschreibt er mir trotz allem die übersetzungsdatei. Habe in meinem Child-Theme die functions.php mit den Daten befüllt und die Sprachdateien nach wp-content/languages/woocommerce/ gepackt.
Wenn ich nun die Originale aus dem eigentlichen language entferne ist mein shop wieder Englisch – er akzeptiert scheinbar meinen Unterordner nicht, oder greift nicht auf die Befehle der funktions.php zu.
Hast du einen Rat? – hab jetzt einfach erstmal die woocommerce-de_DE.mo + .po auf chmod: 444 geändert, dadurch dürfte er die auch nicht mehr ändern dürfen, oder?
Hallo Christian,
probier mal das neue Verfahren, mit add_filter, siehe mein aktuelles Update.
Da kannst du, unabhängig von einem Plugin, jede Übersetzung laden, die du willst.
Zur Kontrolle, in welcher Reihenfolge tatsächlich die Übersetzungen geladen werden, empfehle ich dir das Query Monitor Plugin.
Gruß Ronnie
Super, das war´s wohl !
Hab testweise die Original-Files gelöscht um zu sehen ob er meine geänderten lädt – und es klappt 😉
Vielen Dank für deine schnelle Hilfe und ein frohes Weihnachtsfest …
Gruß Christian
Gerne!
Danke, dir auch schöne Weihnachten!
LG Ronnie
Danke für den tollen Beitrag, er hat mir sehr geholfen!
Ein kleiner Bug-Report – im Code unter „Finale Lösung“ hat sich ein Fehler eingeschlichen:
„foreach( $all_plugins as $plugin => $field) {“ soll heißen:
„foreach( $all_plugins as $plugin => $field) {
Da hat wohl irgendeine WP-Instanz oder mein Browser (Firefox) bei der Zeicheninterpretation versagt.
LG Günter
Hi Günter,
vielen Dank für deinen Beitrag.
Divi und eingebetteter Code ist eine eigene Sache…… 🙁
Ist nun korrigiert.
LG Ronnie