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;
}
Im Folgenden die Details und Erklärungen:

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. allgemeiner WP_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:

Loco_Translate_Header WooCommerce_Germanized_Header

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:

WooCommerce Germanized, Reihenfolge der geladenen  mo-Dateien

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/