[Commits] r2486 - in core/trunk/geoext: lib lib/GeoExt lib/GeoExt/locale tests tests/lib/GeoExt

commits at geoext.org commits at geoext.org
Tue Nov 23 14:43:48 CET 2010


Author: tschaub
Date: 2010-11-23 14:43:48 +0100 (Tue, 23 Nov 2010)
New Revision: 2486

Added:
   core/trunk/geoext/lib/GeoExt/Lang.js
   core/trunk/geoext/lib/GeoExt/locale/
   core/trunk/geoext/lib/GeoExt/locale/GeoExt-fr.js
   core/trunk/geoext/tests/lib/GeoExt/Lang.html
Modified:
   core/trunk/geoext/lib/GeoExt.js
   core/trunk/geoext/tests/list-tests.html
Log:
Adding simple i18n framework to facilitate localization of applications.  Thanks fredj for the French strings.  r=ahocevar (closes #352)

Added: core/trunk/geoext/lib/GeoExt/Lang.js
===================================================================
--- core/trunk/geoext/lib/GeoExt/Lang.js	                        (rev 0)
+++ core/trunk/geoext/lib/GeoExt/Lang.js	2010-11-23 13:43:48 UTC (rev 2486)
@@ -0,0 +1,134 @@
+/**
+ * Copyright (c) 2008-2010 The Open Source Geospatial Foundation
+ *
+ * Published under the BSD license.
+ * See http://svn.geoext.org/core/trunk/geoext/license.txt for the full text
+ * of the license.
+ */
+
+/** api: (define)
+ *  module = GeoExt
+ *  class = Lang
+ *  base_link = `Ext.util.Observable <http://dev.sencha.com/deploy/dev/docs/?class=Ext.util.Observable>`_
+ */
+Ext.namespace("GeoExt");
+
+/** api: constructor
+ *  .. class:: Lang
+ *
+ *      The GeoExt.Lang singleton is created when the library is loaded.
+ *      Include all relevant language files after this file in your build.
+ */
+GeoExt.Lang = new (Ext.extend(Ext.util.Observable, {
+
+    /** api: property[locale]
+     *  ``String``
+     *  The current language tag.  Use :meth:`set` to set the locale.  Defaults
+     *  to the browser language where available.
+     */
+    locale: navigator.language || navigator.userLanguage,
+
+    /** private: property[dict]
+     *  ``Object``
+     *  Dictionary of string lookups per language.
+     */
+    dict: null,
+
+    /** private: method[constructor]
+     *  Construct the Lang singleton.
+     */
+    constructor: function() {
+        this.addEvents(
+            /** api: event[localize]
+             *  Fires when localized strings are set.  Listeners will receive a
+             *  single ``locale`` event with the language tag.
+             */
+            "localize"
+        );
+        this.dict = {};
+        Ext.util.Observable.constructor.apply(this, arguments);
+    },
+
+    /** api: method[add]
+     *  :param locale: ``String`` A language tag that follows the "en-CA"
+     *      convention (http://www.ietf.org/rfc/rfc3066.txt).
+     *  :param lookup: ``Object`` An object with properties that are dot
+     *      delimited names of objects with localizable strings (e.g.
+     *      "GeoExt.VectorLegend.prototype").  The values for these properties
+     *      are objects that will be used to extend the target objects with
+     *      localized strings (e.g. {untitledPrefix: "Untitiled "})
+     *
+     *  Add translation strings to the dictionary.  This method can be called
+     *  multiple times with the same language tag (locale argument) to extend
+     *  a single dictionary.
+     */
+    add: function(locale, lookup) {
+        var obj = this.dict[locale];
+        if (!obj) {
+            this.dict[locale] = Ext.apply({}, lookup);
+        } else {
+            for (var key in lookup) {
+                obj[key] = Ext.apply(obj[key] || {}, lookup[key]);
+            }
+        }
+        if (!locale || locale === this.locale) {
+            this.set(locale);
+        } else if (this.locale.indexOf(locale + "-") === 0) {
+            // current locale is regional variation of added strings
+            // call set so newly added strings are used where appropriate
+            this.set(this.locale);
+        }
+    },
+
+    /** api: method[set]
+     * :arg locale: ''String'' Language identifier tag following recommendations
+     *     at http://www.ietf.org/rfc/rfc3066.txt.
+     *
+     * Set the language for all GeoExt components.  This will use any localized
+     * strings in the dictionary (set with the :meth:`add` method) that
+     * correspond to the complete matching language tag or any "higher order"
+     * tag (e.g. setting "en-CA" will use strings from the "en" dictionary if
+     * matching strings are not found in the "en-CA" dictionary).
+     */
+    set: function(locale) {
+        // compile lookup based on primary and all subtags
+        var tags = locale ? locale.split("-") : [];
+        var id = "";
+        var lookup = {}, parent;
+        for (var i=0, ii=tags.length; i<ii; ++i) {
+            id += (id && "-" || "") + tags[i];
+            if (id in this.dict) {
+                parent = this.dict[id];
+                for (var str in parent) {
+                    if (str in lookup) {
+                        Ext.apply(lookup[str], parent[str]);
+                    } else {
+                        lookup[str] = Ext.apply({}, parent[str]);
+                    }
+                }
+            }
+        }
+
+        // now extend all objects given by dot delimited names in lookup
+        for (var str in lookup) {
+            var obj = window;
+            var parts = str.split(".");
+            var missing = false;
+            for (var i=0, ii=parts.length; i<ii; ++i) {
+                var name = parts[i];
+                if (name in obj) {
+                    obj = obj[name];
+                } else {
+                    missing = true;
+                    break;
+                }
+            }
+            if (!missing) {
+                Ext.apply(obj, lookup[str]);
+            }
+        }
+        this.locale = locale;
+        this.fireEvent("localize", locale);
+    }
+}))();
+

Added: core/trunk/geoext/lib/GeoExt/locale/GeoExt-fr.js
===================================================================
--- core/trunk/geoext/lib/GeoExt/locale/GeoExt-fr.js	                        (rev 0)
+++ core/trunk/geoext/lib/GeoExt/locale/GeoExt-fr.js	2010-11-23 13:43:48 UTC (rev 2486)
@@ -0,0 +1,23 @@
+/**
+ * Copyright (c) 2008-2010 The Open Source Geospatial Foundation
+ *
+ * Published under the BSD license.
+ * See http://svn.geoext.org/core/trunk/geoext/license.txt for the full text
+ * of the license.
+ */
+
+/**
+ * @requires GeoExt/Lang.js
+ */
+
+GeoExt.Lang.add("fr", {
+    "GeoExt.tree.LayerContainer.prototype": {
+        text: "Couches"
+    },
+    "GeoExt.tree.BaseLayerContainer.prototype": {
+        text: "Couches de base"
+    },
+    "GeoExt.tree.OverlayLayerContainer.prototype": {
+        text: "Couches additionnelles"
+    }
+});

Modified: core/trunk/geoext/lib/GeoExt.js
===================================================================
--- core/trunk/geoext/lib/GeoExt.js	2010-11-18 12:58:04 UTC (rev 2485)
+++ core/trunk/geoext/lib/GeoExt.js	2010-11-23 13:43:48 UTC (rev 2486)
@@ -119,7 +119,8 @@
             "GeoExt/plugins/PrintExtent.js",
             "GeoExt/plugins/AttributeForm.js",
             "GeoExt/widgets/PrintMapPanel.js",
-            "GeoExt/state/PermalinkProvider.js"
+            "GeoExt/state/PermalinkProvider.js",
+            "GeoExt/Lang.js"
         );
 
         var agent = navigator.userAgent;

