Als Gott sah was beim Turmbau zu Babel passierte, entschied er sich die Sprache der Menschen zu teilen, so dass der Eine den Anderen nicht mehr verstehen möge. Ähnlich scheinen sich die Designer von GWT gefühlt haben, als sie die Internationalisierung von GWT 2.x entworfen haben.
Das Internationalisierungsprinzip von GWT ist an sich sehr einfach. Man nehme ein paar properties-Dateien, inkludiere das I18N-Modul und lasse sich die entsprechende Java-Datei generieren. Soweit so gut. Leider nicht so, wenn man den UIBinder verwenden möchte. Hier haben sich bereits diverse verzweifelte Entwickler einen “abgebrochen” um herauszufinden, welche Dateien an welcher Stelle liegen müssen, damit man in den jeweiligen ui.xml-Dateien eine vernünftige Übersetzung erhält.
Grundsätzlich gilt, dass Übersetzungs-Keys aus ui.xml-Dateien immer im Package com.google.gwt.i18n.client mit dem Namen LocalizableResource_[locale].properties liegen müssen. Diesen Umstand verdanken wir dem MessagesWriter, der vom UiBinderGenerator herangezogen wird. Dieser generiert zwar die Messages-Dateien für alle Keys, die sich in einer ui.xml-Datei befinden in eine eigene Messages-Klasse, importiert aber mittels
writer.write("import static com.google.gwt.i18n.client.LocalizableResource.*;");
ausschließlich die Übersetzungen aus LocalizableResource. Soweit so gut, möchte man meinen. Problematisch wird es dann, wenn man Übersetzungen sowohl im Java-Code, als auch im in den ui-Dateien haben will. Typisches Beispiel sind hier Tabellen vom Typ CellTable, deren Spalten normalerweise in den View-Klassen definiert werden. Will man hier nun sprachlich angepasste Spaltenköpfe haben, muss man diese mittels einer Messages-Klasse übersetzen.
In diesem Moment steht man vor der Frage: Möchte ich zwei Übersetzungsdateien verwalten, in denen ggf. Keys doppelt auftreten, oder möchte ich nur Eine für beide Mechanismen haben?
Ohne auf die unsäglichen und teilweise unpraktikablen Varianten einzugehen, die sich im Netz bereits befinden bzw. die kryptisch auf der GWT-Dokumentation vorgestellt wurde, stelle ich hier meine zwei Lösungen vor, die ich für sinnvoll erachte.
Variante 1: ui:with
Die klassische Variante. Man nehme einfach die existierenden Messages und importiere sie wie folgt in die ui.xml-Datei.
<ui:with field='messages' type='com.my.app.MyMessages'/>
<g:Label text="{messages.hello}" />
Diese Variante hat den Vorteil, dass man auf die komplette Bandbreite der Erweiterungen, die für Messages-Klassen bereitgestellt wurde zugreifen kann.
Variante 2: Customized Messages-Interface
In Variante 1 nutzen wir den Mechanismus, der eigentlich für den Java-Code gedacht war für den UiBinder. Natürlich geht es auch anders herum. Wir “kopieren” einfach den Mechanismus der MessagesWriter-Klasse. Wir bauen uns ein Interface und importieren die LocalizableResources.
package com.mypackage;
import static com.google.gwt.i18n.client.LocalizableResource.*;
public interface MyAppMessages extends Messages {
@DefaultMessage("Example")
String example();
}
Der GWT-Compiler greift hier einfach das Interface und verknüpft die Methoden mit den Keys aus der jeweiligen LocalizableResource-Datei. Damit lassen sich auch innerhalb von Java-Dateien die Keys aus LocalizableResource verwenden.
Achtung:
Leider gibt es nicht die Möglichkeit LocalizableResource-Dateien über den i18n-Aufruf in eine Klasse übersetzen zu lassen, da LocalizableResource bereits ein existierendes Interface ist und die Übersetzung zu einer sogenannten zyklischen Vererbung führt.
Fazit
Mit beiden Varianten lässt sich das bestehende Problem lösen, dass man nur einen Mechanismus zur Internationalisierung innerhalb von GWT-Projekten nutzen möchte. Welche der beiden zum Einsatz kommt hängt stark von der bestehenden Struktur und – was nicht zur vernachlässigen ist – von der Motivation des Entwicklers, welche Schritte für ihn in seinem Arbeitsprozess besser integriert werden können, ab. Während Variante 1 den Aufruf des i18n-Creators erfordert, muss in Variante 2 das Interface immer manuell angepasst werden.
Clean Code stellt viele Aspekte für unschönen Code vor, die man bei der täglichen Arbeit zwar bemerkt, aber bei denen man unter Umständen nicht immer weiss wie man sie besser machen kann. Der Autor gibt sich hierbei nicht den Anspruch des allumfassenden “So-und-nicht-anders”, sondern lässt Freiraum für eigene Entscheidungen.
Martin Fowler stellt verschiedene Refactoring-Mechanismen vor und erklärt die Motivation, die hinter jedem einzelnen steckt. Da es sich hierbei u.a. auch um sehr triviale Refactorings handelt, wirkt das Buch zeitweise etwas trocken.
Ähnlich aufgebaut wie das “Refactoring”-Buch werden hier verschiedene Code-Probleme anhand einfacher Beispiele erklärt und deren Lösung durch Patterns oder in Richtung eines Patterns vorgestellt.