/* AIRLocalizer.js - Revision: 1.5 */ /* ADOBE SYSTEMS INCORPORATED Copyright 2007-2008 Adobe Systems Incorporated. All Rights Reserved. NOTICE: Adobe permits you to modify and distribute this file only in accordance with the terms of Adobe AIR SDK license agreement. You may have received this file from a source other than Adobe. Nonetheless, you may modify or distribute this file only in accordance with such agreement. */ // Using the function method to obfuscate inner globals to the main script, so that the // "window" remains untouched. (function(){ /** * This is automatically replaced while building * @version "1.5" * @private */ var version = "1.5"; /** * Just to make sure we have the right shortcuts included * @namespace window.air * @private */ var air = { File : window.runtime.flash.filesystem.File, FileStream : window.runtime.flash.filesystem.FileStream, FileMode : window.runtime.flash.filesystem.FileMode, Capabilities : window.runtime.flash.system.Capabilities }; /** * Utility functions * @namespace util * @private */ var util = { /** * Returns the content of a file * @param file air.File File that should be read * @return string */ stringFromFile : function stringFromFile(file){ var fileStream = new air.FileStream(); fileStream.open(file, air.FileMode.READ); var result = fileStream.readUTFBytes(file.size); fileStream.close(); return result; } }; /** * Used for sorting the locales array * @private */ function LocaleId(){ this.lang = ''; this.script = ''; this.region = ''; this.extended_langs = []; this.variants = []; this.extensions = {}; this.privates = []; this.privateLangs = false; }; LocaleId.prototype = { transformToParent: function(){ if(this.privates.length>0){ this.privates.splice(this.privates.length-1, 1); return true; } var lastExtensionName = null; for(var i in this.extensions){ if(this.extensions.hasOwnProperty(i)){ lastExtensionName = i; } } if(lastExtensionName){ var lastExtension = this.extensions[lastExtensionName]; if(lastExtension.length==1){ delete this.extensions[ lastExtensionName ]; return true; } lastExtension.splice(lastExtension.length-1, 1); return true; } if(this.variants.length>0){ this.variants.splice(this.variants.length-1, 1); return true; } if(this.script!=''){ //check if we can surpress the script if(LocaleId.getScriptByLang(this.lang)!=''){ this.script=''; return true; }else if(this.region==''){ //maybe the default region can surpress the script var region = LocaleId.getDefaultRegionForLangAndScript(this.lang, this.script); if(region!=''){ this.region = region; this.script = ''; return true; } } } if(this.region!=''){ if(!(this.script=='' && LocaleId.getScriptByLang(this.lang) == '')){ this.region=''; return true; } } if(this.extended_langs.length>0){ this.extended_langs.splice(this.extended_langs.length-1, 1); return true; } return false; }, isSiblingOf: function(other){ if(this.lang==other.lang&&this.script==other.script){ return true; } return false; }, equals: function(locale){ return this.toString() == locale.toString(); }, toString: function(){ var stack = [ this.lang ]; Array.prototype.push.apply(stack, this.extended_langs); if(this.script!='') stack.push(this.script); if(this.region!='') stack.push(this.region); Array.prototype.push.apply(stack, this.variants); for(var i in this.extensions){ if(this.extensions.hasOwnProperty(i)){ stack.push(i); Array.prototype.push.apply(stack, this.extensions[i]); } } if(this.privates.length>0){ stack.push('x'); Array.prototype.push.apply(stack, this.privates); } return stack.join('_'); }, canonicalize: function(){ for(var i in this.extensions){ if(this.extensions.hasOwnProperty(i)){ //also clear zero length extensions if(this.extensions[i].length==0) delete this.extensions[i]; else this.extensions[i] = this.extensions[i].sort(); } } this.extended_langs = this.extended_langs.sort(); this.variants = this.variants.sort(); this.privates = this.privates.sort(); if(this.script == ''){ this.script = LocaleId.getScriptByLang(this.lang); } //still no script, check the region if(this.script == '' && this.region!=''){ this.script = LocaleId.getScriptByLangAndRegion(this.lang, this.region); } if(this.region=='' && this.script!=''){ this.region = LocaleId.getDefaultRegionForLangAndScript(this.lang, this.script); } } } LocaleId.registry = { "scripts": ["cyrl", "latn", "ethi", "arab", "beng", "cyrl", "thaa", "tibt", "grek", "gujr", "hebr", "deva", "armn", "jpan", "geor", "khmr", "knda", "kore", "laoo", "mlym", "mymr", "orya", "guru", "sinh", "taml", "telu", "thai", "nkoo", "blis", "hans", "hant", "mong", "syrc"], "scriptById": {"cyrl": 5, "latn": 1, "ethi": 2, "arab": 3, "beng": 4, "thaa": 6, "tibt": 7, "grek": 8, "gujr": 9, "hebr": 10, "deva": 11, "armn": 12, "jpan": 13, "geor": 14, "khmr": 15, "knda": 16, "kore": 17, "laoo": 18, "mlym": 19, "mymr": 20, "orya": 21, "guru": 22, "sinh": 23, "taml": 24, "telu": 25, "thai": 26, "nkoo": 27, "blis": 28, "hans": 29, "hant": 30, "mong": 31, "syrc": 32} , "defaultRegionByLangAndScript": {"bg": {5: "bg"}, "ca": {1: "es"}, "zh": {30: "tw", 29: "cn"}, "cs": {1: "cz"}, "da": {1: "dk"}, "de": {1: "de"}, "el": {8: "gr"}, "en": {1: "us"}, "es": {1: "es"}, "fi": {1: "fi"}, "fr": {1: "fr"}, "he": {10: "il"}, "hu": {1: "hu"}, "is": {1: "is"}, "it": {1: "it"}, "ja": {13: "jp"}, "ko": {17: "kr"}, "nl": {1: "nl"}, "nb": {1: "no"}, "pl": {1: "pl"}, "pt": {1: "br"}, "ro": {1: "ro"}, "ru": {5: "ru"}, "hr": {1: "hr"}, "sk": {1: "sk"}, "sq": {1: "al"}, "sv": {1: "se"}, "th": {26: "th"}, "tr": {1: "tr"}, "ur": {3: "pk"}, "id": {1: "id"}, "uk": {5: "ua"}, "be": {5: "by"}, "sl": {1: "si"}, "et": {1: "ee"}, "lv": {1: "lv"}, "lt": {1: "lt"}, "fa": {3: "ir"}, "vi": {1: "vn"}, "hy": {12: "am"}, "az": {1: "az", 5: "az"}, "eu": {1: "es"}, "mk": {5: "mk"}, "af": {1: "za"}, "ka": {14: "ge"}, "fo": {1: "fo"}, "hi": {11: "in"}, "ms": {1: "my"}, "kk": {5: "kz"}, "ky": {5: "kg"}, "sw": {1: "ke"}, "uz": {1: "uz", 5: "uz"}, "tt": {5: "ru"}, "pa": {22: "in"}, "gu": {9: "in"}, "ta": {24: "in"}, "te": {25: "in"}, "kn": {16: "in"}, "mr": {11: "in"}, "sa": {11: "in"}, "mn": {5: "mn"}, "gl": {1: "es"}, "kok": {11: "in"}, "syr": {32: "sy"}, "dv": {6: "mv"}, "nn": {1: "no"}, "sr": {1: "cs", 5: "cs"}, "cy": {1: "gb"}, "mi": {1: "nz"}, "mt": {1: "mt"}, "quz": {1: "bo"}, "tn": {1: "za"}, "xh": {1: "za"}, "zu": {1: "za"}, "nso": {1: "za"}, "se": {1: "no"}, "smj": {1: "no"}, "sma": {1: "no"}, "sms": {1: "fi"}, "smn": {1: "fi"}, "bs": {1: "ba"}}, "scriptIdByLang": {"ab": 0, "af": 1, "am": 2, "ar": 3, "as": 4, "ay": 1, "be": 5, "bg": 5, "bn": 4, "bs": 1, "ca": 1, "ch": 1, "cs": 1, "cy": 1, "da": 1, "de": 1, "dv": 6, "dz": 7, "el": 8, "en": 1, "eo": 1, "es": 1, "et": 1, "eu": 1, "fa": 3, "fi": 1, "fj": 1, "fo": 1, "fr": 1, "frr": 1, "fy": 1, "ga": 1, "gl": 1, "gn": 1, "gu": 9, "gv": 1, "he": 10, "hi": 11, "hr": 1, "ht": 1, "hu": 1, "hy": 12, "id": 1, "in": 1, "is": 1, "it": 1, "iw": 10, "ja": 13, "ka": 14, "kk": 5, "kl": 1, "km": 15, "kn": 16, "ko": 17, "la": 1, "lb": 1, "ln": 1, "lo": 18, "lt": 1, "lv": 1, "mg": 1, "mh": 1, "mk": 5, "ml": 19, "mo": 1, "mr": 11, "ms": 1, "mt": 1, "my": 20, "na": 1, "nb": 1, "nd": 1, "ne": 11, "nl": 1, "nn": 1, "no": 1, "nr": 1, "ny": 1, "om": 1, "or": 21, "pa": 22, "pl": 1, "ps": 3, "pt": 1, "qu": 1, "rn": 1, "ro": 1, "ru": 5, "rw": 1, "sg": 1, "si": 23, "sk": 1, "sl": 1, "sm": 1, "so": 1, "sq": 1, "ss": 1, "st": 1, "sv": 1, "sw": 1, "ta": 24, "te": 25, "th": 26, "ti": 2, "tl": 1, "tn": 1, "to": 1, "tr": 1, "ts": 1, "uk": 5, "ur": 3, "ve": 1, "vi": 1, "wo": 1, "xh": 1, "yi": 10, "zu": 1, "cpe": 1, "dsb": 1, "frs": 1, "gsw": 1, "hsb": 1, "kok": 11, "mai": 11, "men": 1, "nds": 1, "niu": 1, "nqo": 27, "nso": 1, "son": 1, "tem": 1, "tkl": 1, "tmh": 1, "tpi": 1, "tvl": 1, "zbl": 28}, "scriptIdByLangAndRegion": {"zh": {"cn": 29, "sg": 29, "tw": 30, "hk": 30, "mo": 30}, "mn": {"cn": 31, "sg": 5}, "pa": {"pk": 3, "in": 22}, "ha": {"gh": 1, "ne": 1}}}; LocaleId.getScriptByLangAndRegion = function(lang, region){ var langRegions = LocaleId.registry.scriptIdByLangAndRegion[ lang ]; if(typeof langRegions=='undefined') return ''; var scriptId = langRegions[region]; if(typeof scriptId!='undefined'){ return LocaleId.registry.scripts[scriptId].toLowerCase(); } return ''; } LocaleId.getScriptByLang = function(lang){ var scriptId = LocaleId.registry.scriptIdByLang[ lang ]; if(typeof scriptId!='undefined'){ return LocaleId.registry.scripts[scriptId].toLowerCase(); } return ''; } LocaleId.getDefaultRegionForLangAndScript = function(lang, script){ var lang = LocaleId.registry.defaultRegionByLangAndScript[ lang ]; var scriptId = LocaleId.registry.scriptById[ script ]; if(typeof lang!='undefined' && typeof scriptId !='undefined' ){ return lang[scriptId] || ""; } return ''; } LocaleId.fromString = function(str){ //states { var PrimaryLanguage = 0, ExtendedLanguages = 1, Script = 2, Region = 3, Variants = 5, Extensions = 6, Privates = 7; } var localeId = new LocaleId(); var state = PrimaryLanguage; var subtags = str.replace(/-/g, '_').split('_'); var last_extension; for(var i=0, l=subtags.length; i='a' && firstChar<='z' && subtag_length>=5 ) || ( firstChar>='0' && firstChar<='9' && subtag_length>=4 ) ) ){ //variant localeId.variants.push(subtag); state = Variants; }else if ( state < Privates && subtag_length==1 ){ //singleton if(subtag == 'x'){ state = Privates; last_extension = localeId.privates; } else { state = Extensions; last_extension = localeId.extensions[subtag] || []; localeId.extensions[subtag] = last_extension; } }else if(state >= Extensions){ last_extension.push(subtag); } } } localeId.canonicalize(); return localeId; } var LocaleSorter = { /** * Promote only that locales from preferenceLocales that have parents in locales * @private * @param locales String[] List of locales to be sorted. * @param preferenceLocales String[] List of locales in the preference order * @param addAll Adds all the locales at the end, even though no locale is in the preferences list. Default is true * @return String[] */ sortLocalesUsingPreferences: function(_locales, _preferenceLocales, ultimateFallbackLocale, addAll){ var result = []; var haveLocale = {}; function prepare(list){ var resultList = []; for(var i=0,l=list.length;i=0;i--){ var attr = node.attributes[i]; if(attr.name.toLowerCase()==attributeInnerPrefix){ setInner = true; innerValue = attr.value; }else if(this.isLocalAttribute(attr)){ params.push([this.getLocalAttribute(attr), attr.value]); } } if(setInner){ var value = this.getHtmlString(innerValue); try{ node.innerHTML = value||innerValue; }catch(e){ node.textContent = value||innerValue; } } for(var i=params.length-1;i>=0;i--){ var param = params[i]; var value = this.getHtmlString(param[1]); node.setAttribute(param[0], value||param[1]); } }, /** * Replaces each node under 'node' * @param node DOMElement * @public */ run: function (node){ this.attributePrefix = this.parent.attributePrefix.toLowerCase(); this.attributePrefixLength = this.attributePrefix.length; var treeWalker = document.createTreeWalker( node||document, NodeFilter.SHOW_ELEMENT, { acceptNode: function(node) { return NodeFilter.FILTER_ACCEPT; } }, false ); while(treeWalker.nextNode()) this.walkNode(treeWalker.currentNode); }, /** * Separates the bundleName and resourceName and returns the string from locale chain * @param resourceName String Default bundle is "default" * @return String */ getHtmlString: function(resourceName){ var bundleName; var i = resourceName.indexOf('.'); if(i!=-1){ bundleName = resourceName.substr(0, i); resourceName=resourceName.substr(i+1); } else bundleName = "default" return this.parent.getStringFromChain(bundleName, resourceName); } }; /** * Creates a new object used in Localizer to store loaded bundles, locale chain and bundles path * @private * @constructor * @v */ function LocalizerPrivate(parent){ this.parent = parent; // dom walker needed to update nodes this.domWalker = new DOMWalker(this); // Locale chain is the internal order that the framework uses to search for locales this.localeChain = []; // When the developer sets the locale chain we should disable the automatic detection this.autoLocaleChain = true; // Loaded locales should be sorted for later use this.localeCache = {}; // Default bundle path used to solve the locales directories this.bundlePath = new air.File('app:/locale/'); // Store the system capabilities for later use this.userPreference = air.Capabilities.languages || [air.Capabilities.language]; // Event listeners this.eventListeners = {}; this.attributePrefix = 'local_'; } LocalizerPrivate.prototype = { /** * Creates a new locale object that caches bundles and files * registers the locale for later use * @private * @param locale The localeName to be created * @return LocaleHash */ createLocaleCache: function(locale){ var obj = { name : locale, bundles: {}, files: {} }; this.localeCache[locale] = obj; return obj; }, /** * Returns the locale cache object. In case it is not already loaded it is created. * @private * @param locale String The localeName to be returned * @return LocaleHash */ getLocaleCache: function(locale){ return this.localeCache[locale] || this.createLocaleCache(locale); }, /** * Removes all the cached locales that are not in the locale chain * @private */ cleanupCache: function (){ var isInChain = {}; for(var i in this.localeChain) isInChain[ this.localeChain[i] ] = true; for(var locale in this.localeCache ) { if( ! isInChain[locale] ){ this.localeCache [ locale ] = null; delete this.localeCache [locale]; } } }, /** * Removes all the cached locales * @private */ clearCache: function (){ for(var locale in this.localeCache ) { this.localeCache [ locale ] = null; delete this.localeCache [locale]; } }, /** * Creates the bundle object. Also caches it for later use * @private * @param locale String The locale name where it should search the bundle * @param bundlename String The bundle name for the returned bundle * @return ResourceBundle */ createBundle: function(locale, bundleName){ var localeCache = this.getLocaleCache(locale); var file = this.bundlePath.resolvePath(locale).resolvePath(bundleName+".properties"); var resourceBundle = new ResourceBundle(file); localeCache.bundles[bundleName] = resourceBundle; if(!resourceBundle.found){ setTimeout(function(obj){ obj.dispatchEvent(new LocalizerEvent(Localizer.BUNDLE_NOT_FOUND, { bundleName: bundleName, locale: locale })); }, 0, this); } return resourceBundle; }, /** * Loads a bundle and returns it. * @private * @param locale String The locale name where it should search the bundle * @param bundleName String The bundle name to be loaded * @param useCache Bool Setting this to false will force the function to reread the bundle. Default is true * @return ResourceBundle */ loadBundle: function(locale, bundleName, useCache){ var localeCache = this.getLocaleCache(locale); useCache=(typeof useCache=='undefined')?true:useCache; return (useCache&&localeCache.bundles[bundleName]) || this.createBundle(locale, bundleName); }, /** * Loads a bundle and returns its keys and values as an object. * @private * @param locale String The locale name where it should search the bundle * @param bundleName String The bundle name to be loaded * @return Object Returns null if the bundle is not found. */ getResourceBundle: function(locale, bundleName){ var bundle = this.loadBundle(locale, bundleName); if(bundle==null) return null; //make a copy of the bundle var result = {}; for (var key in bundle.keys){ result [ key ] = bundle.keys [key]; } return result; }, /** * Creates a new ResourceFile based on the fileName * @private * @param locale String The locale name where it should search the file * @param fileName String Filename that should be loaded * @return ResourceFile */ createFile: function(locale, fileName){ var localeCache = this.getLocaleCache(locale); var file = this.bundlePath.resolvePath(locale).resolvePath(fileName); var resourceFile = new ResourceFile(file); localeCache.files[fileName] = resourceFile; if(!resourceFile.found){ setTimeout(function(obj){ obj.dispatchEvent(new LocalizerEvent(Localizer.FILE_NOT_FOUND, { fileName: fileName, locale: locale })); }, 0, this); } return resourceFile; }, /** * Loads a file and returns it. * @private * @param locale String The locale name where it should search the file * @param bundleName String The file name to be loaded * @param useCache Bool Setting this to false will force the function to reread the file. Default is true * @return ResourceFile */ loadFile: function(locale, fileName, useCache){ var localeCache = this.getLocaleCache(locale); useCache=(typeof useCache=='undefined')?true:useCache; return (useCache&&localeCache.files[fileName]) || this.createFile(locale, fileName); }, /** * Automatically discovers the available locales using the listing directory of the bundle path, * returns an array of locales * @private * @return StringArray */ discoverAndReturnAvailableLocales: function (){ var folder = this.bundlePath; // check if the file exists and is a directory if(!(folder.exists&&folder.isDirectory)){ // just die sillently, because a higher level should take care of that return []; } var localeChain = [] var localeFolders = folder.getDirectoryListing(); for(var i=0, l=localeFolders.length; i0 ){ var oldChain = this.localeChain; this.localeChain = this.cloneChain(chain); // switch off auto locale chain detection this.autoLocaleChain = false; // send some events this.diffChainsAndFireEvent(this.localeChain, oldChain); } }, /** * Returns the current locale chain * @return String[] */ getLocaleChain: function(){ return this.cloneChain(this.localeChain); }, /** * Returns the source string, but replaces the "{i}" string with args[i] * @param source String * @param args String[] * @return String */ template: function(source, args){ var parser = /{([^}]*)}/g; var a = source.split(parser); var result = []; var d=0; for(var i=0,l=a.length;i=0;i--){ var evh = handlers[i]; if(evh.eventName==eventName&&evh.handler==handler){ handlers.splice(i, 1); } } }, dispatchEvent: function(ev){ var handlers = this.eventListeners[ev.name]; if(!handlers) return; for(var i=handlers.length-1;i>=0;i--){ handlers[i].handler.call(this.parent, ev); } }, /** * Loads the bundle and returns the resource value * @public * @param bundleName String * @param resourceName String * @param locale String * @return String */ getString: function(bundleName, resourceName, locale){ var bundle = this.loadBundle(locale, bundleName); var result = bundle.get(resourceName); if(result==null){ setTimeout(function(obj){ obj.dispatchEvent(new LocalizerEvent(Localizer.RESOURCE_NOT_FOUND, { bundleName: bundleName, resourceName: resourceName, locale: locale })); }, 0, this); } return result; }, /** * loads the file and returns the contents * @public * @param fileName String * @param locale String * @return String */ getFile: function(fileName, locale){ var file = this.loadFile(locale, fileName); return file.getContent(); }, /** * Uses the locale chain to get the first defined value * @public * @param bundleName String * @param resourcename String * @return String */ getStringFromChain: function(bundleName, resourceName){ var result; var chain = this.localeChain; if(chain){ for(var i=0, l=chain.length; i // becomes // Click here to login update: function update(/* DOMElement, optional */ parentNode /* = document */){ this._private.update(parentNode); }, // * If “locale” argument is provided it is used to return the value of the localization resource // called “resourceName” located in the bundle called “bundleName” only for the locale “locale”. // Otherwise, the locale chain returned by “getLocaleChain” is used to lookup the first locale // that that provides the “resourceName”. For example if the locale chain is [fr_CA, fr] and a // particular resource is not found in “fr_CA”, the framework will also search that resource in the “fr”. // // * It will automatically load the bundles if needed. // // * Returns null if the resource is not found and fires one of the following events: // * RESOURCE_NOT_FOUND when the bundle does not contain the specified “resourceName”; // * BUNDLE_NOT_FOUND when the bundle file is not found. // // * If “templateArgs” is provided the function will use it to replace bracketed numbers in the resource // with the correspondent values from the “templateArgs” array (only where applicable, meaning // that if templateArgs[n] is not defined, “{n}” will not be changed): // * “{0}”, “{1}”, .... “{n}” will be replaced with templateArgs[0], templateArgs[1] ... templateArgs[n]. // * in order to skip replacement for one number, just set that “templateArgs” item to undefined or null; getString: function getString(/* String */ bundleName, /*String */ resourceName, /* optional String[] */ templateArgs, /* optional, String */ locale ) /*: String*/{ var result = null; if(locale){ result = this._private.getString(bundleName, resourceName, locale); }else{ result = this._private.getStringFromChain(bundleName, resourceName); } if(templateArgs&&result!=null){ result = this._private.template(result, templateArgs); } return result; }, // // * Returns the keys found in a bundle // // @param locale String The locale name where it should search the bundle // @param bundleName String The bundle name to be loaded // // * Returns null if the bundle is not found. // getResourceBundle: function(locale, bundleName){ return this._private.getResourceBundle(locale, bundleName); }, // * If “locale” argument is provided, it returns the contents of the file // “{bundlesDirectory}/{locale}/{resourceFileName}”. Otherwise, the locale // chain returned by “getLocaleChain” is used to lookup the first locale that // that provides the “resourceFileName” file. For example if the locale chain is // [fr_FR, fr] the file will be searched using that order: first will search // “{bundlesDirectory}/fr_FR/{resourceFileName}” and only if it is not found the // framework will continue to search for “{bundlesDirectory}/fr/{resourceFileName}”. // // * {bundlesPath} is the current bundle path set using “setBundlesDirectory”; // // * Returns null if the resource file is not found and fires: // * FILE_NOT_FOUND event; // // * If “templateArgs” is provided the function will use it to replace bracketed numbers in the // resource file with the correspondent values from the “templateArgs” array (only where applicable, // meaning that if templateArgs[n] is not defined, “{n}” will not be changed): // * “{0}”, “{1}”, .... “{n}” will be replaced with templateArgs[0], templateArgs[1] ... templateArgs[n]. // * in order to skip replacement for one number, just set that “templateArgs” item to undefined; getFile: function getFile(/* String */ resourceFileName, /* optional String[] */ templateArgs, /* String */ locale ) /*: String*/ { var result = null; if(locale){ result = this._private.getFile(resourceFileName, locale); }else{ result = this._private.getFileFromChain(resourceFileName); } if(templateArgs&&result!=null){ result = this._private.template(result, templateArgs); } return result; }, // * Sets the locale chain and updates the current locale used by all other functions. // // * Note: When “chain” argument is missing, is not an array or has zero length the function fails // and throws “air.Localizer.IllegalArgumentsError” exception // // * Fires LOCALE_CHANGE event, the developer should update the DOM using update function. This event // is fired synchronous whenever the locale chain has changed. setLocaleChain: function setLocaleChain(/* optional, String[] */ chain){ if(!(chain&&chain.hasOwnProperty&&chain.hasOwnProperty('length')&&chain.length>0)){ throw new Localizer.IllegalArgumentsError("Locale chain should be an array."); } this._private.setLocaleChain(chain); }, // * Returns the locale chain set by the previous call to “setLocaleChain”. // // * NOTE: If “setLocaleChain” hasn’t been called the function returns the automatically // detected locale chain computed when the Localizer is instantiated (see also the property called “localizer”); getLocaleChain: function getLocaleChain()/* :String[] */ { return this._private.getLocaleChain(); }, // * Sets the prefix for local attributes used in the “update” function. // // * Default prefix is “local_”. setLocalAttributePrefix: function setLocalAttributePrefix(value){ this._private.attributePrefix = value+''; }, // typical add/remove Event Dispatcher addEventListener: function addEventListener(/* String */ eventName, /* callback function */ callback){ this._private.addEventListener(eventName, callback); }, removeEventListener: function removeEventListener(/* String */ eventName, /* callback function */ callback){ this._private.removeEventListener(eventName, callback); } }; // Define the getter function. This will temporararly set canCreateLocalizer to true // and create a new Localizer. It writes the newly created localizer to the localizerInstance variable; var localizerInstance = null; Localizer.__defineGetter__("localizer", function(){ if(!localizerInstance){ canCreateLocalizer = true; localizerInstance = new Localizer(); canCreateLocalizer = false; } return localizerInstance; }); // EVENTS // Fired by “setLocaleChain” when the current locale is changed (synchronous) // Event object strcuture is : { localeChain /* : String [] */ } Localizer.LOCALE_CHANGE = "change"; // Fired by “getString” and “update” functions when a resource is not found in the specified bundle (fired asynchronous) // Event object structure is : { resourceName /* : String */, bundleName /* : String */ } Localizer.RESOURCE_NOT_FOUND = "resourceNotFound"; // Fired by “getFile” when a resource file is not found (fired asynchronous) // Event object structure is : { resourceFileName /* : String */ } Localizer.FILE_NOT_FOUND = "fileNotFound"; // Fired by “getString” and “update” functions when a bundle file is not found (fired asynchronous) // Event object structure is : { bundleName /* : String */ } Localizer.BUNDLE_NOT_FOUND = "bundleNotFound"; // Version number Localizer.version = version; (function(){ // ERROR function BundlePathNotFoundError(path){ this.name = 'air.Localizer.BundlePathNotFoundError'; this.message = "Bundle directory not found "+path; } BundlePathNotFoundError.prototype = new Error; Localizer.BundlePathNotFoundError = BundlePathNotFoundError; function IllegalArgumentsError(msg){ this.name = 'air.Localizer.IllegalArgumentsError'; this.message = msg; } IllegalArgumentsError.prototype = new Error; Localizer.IllegalArgumentsError = IllegalArgumentsError; }()); Localizer.LocaleId = LocaleId; Localizer.ultimateFallbackLocale = 'en'; // * This function is for internal use only. // // * Sort a languages array using the order given by the system capabilities languages array. // // * Setting removeUnsupported to true removes system unsupported languages. When removeUnsupported is false it makes the system unsupported languages be the last ones preserving order from original array. // // * Default value for removeUnsupported is true. // // *NOTE: It uses the same approach that the ADOBE AIR application Installer uses: // * it tries to find the perfect match for locale name; // * otherwise it fallbacks to finding locales with same parents (eg. “en_US” will fallback to “en” ) // * user preference will be promoted; // // * (system supported means they are in the Capabilities.languages array from the runtime) // // * eg: If Capabilities.languages = [ “fr_CA”, “en_UK”, “ja” ] and the “languages” argument // is [ “en”, “fr_FR”, “zn_ZN” ] the returning array will be [ “fr_FR”, “en” ] // // * eg2: When the “languages” argument is [ fr, fr_CA, fr_FR, ro, en, en_US ]: // * and the Capabilities.languages = [ fr_CA ] the result will be [ fr_CA, fr, fr_FR ] // * and the Capabilities.languages = [ fr_CA, en ] the result will be [ fr_CA, fr, fr_FR, en, en_US ] Localizer.sortLanguagesByPreference = function sortLanguagesByPreference( /* String[] */ appLocales, /* String[] */ systemPreferences, /* String, optional */ ultimateFallbackLocale /* = null*/, /*Boolean, optional */ keepAllLocales /* = true */) /* : String[] */{ if(!( appLocales && appLocales.hasOwnProperty('length') && systemPreferences&&systemPreferences.hasOwnProperty('length') )){ throw new Localizer.IllegalArgumentsError("Expected at least two arguments: appLocales and systemPreferences."); } return LocaleSorter.sortLocalesUsingPreferences( appLocales, systemPreferences, ultimateFallbackLocale, keepAllLocales) }; if(!window.air){ window.air = {}; } window.air.Localizer = Localizer; }());