Eine Frage, die sich mir schnell stellte, als ich mit der Entwicklung der ersten App auf Basis von Sencha Touch begann, war die Frage nach der Internationalisierung. Wie konnte man am besten eine mehrsprachige Anwendung umsetzen? Ich habe ein wenig im Netz gesucht und mich dabei inspirieren lassen. Herausgekommen ist eine Erweiterung für Sencha Touch, die es ermöglicht JSON Dateien zu laden, in denen Texte gespeichert sind.
Dieses kleine Helferlein war für mich einfach eine erste Fingerübung im Umgang mit Sencha Touch.
Mehr gibt es dazu eigentlich nicht zu sagen. Per XMLHttpRequest werden die gewünschten Dateien für die angegebenen Sprachen einmalig geladen und später greift man auf diese Daten über den Pfad in dem JSON Objekt zu.
Das funktioniert auch in einem PhoneGap-Projekt.
Für Verbesserungsvorschläge bin ich jederzeit dankbar 😉
- /**
- * aflx - always flexible
- * http://blog.aflx.de
- * ak@aflx.de
- *
- * Copyright 2011 Alexander Keller
- * All Rights Reserved.
- */
- Ext.ns('Ext.util');
- /**
- * This class provides the possibility to load files for different languages containing
- * a JSON object, where the leaves are strings/translations. Later you can access theses
- * strings by calling the __() function passing the path to the leave.
- */
- Ext.util.Translation = Ext.extend(Object, {
- language: "de",
- files: [ "application" ],
- languages: [ "de" ],
- pathPrefix: "app/resources",
- loadingStack: {},
- data: {},
- // Recursive looking for the given path
- getTranslation: function(value, data) {
- var prop = value.shift();
- if (data == null) {
- console.log("Could not load language resources!");
- return false;
- }
- if (typeof(data[prop]) == "object") {
- return this.getTranslation(value, data[prop]);
- }
- return data[prop];
- },
- // extract the language from a given string (example: de_DE => de)
- parseLang: function(lang) {
- lang = lang.replace(/_/, '-').toLowerCase();
- if (lang.length > 3) {
- lang = lang.substring(0, 3) + lang.substring(3).toUpperCase();
- }
- return lang;
- },
- // downloads a file and saves it
- loadResources: function(resource, options) {
- var me = this;
- if (options == null) {
- options = {};
- }
- if (options.language == null) {
- options.language = this.language;
- }
- var file = resource + "-" + options.language + ".json";
- if (options.pathPrefix != null) {
- file = options.pathPrefix + "/" + resource + "-" + options.language + ".json";
- }
- if (this.loadingStack[file] == "loading" ||
- this.loadingStack[file] == "ready") {
- return;
- } else if (this.loadingStack[file] == "error") {
- return;
- } else {
- this.loadingStack[file] = "loading";
- }
- var xhr;
- try {
- xhr = new ActiveXObject('Msxml2.XMLHTTP');
- } catch (e) {
- try {
- xhr = new ActiveXObject('Microsoft.XMLHTTP');
- } catch (e2) {
- try {
- xhr = new XMLHttpRequest();
- } catch (e3) {
- xhr = false;
- }
- }
- }
- xhr.onreadystatechange = function() {
- if(xhr.readyState == 4) {
- if (xhr.responseText) {
- var data = Ext.util.JSON.decode(xhr.responseText);
- me.data[options.language] = data;
- me.loadingStack[file] = "ready";
- return true;
- } else {
- console.log("Could not load language resources!");
- me.loadingStack[file] = "error";
- return false;
- }
- }
- };
- xhr.open("GET", file, XMLHttpRequest.SYNC);
- xhr.send(null);
- },
- // searching for a path in the JSON object
- translate: function(value, options) {
- if (options == null) {
- options = {};
- }
- // if not requesting the translation for a specific language,
- // use the active language
- if (options.language == null) {
- options.language = this.language;
- }
- // load the files if not already done
- if (options.resources != null) {
- if (options.pathPrefix == null) {
- options.pathPrefix = "";
- }
- for(var i = 0; i < options.resources.length; i++) {
- this.loadResources(options.resources[i], options);
- }
- }
- return this.getTranslation(value.split("."), this.data[options.language]);
- }
- });
- // A reference to an instance of the translator (singleton).
- Ext.util.Translation.self = null;
- /**
- * The setup for the translations.
- * Call this function before using this class...
- *
- * Usage example
- * =============
- *
- * Ext.util.Translation.init({
- * files: [ "application" ],
- * language: "de",
- * languages: [ "de" ],
- * pathPrefix: "app/resources"
- * });
- *
- */
- Ext.util.Translation.init = function(options) {
- Ext.util.Translation.self = new Ext.util.Translation();
- Ext.util.Translation.load(options);
- };
- /**
- * The load() function can be used to load an additional language pack.
- *
- * @param options - an object
- * pathPrefix: the path relative to the html file where this js is include
- * languages: languages to load (use de, en, it etc...)
- * language: the active language, one of the languages array
- * files: an array with the files to load (It's not the real filename,
- * it's just the first part of the filename. If you want to load
- * application-de.json you have to add "application" to this array and
- * "de" to the languages array.)
- */
- Ext.util.Translation.load = function(options) {
- for (var i = 0; i < options.files.length; i++) {
- for (var j = 0; j < options.languages.length; j++) {
- Ext.util.Translation.self.loadResources(
- options.files[i],{ pathPrefix: options.pathPrefix, language: options.languages[j]}
- );
- }
- }
- };
- /**
- * The translation texts are saved as a JSON object. So this function does not translate
- * a string, it returns just a value for a given path in this JSON object.
- *
- * Usage example:
- *
- * example-de.json
- * ===============
- *
- * {
- * "title": {
- * "hello_world": "Hallo Welt!"
- * }
- * }
- *
- * Getting the translation
- * =======================
- *
- * var trans = Ext.util.Translation.__('title.hello_world');
- */
- Ext.util.Translation.__ = function(value) {
- if (Ext.util.Translation.self == null) {
- Ext.util.Translation.init({
- files: Ext.util.Translation.self.files,
- languages: Ext.util.Translation.self.languages,
- pathPrefix: Ext.util.Translation.self.pathPrefix
- });
- }
- return Ext.util.Translation.self.translate(value);
- };
Hey Alex,
mir geistert seit einiger Zeit schon folgendes im Kopf herum:
Wenn PhoneGap AJAX will, ist es dann möglich mit GWT Mobile-Apps zu schreiben?
Laut Google sieht es tatsächlich so aus.
Die Schritte wären dann:
Anwendung schreiben in GWT (Java mit allen Sprachfeatures)
-> compile to AJAX mit GWT
-> compile to App mit PhoneGap
Ich hab das ganze natürlich noch nicht selber ausprobiert, aber gerade im Zusammenhang mit i18n würde mich interessieren ob die GWT-eigenen Mechnaismen, bei denen zur Compilezeit die Sprache in die Anwendung geschraubt werden kann greifen können (der Installer müsste dann wohl die richtige Endanwendung wählen).
Selbst wenn das ungünstig ist, gibt es von GWT-Seite schon vordefinerte Mechanismen um Resourcen zur Laufzeit zu lokalisieren (also auch Bilder und Files etc.).
Das ganze find ich recht spannend und ich werde dich bei Gelegenheit mal dazu anrufen falls ich Zeit finde mal selber in Richtung mobile Entwicklung zu linsen :).
Ich kenne mich mit GWT leider nicht so gut aus. Klingt aber interessant!
Ein Treffen würde ich auch mal wieder begrüßen 😉
Beim Stöbern auf der PhoneGap Seite habe ich heute das hier gefunden: GwTMobile
Das sieht für mich doch sehr interessant aus! Vielleicht werde ich mich damit mal etwas ausführlicher beschäftigen.