zurück zum Artikel

Tipps und Tricks für AngularJS, Teil 1: Internationalisierung

Manfred Steyer

Hat man viel Zeit in die Entwicklung einer Anwendung gesteckt, will man sie in der Regel so vielen Nutzern wie möglich zugänglich machen. Für weltweilten Erfolg sind einige landesspezifische Anpassungen nötig, bei denen das Modul angular-translate helfen kann.

Tipps und Tricks für AngularJS, Teil 1 - Internationalisierung

Hat man viel Zeit in die Entwicklung einer Anwendung gesteckt, will man sie in der Regel so vielen Nutzern wie möglich zugänglich machen. Für weltweilten Erfolg sind einige landesspezifische Anpassungen nötig, bei denen das Modul angular-translate helfen kann.

Während der Entwicklung bezeichnet Internationalisierung Maßnahmen, um Anwendungen für unterschiedliche Kulturen, Sprachen und Regionen einfach anpassen zu können. Mit AngularJS 1.4 soll sich zwar einiges ändern, in der hier betrachteten Version 1.3 gibt es diesbezüglich allerdings nur wenig Unterstützung. Darunter fallen die bekannten Filter date und number, die ein Datum beziehungsweise eine Zahl entsprechend der Gepflogenheiten einer bestimmten Region oder einer bestimmten Sprache formatieren.

Als Ergänzung kann beispielsweise das freie Modul angular-translate [1] dienen. Es bietet eine Vielzahl an Möglichkeiten bei der Umsetzung von Übersetzungen. Dazu zählen unterschiedliche Ladestrategien für Übersetzungen von Titeln, Texten oder Beschriftungsfeldern, deren Einbinden in die Anwendung unter Berücksichtigung von Platzhaltern sowie ein Sprachwechsel zur Laufzeit.

Um angular-translate nutzen zu können, ist zunächst die im Lieferumfang enthaltene Skript-Datei angular-translate.js in das Projekt einzubinden. Zusätzlich referenziert man das von letzterer bereitgestellte Modul pascalprecht.translate.

Mehr Infos

Installation via Bower

Entwickler können die im Beitrag genutzten Teile von angular-translate komfortabel mit dem Paketmanager Bower installieren. Dazu sind die nachfolgenden Anweisungen auf der Kommandozeile einzugeben:

bower install angular-translate
bower install angular-translate-loader-url
bower install angular-translate-loader-static-files
bower install angular-translate-loader-partial

Zum Bereitstellen einer Konfiguration kommt, wie unter AngularJS üblich, eine an die Funktion config übergebene Konfigurationsfunktion zum Einsatz. Sie lässt sich dem $translateProvider injizieren, wie folgendes Beispiel veranschaulicht:

var app = angular.module("demo",
["pascalprecht.translate"]);

app.config(function($translateProvider) {

$translateProvider.translations('de', {
GRUSS: 'Hallo {{name}}!',
STATUS: {
INFO: "Heute ist ein schöner Tag!"
}
});

$translateProvider.translations('en', {
GRUSS: 'Hello {{name}}!',
STATUS: {
INFO: "It's a nice day today!"
}
});
$translateProvider.preferredLanguage("de");
});

Es nutzt die von $translateProvider angebotene Funktion translations, um zunächst deutsche Texte festzulegen. Der erste Parameter repräsentiert dabei das Kürzel der Sprache; der zweite ein Objekt mit Texten, das angular-translate als Übersetzungstabelle (translation table) bezeichnet. Letztere bildet sogenannte Übersetzungs-IDs (translation-ids) auf Texte der jeweiligen Sprache ab. Diese können Platzhalter aufweisen, die eine Interpolator genannte austauschbare Komponente zur Laufzeit durch konkrete Werte ersetzt. Der standardmäßig von angular-translate genutzte Interpolator verwendet dazu jene Möglichkeiten, die AngularJS auch im Zuge der Datenbindung nutzt. Deswegen sind die Platzhalter in zwei geschweifte Klammern einzuschließen. Das eben betrachtete Beispiel definiert so name.