Added: core/trunk/geoext/tests/lib/GeoExt/Lang.html
===================================================================
--- core/trunk/geoext/tests/lib/GeoExt/Lang.html	                        (rev 0)
+++ core/trunk/geoext/tests/lib/GeoExt/Lang.html	2010-11-23 13:43:48 UTC (rev 2486)
@@ -0,0 +1,195 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <script type="text/javascript" src="../../../../ext/adapter/ext/ext-base.js"></script>
+        <script type="text/javascript" src="../../../../ext/ext-all.js"></script>
+        <script type="text/javascript" src="../../../lib/GeoExt/Lang.js"></script>
+        <script type="text/javascript">
+
+            var Test, original = GeoExt.Lang.locale;
+            function setup() {
+                GeoExt.Lang.dict = {};
+                Test = {
+                    things: {
+                        more: {
+                            Class: Ext.extend(Ext.util.Observable, {
+                                prop1: "chicken",
+                                prop2: "beer"
+                            })
+                        },
+                        Class: Ext.extend(Ext.util.Observable, {
+                            prop1: "burger",
+                            prop2: "milk"
+                        })
+                    },
+                    data: {
+                        foo: "bar"
+                    }
+                };
+            }
+            function teardown() {
+                GeoExt.Lang.dict = {};
+                if (original) {
+                    GeoExt.Lang.set(original);
+                }
+                Test = undefined;
+            }
+            
+            function test_add(t) {
+                t.plan(4);
+                setup();
+                
+                GeoExt.Lang.add("en", {
+                    "Test.things.more.Class.prototype": {
+                        prop1: "potato"
+                    },
+                    "Test.things.Class.prototype": {
+                        prop1: "pizza",
+                        prop2: "soda"
+                    },
+                    "Test.data": {
+                        foo: "baz"
+                    }
+                });
+                
+                t.ok("en" in GeoExt.Lang.dict, "en lookup added");
+                t.eq(GeoExt.Lang.dict.en["Test.data"].foo, "baz", "foo set");
+                
+                GeoExt.Lang.add("en", {
+                    "Test.data": {
+                        bar: "foo"
+                    }
+                });
+                t.eq(GeoExt.Lang.dict.en["Test.data"].bar, "foo", "bar set");
+                t.eq(GeoExt.Lang.dict.en["Test.data"].foo, "baz", "foo unchanged");
+                
+                teardown();
+            }
+            
+            function test_add_more(t) {
+                t.plan(3);
+                setup();
+                
+                // assume browser language is regional variation
+                GeoExt.Lang.locale = "xx-YY";
+                
+                GeoExt.Lang.add("xx-YY", {
+                    "Test.things.Class.prototype": {
+                        prop1: "xx-YY"
+                    }
+                });
+                
+                t.eq(Test.things.Class.prototype.prop1, "xx-YY", "Calling add immediately sets strings when locale matches browser language.");
+                
+                GeoExt.Lang.add("xx", {
+                    "Test.things.Class.prototype": {
+                        prop1: "xx",
+                        prop2: "xx"
+                    }
+                });
+                
+                t.eq(Test.things.Class.prototype.prop1, "xx-YY", "Calling add with more general locale doesn't override regional specific strings.");
+                t.eq(Test.things.Class.prototype.prop2, "xx", "Calling add with more general locale than browser language sets strings not previously set.");
+                
+                teardown();
+            }
+            
+            function test_set(t) {
+                t.plan(12);
+                setup();
+
+                GeoExt.Lang.add("en", {
+                    "Test.things.more.Class.prototype": {
+                        prop1: "potato"
+                    },
+                    "Test.things.Class.prototype": {
+                        prop1: "pizza",
+                        prop2: "soda"
+                    },
+                    "Test.data": {
+                        foo: "baz"
+                    }
+                });
+                
+                GeoExt.Lang.add("xx-YY", {
+                    "Test.data": {
+                        foo: "bar xx-YY"
+                    }
+                })
+
+                GeoExt.Lang.add("xx", {
+                    "Test.things.more.Class.prototype": {
+                        prop1: "steak"
+                    },
+                    "Test.things.Class.prototype": {
+                        prop1: "pasta",
+                        prop2: "water"
+                    },
+                    "Test.data": {
+                        foo: "bar xx"
+                    }
+                });
+                
+                GeoExt.Lang.set("en");
+                t.eq(GeoExt.Lang.locale, "en", "locale set to en");
+                
+                t.eq(Test.things.more.Class.prototype.prop1, "potato", "[en] potato set");
+                t.eq(Test.things.more.Class.prototype.prop2, "beer", "[en] beer unmolested");
+                t.eq(Test.things.Class.prototype.prop1, "pizza", "[en] pizza set");
+                t.eq(Test.things.Class.prototype.prop2, "soda", "[en] soda set");
+                t.eq(Test.data.foo, "baz", "[en] baz set");
+                
+                GeoExt.Lang.set("xx-YY");
+                t.eq(GeoExt.Lang.locale, "xx-YY", "locale set to xx-YY");
+                
+                t.eq(Test.things.more.Class.prototype.prop1, "steak", "[xx-YY] steak set");
+                t.eq(Test.things.more.Class.prototype.prop2, "beer", "[xx-YY] beer unmolested");
+                t.eq(Test.things.Class.prototype.prop1, "pasta", "[xx-YY] pasta set");
+                t.eq(Test.things.Class.prototype.prop2, "water", "[xx-YY] water set");
+                t.eq(Test.data.foo, "bar xx-YY", "[xx-YY] baz xx-YY set");
+
+                teardown();                
+            }
+            
+            function test_browser_language(t) {
+                setup();
+
+                var locale = navigator.language || navigator.userLanguage;
+                if (locale) {
+                    
+                    t.plan(5)
+                    t.eq(GeoExt.Lang.locale, locale, "Locale set to browser language when available.");
+
+                    GeoExt.Lang.add("boguslang", {
+                        "Test.data": {
+                            foo: "bogus"
+                        }
+                    });
+
+                    t.ok(GeoExt.Lang.locale !== "boguslang", "Adding localized strings doesn't change locale if browser language is detected");
+                    t.ok(Test.data.foo !== "bogus", "Default values not changed when localized strings are added for non-browser language");
+                    
+                    GeoExt.Lang.add(locale, {
+                        "Test.data": {
+                            foo: "localized"
+                        }
+                    });
+                    
+                    t.eq(Test.data.foo, "localized", "Adding localized strings for browser language sets values on target objects");
+                    
+                    GeoExt.Lang.set("boguslang");
+                    t.eq(Test.data.foo, "bogus", "Call GeoExt.Lang.set to override browser default language");
+
+                } else {
+                    t.plan(1);
+                    t.debug("No browser language detected.")
+                    t.ok(true, "No browser language detected, nothing to test");
+                }
+                
+                teardown();                
+            }
+
+        </script>
+    </head>
+    <body></body>
+</html>

Modified: core/trunk/geoext/tests/list-tests.html
===================================================================
--- core/trunk/geoext/tests/list-tests.html	2010-11-18 12:58:04 UTC (rev 2485)
+++ core/trunk/geoext/tests/list-tests.html	2010-11-23 13:43:48 UTC (rev 2486)
@@ -16,6 +16,7 @@
   <li>lib/GeoExt/data/WMSCapabilitiesReader.html</li>
   <li>lib/GeoExt/data/WMSDescribeLayerReader.html</li>
   <li>lib/GeoExt/data/WMCReader.html</li>
+  <li>lib/GeoExt/Lang.html</li>
   <li>lib/GeoExt/plugins/PrintPageField.html</li>
   <li>lib/GeoExt/plugins/PrintProviderField.html</li>
   <li>lib/GeoExt/plugins/PrintExtent.html</li>



More information about the Commits mailing list