Ein Echtzeit-Sprachumschalter zur UI-Lokalisierung in WPF (Teil 3)de

Dritter und letzter Teil der Serie zur WPF-Lokalisierung, in welchem die zuvor erstellten Klassen (Daten und Datenbindung) miteinander zu interagieren beginnen. Im Zentrum dieses Posts steht der eigentliche Localizer, welcher über deklarativen XAML-Code die Sprachdaten an die  Oberfläche bindet. Abschließend wird die vorgestellte Lösung kritisch betrachtet und Raum für Verbesserungen aufgezeigt.

Im zweiten Teil dieser Serie haben wir uns mit wichtigen terminologischen und konzeptuellen Grundlagen zur Lokalisierung einer WPF-Anwendung vertraut gemacht. Außerdem wurde eine Schnittstelle zur Datenhaltung vorgestellt und in einer einfachen Variante implementiert sowie die Basisklasse für die Datenbindung an die UI erzeugt. In diesem Teil geht es nun um die zentrale Verantwortlichkeit der Datenbindung und des Umschaltens der aktiven Zielkultur.

Aufgabe 3: Die Lokalisierung und der Sprachumschalter

Als zentrale Anforderung an unseren Sprachumschalter (“Localizer“) haben wir gefordert, dass die Datenbindung möglichst einfach und direkt zu realisieren sei. “Einfach und direkt” bedeutet in WPF, dass wir uns die Möglichkeiten der deklarativen XAML-Oberflächenbeschreibung zu Nutze machen. Als Beispiel sei ein Fenster gegeben, das die folgenden Komponenten enthält:

  • Sprachliste und -umschalter: eine Liste aller verfügbaren Zielkulturen, aus welcher eine dieser durch Anklicken aktiviert werden kann,
  • Beispiel-Steuerelemente, die über den Localizer angebunden werden und deren Datenbindung beim Umschalten der aktiven Kultur automatisch aktualisiert wird.

Betrachten wir folgenden Code, der obige Komponenten beschreibt:

Das Fenster stellt den Localizer (dessen Implementierung wir in Kürze besprechen werden) als zentrale Property zur Verfügung. Das Grid-Wurzelelement (Zeile 9) bindet den DataContext per ElementName an das Fenster (das wir in Zeile 3 benannt haben), so dass die Steuerelemente auf die Liste der verfügbaren Zielkulturen (Zeile 17) sowie die Sprachdaten (Zeilen 21 und 22) im Localizer zugreifen können. Letztere greifen über einen sogenannten Indexer auf die Liste der verfügbaren Übersetzungen zu. Hierbei handelt es sich um den zentralen Bindungspunkt, der für die Echtzeit-Aktualisierung der UI beim Wechseln der aktiven Zielkultur sorgt.

Bevor wir uns den Mechanismus genauer ansehen, folgt hier noch die Fenster-Implementierung:

Ganz wichtig: Der Localizer wird instanziiert (Zeile 10) noch bevor das Fenster selbst initialisiert (Zeile 13) wird. Dies ist erforderlich, da sonst die Datenbindung (die mit dem Fenster initialisiert wird) die Bindungsquelle nicht finden würde.

Wie genau arbeitet nun der Localizer?

Zunächst werden der Localizer-Instanz die Sprachendaten über den Konstruktor injiziert (Zeilen 12 bis 23), wobei dieser die verfügbaren Zielkulturen (Property AvailableTargetCultures, Zeile 45) und die aktive Zielkultur (Property ActiveTargetCulture, Zeilen 29 bis 31) nach außen weiterreicht. Der bereits erwähnte Kniff an dieser Implementierung zeigt sich in Zeile 52. Hier wird eine Indexed Property, welche die verfügbaren Übersetzungen der Datenhaltung an die Benutzeroberfläche durchreicht, direkt in XAML angebunden. Diese einfache aber elegante Lösung bietet folgende Vorteile:

  • die Übersetzungen können über ihren jeweiligen Index sowie über eine einzige Eigenschaft im Localizer deklarativ angebunden werden, so dass kein prozeduraler Code für die Lokalisierung und die Umschalt-Logik geschrieben werden muss;
  • die Übersetzungen werden über ihre zentralen Indizes direkt zur Verfügung gestellt (d.h. wir sparen uns jegliche Indirektion z.B. über GUIDs oder anderweitige nichtssagende Schlüssel);
  • die Sprachumschaltung erfolgt über das bereits implementierte Interface INotifyPropertyChanged, und zwar genau dann, wenn der Wert der ActiveTargetCulture Eigenschaft geändert wird (vgl. Z. 32-40);

Kritische Betrachtung und Schlussbemerkungen

Die hier vorgestellte Lösung erfüllt die eingangs erwähnten Anforderungen (u.a. externe Sprachpakete, direkte und einfache Anbindung der Übersetzungen an die UI, Umschaltung der Lokalisierung in Echtzeit). Insbesondere die Echzeit-Umschaltung der aktiven Zielkultur erfolgt über die Anbindung der UI an eine Indexed Property. Für die Auslagerung der Sprachdaten in externe Dateien sorgt die Daten-Schnittstelle ILocalizerTranslations, welche beliebige Implementierungen und Datenquellen zulässt (zentrale XML-Datei, Datenbank-Server, REST-Service etc.).

Kritisch anzumerken bleibt, dass es sich hierbei um eine rudimentäre Lösung handelt, die noch viel Verbesserungs- und Erweiterungspotential mit sich bringt:

  • zu Debug-Zwecken kann ein Debug-Schalter implementiert werden (z.B. über eine weitere Property); ist dieser gesetzt, kann bereits in der XML-Entwurfsansicht geprüft werden, ob die Datenbindung korrekt funktioniert (indem z.B. der Index an Stelle der eigentlichen Übersetzung an die UI gebunden wird);
  • der Zugriff auf die Übersetzungen in der Indexed Property erfolgt über einen alphanumerischen Index, welcher in eckigen Klammern notiert den üblichen XAML-Benennungs-Einschränkungen unterliegt; so sind z.B. keine Sonderzeichen erlaubt (wohl aber Leerzeichen); daher bietet er sich nur für kurze, prägnante Indizes an; für längere Texte hingegen können beschreibende Schlüssel gewählt werden;
  • die vorgestellte Lösung erlaubt auch das Binden an Nicht-Text-Übersetzungen wie z.B. Bilder; hierfür ist das Interface bereits vorbereitet, indem es für Texteinträge die Property GetTextEntries zur Verfügung stellt; entsprechend könnten weitere Zieltypen erstellt werden (z.B. via Dictionary<stringImageSource> GetImageEntries(CultureInfo targetCulture)); der Localizer müsste dann ebenfalls entsprechend modifiziert werden;
  • natürlich würde eine komplexe Anwendung den Zugriff auf den Localizer über eine separate Schnittstelle zur Verfügung stellen sowie seine Abhängigkeiten per DI verwalten;

Source Code auf GitHub

Der Source Code für die zweite Iteration (Tag v0.2) ist auf GitHub verfügbar.

Related Posts

European Citizens’ Initiative Against TTIP and CETA