Anhand der Eigenschaft INFO ist zu sehen, dass Entwickler Einträge schachteln können, um sie nach Themenbereichen zu gruppieren. Solche Hierarchien drückt angular-translate flach und nutzt Schlüssel, die sich aus denen der Hierachie zusammensetzen. Für den betrachteten Eintrag vergibt angular-translate beispielsweise den Schlüssel STATUS.INFO.

Der zweite Aufruf von translations hinterlegt für dieselben IDs englische Gegenstücke. Um Deutsch als bevorzugte Sprache festzulegen, ruft der oben gezeigte Programmcode die Funktion preferredLanguage auf und übergibt dabei das zuvor festgelegte Kürzel de.

Der folgende Codeausschnitt für einen Controller bietet die Möglichkeit, zwischen den konfigurierten Sprachen zu wechseln:

app.controller("DemoCtrl", function($scope, $translate) { 
$scope.model = {};
$scope.model.setCulture = function() {
$translate.use($scope.model.culture);
}
$scope.model.culture = $translate.preferredLanguage();
});

Dazu bekommt er den Service $translate injiziert. Die im Scope eingerichtete Funktion setCulture nutzt dessen use, um zur in $scope.model.culture hinterlegten Sprache zu wechseln. Diese Eigenschaft bekommt die eingangs konfigurierte bevorzugte Sprache zugewiesen.

Das Markup im nächsten Quelltextauszug verwendet den gezeigten Controller:

<div ng-app="demo" ng-controller="DemoCtrl">
<h1>{{ 'GRUSS' | translate:{name:'Max'} }}</h1>
<p>
<span translate="GRUSS" translate-values="{name:'Max'}"
translate-default="Moin!"></span>
{{ 'STATUS.INFO' | translate }}
</p>
<div>
<form>
<input ng-model="model.culture"><input type="button"
ng-click="model.setCulture()" value="Ok">
</form>
</div>
</div>
Mehr Infos

Es verwendet den Filter translate, um innerhalb des h1-Elements den Text, der hinter der ID GRUSS steht, für die aktuell festgelegte Sprache auszugeben. Der erste Parameter des Filters ist ein Objekt, das Werte für die definierten Platzhalter enthält. Kann der Filter für die angegebene ID keinen Eintrag finden, schreibt er stattdessen die ID in die Seite. Im darauf folgenden p-Element nutzt das Markup die Direktive translate für denselben Zweck.

Das Attribut translate erhält dabei die zu nutzende ID und translate-values, ein Objekt mit Werten für die festgelegten Platzhalter. Ist die Direktive nicht in der Lage, einen Eintrag zu finden, nimmt sie mit dem im Attribut translate-default hinterlegten Wert vorlieb. Haben Entwickler das Attribut nicht eingerichtet, verhält sich translate, wie der gleichnamige Filter und nutzt die ID statt der Übersetzung.

Zusätzlich veranschaulicht das Beispiel die Nutzung hierarchisch organisierter Übersetzungstabelleneinträge, indem es die ID STATUS.INFO nutzt. Das Formular am Ende des Markups gibt Entwicklern die Möglichkeit, zur Laufzeit die Sprache zu wechseln. Dazu bindet es die Eigenschaft model.culture, mit der der Controller die aktuelle Sprache repräsentiert, an ein Eingabefeld und die Funktion setCulture, die die erfasste Sprache als aktuelle Sprache festlegt, an eine Schaltfläche.

Damit eine Single Page Application nicht von Anfang an sämtliche Übersetzungen für die unterstützten Sprachen enthalten muss, gibt angular-translate Entwicklern die Möglichkeit, sie bei Bedarf nachzuladen. Dazu findet die austauschbare Komponente Loader Verwendung. Das Team hinter angular-translate stellt auch für sie mehrere Implementierungen zur Verfügung. Eine davon ist staticFilesLoader, die pro Sprache eine JSON-Datei mit Einträgen für die Übersetzungstabelle erwartet. Dabei ist zu beachten, dass beim Einsatz von JSON die Namen von Eigenschaften in Anführungszeichen zu halten sind:

