Blog

Requirements: Cordova || Ionic || Angular && JQuery
Auf der Suche im Web, nach einer vernünftigen Localization-Lösung für Cordova, Ionic bzw. Angular war ich nicht erfolgreich. Oops, so viel neue Technik und so schlechte Doku im Netz. Na gut..., muss ich es mir halt selbst besorgen... Also fing ich an zu coden und das kam dabei raus:

Es gibt natürlich jede Menge varalteter Artikel (z.B. aus dem Jahr 2014) im Web. Diese beschreiben aber ebend auch eine veraltete Technik... Ich versuche mit diesem Artikel eine moderne Variante für Übersetzungen aufzuzeigen, die aber trotzdem "lightway" ist.

Mal vorab: Zur Localization von Cordova-Apps wird das Globalization Plugin von Apache/Cordova nicht mehr benötigt. Das Plugin wird über kurz oder lang aus dem Plugin-Repository von Cordova und Ionic rausfliegen. Moderne Browser liefern eine Localization von Haus aus mit. Und mit meiner Lösung existiert immer noch ein Fallback, falls der Browser wider Erwarten doch keine Localization ausliefert. Das Plugin bzw. die Technik, die der Browser mitliefert, nennt sich: „Internationalization API“.

Die Programmierung verlief früher wie folgt: In Cordova wurde das Globalization-Plugin implementiert und mit folgendem Script ermittelt welche Sprache der lokale Browser verwendet:

navigator.globalization.getPreferredLanguage(function (language) {     
    console.log('language: ' + language.value + '\n');
        }, function () {
    console.log('Error getting language\n');
});
Auf dieses Plugin kann nun komplett verzichtet werden. Siehe dazu auch hier: Migration from the Globalization Plugin
Die Internationalization API von Browser lässt sich stabil ausführen, wenn (wie üblich) das „deviceready“ Statement "abgefeuert" wurde.

Check this out:
document.addEventListener("deviceready", function() {
    if (window.Intl && typeof window.Intl === 'object') {

        console.log("%cAPI available. Detected Lan: " + navigator.language + "", "color: green");
        alert(navigator.language);

    }
}, false);

Je nach Sprache und Browser liefert navigator.language verschiedene Landeskürzel (de, de-DE, en, en-US, fr, fr-FR etc.). Daraus extrahiere ich eine Variable, mit der später die entsprechende JSON-Datei, mit den eigentlichen Übersetzungen, geladen wird.

document.addEventListener("deviceready", function() {
  
  if (window.Intl && typeof window.Intl === 'object') {

    console.log("%cAPI available. Lan: " + navigator.language + "", "color: green");
                       
    // URL implementieren
    if (navigator.language == "de" || navigator.language == "de-DE") {
        var url = "i18n/de.json";
    } else if (navigator.language == "en" || navigator.language == "en-US") {
        var url = "i18n/en.json";
    } else {
        // Fallback für alle anderen Sprachen
        var url = "i18n/en.json";
        }
                            
    } else {
        // Fallback - Falls die API keine Sprache liefert.
        var url = "i18n/en.json";
    }
                        
                          
    seti18n(url); // fire Language-Chooser function with detected url
                         
}, false);

Die Übersetzungen befinden sich im Ordner: www/i18n/ und heißen de.json, en.json, etc.

json - ordner der einzelnen dateien

Die jeweilige JSON-Datei hat folgendes Format:
JSON-Datei:
{
  "Strings": [
  {
    "username": "Gib deinen Usernamen ein.",
    "password": "Gib dein Passwort ein.",
    "headingtext": "Überschrift Deutsch",
    "submit": "Abschicken"
  }
  ]
}

Vorbereitung der HTML-Datei:

Alle Tags, die potentiell übersetzt werden sollen, bekommen die Klasse ‚i18n‘ sowie
das ‚data-i18n‘ Attribut verpasst. Nun kommt der nächste Fallback ins Spiel. Kann die JSON-Datei aus irgend einem Grund nicht geladen werden, wird immer noch der Node-Wert aus dem HTML-Tag verwendet.

Beispiel eines Button-Tag:
<button class="i18n" data-i18n="submit" type="submit">Abschicken</button>
Das data-i18n Attribut definiert, welches JSON-Objekt eingefügt werden soll. In dem Fall unseres Beispiel Buttons ist dies das Objekt „submit“ --> Also: "submit": "Abschicken"

Let's Check the "seti18n" Function:
  function seti18n(url) {

   $.getJSON(url, function(data){    
    $(".i18n").each(function(){
      $(this).html(data.Strings[0][$(this).data("i18n")]);
      $(this).attr("placeholder",data.Strings[0][$(this).data("i18n-ph")]);   // i18n-ph für placeholder Übersetzungen only
      $(this).attr("value",data.Strings[0][$(this).data("i18n-val")]); // i18n-val für input value Übersetzungen only
     });
   });

  }
Gar nicht so spektugal eigentlich :-)

Die Funktion "seti18n" lädt per $.getJSON Befehl, die entsprechende JSON-Datei und "parkt" den Inhalt in der Variable ‚data‘. Anschließend werden sämtliche i18n Klassen mit dem JQuery Befehl: $(".i18n").each(function() identifiziert, und die JSON-Strings entsprechend eingesetzt.

Eine kleine, knackige Lösung, wie ich finde.

AUSBLICK: Mit dieser Lösung könnte man mit einem einfachen $.ajax Call selbstverständlich die JSON-Datei auch auf einem externen Server auslagern und somit (völlig unabhängig von der App) Übersetzungen und Aktualisierungen bearbeiten…