Samstag, 15. Dezember 2012

I18n - Einen Wherigo in verschiedenen Sprachen anbieten

This is the german version of the article. It is also available in english

Voraussetzung: "Der Einstieg in lua“
"Internationalisierung bedeutet in der Informatik beziehungsweise in der Softwareentwicklung, ein Programm so zu gestalten, dass es leicht (ohne den Quellcode ändern zu müssen) an andere Sprachen und Kulturen angepasst werden kann."
So startet der aktuelle Wikipedia-Artikel das Thema Internationalisierung.
In diesem Tutorial erfährst du wie man unter Nutzung des ResourceStrings-Ansatzes dein Cartrigde in mehreren Sprachen anbieten und dabei deinen eigentlichen Code vom Sprachproblem abkoppeln kannst.

Abbildung 1: Definition der Textausgaben in allen drei Sprachen
Wir beginnen mit der Definition einer Variablen language, die wir auch gleich als persistente Variable so ans Cartrigde anheften, dass es einen Save/Reload Vorgang übersteht.
Anschließend wird ein Array messages erzeugt indem die unterschiedlichen Textausgaben, sortiert nach Sprachen festgehalten werden.
Jetzt wird sich so mancher Entwickler fragen, warum speichert das messages-Array seine eingestellte Sprache nicht selbst sondern nutzt eine extra Variable. Der Grund hierzu liegt einzig in der Persistenzproblematik. Wir wollen ja auf keinen Fall das gesamte Array persistieren, denn an diesen Werten ändert sich zur Laufzeit nichts. Der einzig dynamische Teil ist die eingestellte Sprache, die nach dem Start z.B per Multiple-Choice ausgewählt werden kann. Um ein aufwendiges OnSave / OnRestore Prozedere zu verhindern, nutzen wir lieber den Workaround über die zweite Variable.
Für jede Sprache (in unserem Beispiel Deutsch, Englisch und Französich) wird ein Subarray wie in Zeile 5-7 definiert. Danach beginnt die eigentliche Stringdefinition.
Die Idee dabei ist, dass im Cartrigde an der Stelle wo für die Begrüßung des Spieler ein übersetzter String ausgegeben werden soll, der key hello verwendet wird und je nachdem welche Sprache eingestellt ist "Herzlich Willkommen", "Welcome" oder "Bienvenue" ausgegeben wird.
Bevor wir jedoch zum Auswerten des passenden String kommen, möchte ich noch auf ein paar Besonderheiten hinweisen. Aufmerksame Codestudenten werden bereits festgestellt haben, dass es für den key hi kein Wert für die englische Version definiert wurde. Wäre die Sprache auf en gesetzt, würde hier der Wert der defaultLanguage (siehe Zeile 4) gezogen. Wie man so etwas auflöst folgt weiter unten. In Zeile 13-18 stehen Beispiele für Verwendung von Platzhaltern, wenn z.B der Name des Spieler in den übersetzten String eingebaut werden soll.

Die Sprache ist aktuell auf Deutsch gestellt. Man kann aber bei Start des Cartrigdes durch eine einfache Multiple-Choice-Abfrage die Sprache wählbar machen und dann language auf en oder fr setzen.

Abbildung 2: Abfrage eines übersetzten Keys
Nach der Definition geht es zur Verwendung der ResourceStrings. Zeile 20 wählt aus dem messages-Array zunächst das für die Sprache passende Subarray (hier messages.de und daraus der Eintrag für unseren key. Wird diese Funktion mit dem key hello aufgerufen, so besitzt value den Wert von messages.de.hello also Herzlich Willkommen zum i18n Wherigo.
Was aber passiert, wenn die Sprache auf en eingestellt ist und der key hi ausgewählt wird. message.en.hi ist nämlich nicht definiert. Dieser Fall ist in Zeile 22-24 behandelt. Dort wird auf die defaultLanguage zurückgegriffen. Man sollte also darauf achten, dass für die defaultLanguage immer ein Wert definiert ist.
Im Idealfall definiert man immer alle Sprachen direkt, in der Praxis kann es aber vorkommen, dass man Erweiterungen (im Wherigo kommt eine Station hinzu und damit neue Sprachelemente) für Deutsch und Englisch direkt eintragen kann, für die spanische Version aber noch warten muss bis der passende Übersetzer (Der Austauschcacher aus Barcelona) Zeit hat die Strings zu vervollständigen.

Abbildung 3: Textausgabe mit Parametern
Bleibt noch die Sache mit den Parametern, z.B der Name des Cachers. Im definierten key können Platzhalter mit einer Zeichenkombi codiert werden. Es sollte eine Kombi sein, die man sonst nicht verwenden muss. Ich habe mich deswegen für #1# entschieden. Bei der Auflösung des ResourceStrings wird dieser Platzhalter mittels string.gsub durch den betreffenenden Wert ersetzt. Abb. 3 zeigt die überarbeitete getMessage Methode in der wir Platzhalter ersetzen können.
getMessage("helloWithName","Krolock") liefert für die deutsche Version "Hallo Krolock, wie geht es dir?"
Um die Anzahl der Parameter im String flexible zu halten, bedienen wir uns des variablen vararg Argumentes "...". Man kann die Funktion getMessage neben dem key mit keinen, einem oder mehreren weiteren Parametern aufrufen.
Beipspielaufrufe:
getMessage("hello")
getMessage("helloWithName","Krolock")
getMessage("openDoor","XYZ","A15N")

Mittels #arg werden die Anzahl der Argumente ermittelt, auf die dann mit arg[1], arg[2] usw zugegriffen wird

Abbildung 4: Aufrufbeispiel der Übersetzung
Bei mehreren Parametern muss aber auf die Reihenfolge achten, wie man an Zeile 16-18 erkennen kann. Für den key openDoor gibt es unterschiedliche Reihenfolgen in der deutschen und englischen Version
Abb. 4 zeigt uns wie man die Übersetzungen im Wherigo aufrufen und benutzen kann. Jeweils ein Beispiel für keinen, einen oder zwei Platzhalter.
Wie sich heute (10.03.14) erst gezeigt hat, zeigt der WhereYouGo-Player bei dem vararg Parameter ein etwas anderes Verhalten. Wenn die Methode getMessage ohne zweiten Parameter aufgerufen wird (also nur mit key) so ist arg kein leeres Array sondern nil. Um dem vorzubeugen fügen wir in Zeile 25 if arg ~= nil then ein, was wir in Zeile 29 mit end wieder schließen.

Eine Kleinigkeit fehlt noch, nämlich die strikte Trennung von Code und Übersetzung. Im Konzept der ResourceBundles trennt man diese Aspekte insofern, als dass für jede Sprache eine eigene Datei angelegt wird.
Man könnte also eine Datei messages_de.lua, messages_en.lua und messages_fr.lua erzeugen und diese dann mit require "messages_de" usw. einbetten. Software-Entwicklungsumgebungen wie eclipse oder Intellij IDEA bieten hier viel Unterstützung bei der Pflege der Übersetzungen.
Diese Trennung ist aber optional. Ihr könnt auch alles im lua-Teil des Urwigos direkt eingeben, wenn es nicht zu unübesichtlich wird.

Update 20.04.14: Da auch der PiGo-Player Probleme mit vararg zu haben scheint, schaut euch auch den Folgeartikel Sprachausgaben übersetzten ohne vararg an.