{
"GRUSS": "Hallo {{name}}!",
"STATUS": {
"INFO": "Heute ist ein schöner Tag!"
}
}

Um eine solche Datei zu laden, binden Entwickler die Skriptdatei angular-translate-loader-static-files.js ein und konfigurieren anschließend den $translateProvider mit der Funktion useStaticFilesLoader:

$translateProvider.useStaticFilesLoader({
prefix: '/translations/',
suffix: '.json'
});

Letztere nimmt ein Objekt mit Details der Konfiguration entgegen. Aus dessen Eigenschaften ergeben sich die Pfade der erwarteten JSON-Dateien. Um sie zu ermitteln, stellt der staticFilesLoader dem Kürzel der gewählten Sprache den konfigurierten Präfix voran und den konfigurierten Suffix nach. Im betrachteten Fall würde er somit die deutsche Übersetzungstabelle unter der URL /translations/de.json erwarten, die englische hingegen unter /translations/en.json.

Um Übersetzungen von einem HTTP-Service zu beziehen, lässt sich auf den urlLoader zurückgreifen. Er befindet sich in der Datei angular-translate-loader-url.js und ist über die Funktion useUrlLoader zu konfigurieren:

$translateProvider.useUrlLoader('api/translations');

Sie nimmt die URL des HTTP-Services entgegen. Um eine Übersetzungstabelle zu laden, wendet sich der urlLoader an diese URL und hängt dabei den Parameter lang an. Im betrachteten Fall würde er somit die zu nutzenden deutschen Texte via api/translations?lang=de abrufen und ihre englischen Gegenstücke unter api/translations?lang=en suchen.

Bei großen Single Page Applications ist es nicht immer sinnvoll, die gesamte Übersetzungstabelle einer Sprache zu laden. Stattdessen gibt es die Option, aus Performancegründen nur die Einträge für das aktuell verwendete Modul zu beziehen. Der sogenannte partialLoader unterstützt bei solch einem Vorhaben, indem er es ermöglicht, die Inhalte der Übersetzungstabellen aufzuteilen. Zur Erläuterung der Funktionsweise dieser Komponente soll folgende Ordnerstruktur dienen:

translations
?
????home
? de.json
? en.json
?
????notifications
de.json
en.json

Im Ordner translations enthält die Struktur folglich für die Module home und notifications Übersetzungsdateien für deutsch- und englischsprachige Nutzer in entsprechend benannten Unterordnern.

Um diese Dateien bei Bedarf mit dem partialLoader zu laden, ist zunächst die Skript-Datei angular-translate-loader-partial.js einzubinden. Nachdem die Standardsprache festgelegt ist, können Entwickler mit der vom Service $translatePartialLoaderProvider gebotenen Funktion addPart die Namen der Teile angeben, die der partialLoader beim Start der Anwendung laden soll. Der weiter unten folgende Quelltextauszug beschränkt sich auf den Teil mit dem Namen home. Zusätzlich sind Entwickler angehalten, den partialLoader über die Funktion useLoader des $translateProviders zu konfigurieren. Hierbei handelt es sich um eine Loader-neutrale Funktion, die im ersten Argument den Namen des Loaders entgegennimmt. Das zweite Argument ist ein Objekt mit Konfigurationsinformationen.

$translateProvider.preferredLanguage("de");
$translatePartialLoaderProvider.addPart('home');
$translateProvider.useLoader('$translatePartialLoader', {
urlTemplate: '/translations/{part}/{lang}.json'
});
Mehr Infos

Nachladen von Übersetzungen

Da das Nachladen von Übersetzungen einige Zeit in Anspruch nehmen kann, besteht nach dem Wechsel der Sprache die Gefahr, dass angular-translate für diese (in der Regel kurze) Zeitspanne keine Texte anzeigen kann. Die Dokumentation von angular-translate empfiehlt daher, die Texte in einer Fallback-Sprache direkt mit der Anwendung auszuliefern, damit während des Ladens zumindest diese angezeigt werden.

Das Beispiel führt dafür die Eigenschaft urlTemplate an. Sie gibt Auskunft über die URLs, unter denen der partialLoader die Übersetzungen für die einzelnen Teile finden kann. Hierzu hinterlegt der Entwickler eine URL mit den Platzhaltern {part} und {lang}. Ersterer repräsentiert den Namen des zu ladenden Teils, letzterer das Kürzel der gewünschten Sprache. Entsprechend der Konfiguration im betrachteten Fall würde partialLoader die deutschen Übersetzungen für den Teil notifications, passend zu der weiter oben gezeigten Ordnerstruktur, unter /translations/notifications/de.json erwarten. Da Entwickler die zu nutzende URL unter Angabe der besprochenen Platzhalter frei festlegen können, können sie damit gleichermaßen statische JSON-Dateien und auch HTTP-Services referenzieren.

Um bei Bedarf einen weiteren Teil für die Übersetzungstabelle zu laden, geben Entwickler dem Service $translatePartialLoader dessen Namen über die Funktion addPart bekannt. Danach ist die Funktion $translate.refresh aufzurufen:

$translatePartialLoader.addPart("notifications");
$translate.refresh();

Das freie Modul angular-translate bietet eine Vielzahl an Möglichkeiten für den Umgang mit Übersetzungen in mehrsprachigen Anwendungen. Dank seiner austauschbaren Komponenten können Entwickler es flexibel ihren Bedürfnisse anpassen. So lassen sich etwa unterschiedliche Strategien zum Laden von Übersetzungstabellen oder Ersetzen von Platzhaltern verwenden. Neben den in diesem Beitrag behandelten Möglichkeiten bietet angular-translate auch Unterstützung für die Pluralisierung von Hauptwörtern, dem Zwischenspeichern von Einträgen oder dem Einrichten von Fallback-Strategien für Fälle, in denen die Anwendung die gewünschte Sprache nicht unterstützt.

Manfred Steyer
ist Trainer und Berater bei IT-Visions [11] und für den Fachbereich Software Engineering der berufsbegleitenden Studienrichtung IT und Wirtschaftsinformatik an der FH CAMPUS 02 in Graz verantwortlich. In seinem aktuellen Buch "AngularJS: Moderne Webanwendungen und Single Page Applications mit JavaScript" behandelt er die vielen Seiten von Googles populärem SPA-Framework.
(jul [12])


URL dieses Artikels:
https://www.heise.de/-2516976

Links in diesem Artikel:
[1] https://github.com/angular-translate/angular-translate
[2] https://www.heise.de/hintergrund/Tipps-und-Tricks-fuer-AngularJS-Teil-1-Internationalisierung-2516976.html
[3] https://www.heise.de/hintergrund/Tipps-und-Tricks-fuer-AngularJS-Teil-2-ES2015-2555723.html
[4] https://www.heise.de/hintergrund/Tipps-und-Tricks-fuer-AngularJS-Teil-3-OAuth-2-0-2620374.html
[5] https://www.heise.de/ratgeber/Tipps-und-Tricks-fuer-AngularJS-Teil-4-Animationen-in-AngularJS-1-4-2774580.html
[6] https://www.heise.de/ratgeber/Tipps-und-Tricks-fuer-AngularJS-Teil-5-Transformationen-und-Interceptors-2821088.html
[7] https://www.heise.de/hintergrund/Backend-lose-Entwicklung-mit-AngularJS-und-ngMockE2E-3086974.html
[8] https://www.heise.de/ratgeber/Tipps-und-Tricks-mit-AngularJS-Teil-7-GUIs-mit-Angular-2-und-Redux-Implementierung-ngrx-store-I-3192046.html
[9] https://www.heise.de/ratgeber/test-3194264.html
[10] https://www.heise.de/ratgeber/AngularJS-1-x-und-2-0-mit-dem-Component-Router-parallel-betreiben-2679282.html
[11] http://www.it-visions.at
[12] mailto:jul@heise.de