[483] | 1 | define(["./_base/kernel", "require", "./has", "./_base/array", "./_base/config", "./_base/lang", "./has!host-browser?./_base/xhr", "./json", "module"], |
---|
| 2 | function(dojo, require, has, array, config, lang, xhr, json, module){ |
---|
| 3 | |
---|
| 4 | // module: |
---|
| 5 | // dojo/i18n |
---|
| 6 | |
---|
| 7 | has.add("dojo-preload-i18n-Api", |
---|
| 8 | // if true, define the preload localizations machinery |
---|
| 9 | 1 |
---|
| 10 | ); |
---|
| 11 | |
---|
| 12 | has.add("dojo-v1x-i18n-Api", |
---|
| 13 | // if true, define the v1.x i18n functions |
---|
| 14 | 1 |
---|
| 15 | ); |
---|
| 16 | |
---|
| 17 | var |
---|
| 18 | thisModule = dojo.i18n = |
---|
| 19 | { |
---|
| 20 | // summary: |
---|
| 21 | // This module implements the dojo/i18n! plugin and the v1.6- i18n API |
---|
| 22 | // description: |
---|
| 23 | // We choose to include our own plugin to leverage functionality already contained in dojo |
---|
| 24 | // and thereby reduce the size of the plugin compared to various loader implementations. Also, this |
---|
| 25 | // allows foreign AMD loaders to be used without their plugins. |
---|
| 26 | }, |
---|
| 27 | |
---|
| 28 | nlsRe = |
---|
| 29 | // regexp for reconstructing the master bundle name from parts of the regexp match |
---|
| 30 | // nlsRe.exec("foo/bar/baz/nls/en-ca/foo") gives: |
---|
| 31 | // ["foo/bar/baz/nls/en-ca/foo", "foo/bar/baz/nls/", "/", "/", "en-ca", "foo"] |
---|
| 32 | // nlsRe.exec("foo/bar/baz/nls/foo") gives: |
---|
| 33 | // ["foo/bar/baz/nls/foo", "foo/bar/baz/nls/", "/", "/", "foo", ""] |
---|
| 34 | // so, if match[5] is blank, it means this is the top bundle definition. |
---|
| 35 | // courtesy of http://requirejs.org |
---|
| 36 | /(^.*(^|\/)nls)(\/|$)([^\/]*)\/?([^\/]*)/, |
---|
| 37 | |
---|
| 38 | getAvailableLocales = function( |
---|
| 39 | root, |
---|
| 40 | locale, |
---|
| 41 | bundlePath, |
---|
| 42 | bundleName |
---|
| 43 | ){ |
---|
| 44 | // summary: |
---|
| 45 | // return a vector of module ids containing all available locales with respect to the target locale |
---|
| 46 | // For example, assuming: |
---|
| 47 | // |
---|
| 48 | // - the root bundle indicates specific bundles for "fr" and "fr-ca", |
---|
| 49 | // - bundlePath is "myPackage/nls" |
---|
| 50 | // - bundleName is "myBundle" |
---|
| 51 | // |
---|
| 52 | // Then a locale argument of "fr-ca" would return |
---|
| 53 | // |
---|
| 54 | // ["myPackage/nls/myBundle", "myPackage/nls/fr/myBundle", "myPackage/nls/fr-ca/myBundle"] |
---|
| 55 | // |
---|
| 56 | // Notice that bundles are returned least-specific to most-specific, starting with the root. |
---|
| 57 | // |
---|
| 58 | // If root===false indicates we're working with a pre-AMD i18n bundle that doesn't tell about the available locales; |
---|
| 59 | // therefore, assume everything is available and get 404 errors that indicate a particular localization is not available |
---|
| 60 | |
---|
| 61 | for(var result = [bundlePath + bundleName], localeParts = locale.split("-"), current = "", i = 0; i<localeParts.length; i++){ |
---|
| 62 | current += (current ? "-" : "") + localeParts[i]; |
---|
| 63 | if(!root || root[current]){ |
---|
| 64 | result.push(bundlePath + current + "/" + bundleName); |
---|
| 65 | result.specificity = current; |
---|
| 66 | } |
---|
| 67 | } |
---|
| 68 | return result; |
---|
| 69 | }, |
---|
| 70 | |
---|
| 71 | cache = {}, |
---|
| 72 | |
---|
| 73 | getBundleName = function(moduleName, bundleName, locale){ |
---|
| 74 | locale = locale ? locale.toLowerCase() : dojo.locale; |
---|
| 75 | moduleName = moduleName.replace(/\./g, "/"); |
---|
| 76 | bundleName = bundleName.replace(/\./g, "/"); |
---|
| 77 | return (/root/i.test(locale)) ? |
---|
| 78 | (moduleName + "/nls/" + bundleName) : |
---|
| 79 | (moduleName + "/nls/" + locale + "/" + bundleName); |
---|
| 80 | }, |
---|
| 81 | |
---|
| 82 | getL10nName = dojo.getL10nName = function(moduleName, bundleName, locale){ |
---|
| 83 | return moduleName = module.id + "!" + getBundleName(moduleName, bundleName, locale); |
---|
| 84 | }, |
---|
| 85 | |
---|
| 86 | doLoad = function(require, bundlePathAndName, bundlePath, bundleName, locale, load){ |
---|
| 87 | // summary: |
---|
| 88 | // get the root bundle which instructs which other bundles are required to construct the localized bundle |
---|
| 89 | require([bundlePathAndName], function(root){ |
---|
| 90 | var current = lang.clone(root.root || root.ROOT),// 1.6 built bundle defined ROOT |
---|
| 91 | availableLocales = getAvailableLocales(!root._v1x && root, locale, bundlePath, bundleName); |
---|
| 92 | require(availableLocales, function(){ |
---|
| 93 | for (var i = 1; i<availableLocales.length; i++){ |
---|
| 94 | current = lang.mixin(lang.clone(current), arguments[i]); |
---|
| 95 | } |
---|
| 96 | // target may not have been resolve (e.g., maybe only "fr" exists when "fr-ca" was requested) |
---|
| 97 | var target = bundlePathAndName + "/" + locale; |
---|
| 98 | cache[target] = current; |
---|
| 99 | current.$locale = availableLocales.specificity; |
---|
| 100 | load(); |
---|
| 101 | }); |
---|
| 102 | }); |
---|
| 103 | }, |
---|
| 104 | |
---|
| 105 | normalize = function(id, toAbsMid){ |
---|
| 106 | // summary: |
---|
| 107 | // id may be relative. |
---|
| 108 | // preload has form `*preload*<path>/nls/<module>*<flattened locales>` and |
---|
| 109 | // therefore never looks like a relative |
---|
| 110 | return /^\./.test(id) ? toAbsMid(id) : id; |
---|
| 111 | }, |
---|
| 112 | |
---|
| 113 | getLocalesToLoad = function(targetLocale){ |
---|
| 114 | var list = config.extraLocale || []; |
---|
| 115 | list = lang.isArray(list) ? list : [list]; |
---|
| 116 | list.push(targetLocale); |
---|
| 117 | return list; |
---|
| 118 | }, |
---|
| 119 | |
---|
| 120 | load = function(id, require, load){ |
---|
| 121 | // summary: |
---|
| 122 | // id is in one of the following formats |
---|
| 123 | // |
---|
| 124 | // 1. <path>/nls/<bundle> |
---|
| 125 | // => load the bundle, localized to config.locale; load all bundles localized to |
---|
| 126 | // config.extraLocale (if any); return the loaded bundle localized to config.locale. |
---|
| 127 | // |
---|
| 128 | // 2. <path>/nls/<locale>/<bundle> |
---|
| 129 | // => load then return the bundle localized to <locale> |
---|
| 130 | // |
---|
| 131 | // 3. *preload*<path>/nls/<module>*<JSON array of available locales> |
---|
| 132 | // => for config.locale and all config.extraLocale, load all bundles found |
---|
| 133 | // in the best-matching bundle rollup. A value of 1 is returned, which |
---|
| 134 | // is meaningless other than to say the plugin is executing the requested |
---|
| 135 | // preloads |
---|
| 136 | // |
---|
| 137 | // In cases 1 and 2, <path> is always normalized to an absolute module id upon entry; see |
---|
| 138 | // normalize. In case 3, it <path> is assumed to be absolute; this is arranged by the builder. |
---|
| 139 | // |
---|
| 140 | // To load a bundle means to insert the bundle into the plugin's cache and publish the bundle |
---|
| 141 | // value to the loader. Given <path>, <bundle>, and a particular <locale>, the cache key |
---|
| 142 | // |
---|
| 143 | // <path>/nls/<bundle>/<locale> |
---|
| 144 | // |
---|
| 145 | // will hold the value. Similarly, then plugin will publish this value to the loader by |
---|
| 146 | // |
---|
| 147 | // define("<path>/nls/<bundle>/<locale>", <bundle-value>); |
---|
| 148 | // |
---|
| 149 | // Given this algorithm, other machinery can provide fast load paths be preplacing |
---|
| 150 | // values in the plugin's cache, which is public. When a load is demanded the |
---|
| 151 | // cache is inspected before starting any loading. Explicitly placing values in the plugin |
---|
| 152 | // cache is an advanced/experimental feature that should not be needed; use at your own risk. |
---|
| 153 | // |
---|
| 154 | // For the normal AMD algorithm, the root bundle is loaded first, which instructs the |
---|
| 155 | // plugin what additional localized bundles are required for a particular locale. These |
---|
| 156 | // additional locales are loaded and a mix of the root and each progressively-specific |
---|
| 157 | // locale is returned. For example: |
---|
| 158 | // |
---|
| 159 | // 1. The client demands "dojo/i18n!some/path/nls/someBundle |
---|
| 160 | // |
---|
| 161 | // 2. The loader demands load(some/path/nls/someBundle) |
---|
| 162 | // |
---|
| 163 | // 3. This plugin require's "some/path/nls/someBundle", which is the root bundle. |
---|
| 164 | // |
---|
| 165 | // 4. Assuming config.locale is "ab-cd-ef" and the root bundle indicates that localizations |
---|
| 166 | // are available for "ab" and "ab-cd-ef" (note the missing "ab-cd", then the plugin |
---|
| 167 | // requires "some/path/nls/ab/someBundle" and "some/path/nls/ab-cd-ef/someBundle" |
---|
| 168 | // |
---|
| 169 | // 5. Upon receiving all required bundles, the plugin constructs the value of the bundle |
---|
| 170 | // ab-cd-ef as... |
---|
| 171 | // |
---|
| 172 | // mixin(mixin(mixin({}, require("some/path/nls/someBundle"), |
---|
| 173 | // require("some/path/nls/ab/someBundle")), |
---|
| 174 | // require("some/path/nls/ab-cd-ef/someBundle")); |
---|
| 175 | // |
---|
| 176 | // This value is inserted into the cache and published to the loader at the |
---|
| 177 | // key/module-id some/path/nls/someBundle/ab-cd-ef. |
---|
| 178 | // |
---|
| 179 | // The special preload signature (case 3) instructs the plugin to stop servicing all normal requests |
---|
| 180 | // (further preload requests will be serviced) until all ongoing preloading has completed. |
---|
| 181 | // |
---|
| 182 | // The preload signature instructs the plugin that a special rollup module is available that contains |
---|
| 183 | // one or more flattened, localized bundles. The JSON array of available locales indicates which locales |
---|
| 184 | // are available. Here is an example: |
---|
| 185 | // |
---|
| 186 | // *preload*some/path/nls/someModule*["root", "ab", "ab-cd-ef"] |
---|
| 187 | // |
---|
| 188 | // This indicates the following rollup modules are available: |
---|
| 189 | // |
---|
| 190 | // some/path/nls/someModule_ROOT |
---|
| 191 | // some/path/nls/someModule_ab |
---|
| 192 | // some/path/nls/someModule_ab-cd-ef |
---|
| 193 | // |
---|
| 194 | // Each of these modules is a normal AMD module that contains one or more flattened bundles in a hash. |
---|
| 195 | // For example, assume someModule contained the bundles some/bundle/path/someBundle and |
---|
| 196 | // some/bundle/path/someOtherBundle, then some/path/nls/someModule_ab would be expressed as follows: |
---|
| 197 | // |
---|
| 198 | // define({ |
---|
| 199 | // some/bundle/path/someBundle:<value of someBundle, flattened with respect to locale ab>, |
---|
| 200 | // some/bundle/path/someOtherBundle:<value of someOtherBundle, flattened with respect to locale ab>, |
---|
| 201 | // }); |
---|
| 202 | // |
---|
| 203 | // E.g., given this design, preloading for locale=="ab" can execute the following algorithm: |
---|
| 204 | // |
---|
| 205 | // require(["some/path/nls/someModule_ab"], function(rollup){ |
---|
| 206 | // for(var p in rollup){ |
---|
| 207 | // var id = p + "/ab", |
---|
| 208 | // cache[id] = rollup[p]; |
---|
| 209 | // define(id, rollup[p]); |
---|
| 210 | // } |
---|
| 211 | // }); |
---|
| 212 | // |
---|
| 213 | // Similarly, if "ab-cd" is requested, the algorithm can determine that "ab" is the best available and |
---|
| 214 | // load accordingly. |
---|
| 215 | // |
---|
| 216 | // The builder will write such rollups for every layer if a non-empty localeList profile property is |
---|
| 217 | // provided. Further, the builder will include the following cache entry in the cache associated with |
---|
| 218 | // any layer. |
---|
| 219 | // |
---|
| 220 | // "*now":function(r){r(['dojo/i18n!*preload*<path>/nls/<module>*<JSON array of available locales>']);} |
---|
| 221 | // |
---|
| 222 | // The *now special cache module instructs the loader to apply the provided function to context-require |
---|
| 223 | // with respect to the particular layer being defined. This causes the plugin to hold all normal service |
---|
| 224 | // requests until all preloading is complete. |
---|
| 225 | // |
---|
| 226 | // Notice that this algorithm is rarely better than the standard AMD load algorithm. Consider the normal case |
---|
| 227 | // where the target locale has a single segment and a layer depends on a single bundle: |
---|
| 228 | // |
---|
| 229 | // Without Preloads: |
---|
| 230 | // |
---|
| 231 | // 1. Layer loads root bundle. |
---|
| 232 | // 2. bundle is demanded; plugin loads single localized bundle. |
---|
| 233 | // |
---|
| 234 | // With Preloads: |
---|
| 235 | // |
---|
| 236 | // 1. Layer causes preloading of target bundle. |
---|
| 237 | // 2. bundle is demanded; service is delayed until preloading complete; bundle is returned. |
---|
| 238 | // |
---|
| 239 | // In each case a single transaction is required to load the target bundle. In cases where multiple bundles |
---|
| 240 | // are required and/or the locale has multiple segments, preloads still requires a single transaction whereas |
---|
| 241 | // the normal path requires an additional transaction for each additional bundle/locale-segment. However all |
---|
| 242 | // of these additional transactions can be done concurrently. Owing to this analysis, the entire preloading |
---|
| 243 | // algorithm can be discard during a build by setting the has feature dojo-preload-i18n-Api to false. |
---|
| 244 | |
---|
| 245 | if(has("dojo-preload-i18n-Api")){ |
---|
| 246 | var split = id.split("*"), |
---|
| 247 | preloadDemand = split[1] == "preload"; |
---|
| 248 | if(preloadDemand){ |
---|
| 249 | if(!cache[id]){ |
---|
| 250 | // use cache[id] to prevent multiple preloads of the same preload; this shouldn't happen, but |
---|
| 251 | // who knows what over-aggressive human optimizers may attempt |
---|
| 252 | cache[id] = 1; |
---|
| 253 | preloadL10n(split[2], json.parse(split[3]), 1, require); |
---|
| 254 | } |
---|
| 255 | // don't stall the loader! |
---|
| 256 | load(1); |
---|
| 257 | } |
---|
| 258 | if(preloadDemand || waitForPreloads(id, require, load)){ |
---|
| 259 | return; |
---|
| 260 | } |
---|
| 261 | } |
---|
| 262 | |
---|
| 263 | var match = nlsRe.exec(id), |
---|
| 264 | bundlePath = match[1] + "/", |
---|
| 265 | bundleName = match[5] || match[4], |
---|
| 266 | bundlePathAndName = bundlePath + bundleName, |
---|
| 267 | localeSpecified = (match[5] && match[4]), |
---|
| 268 | targetLocale = localeSpecified || dojo.locale || "", |
---|
| 269 | loadTarget = bundlePathAndName + "/" + targetLocale, |
---|
| 270 | loadList = localeSpecified ? [targetLocale] : getLocalesToLoad(targetLocale), |
---|
| 271 | remaining = loadList.length, |
---|
| 272 | finish = function(){ |
---|
| 273 | if(!--remaining){ |
---|
| 274 | load(lang.delegate(cache[loadTarget])); |
---|
| 275 | } |
---|
| 276 | }; |
---|
| 277 | array.forEach(loadList, function(locale){ |
---|
| 278 | var target = bundlePathAndName + "/" + locale; |
---|
| 279 | if(has("dojo-preload-i18n-Api")){ |
---|
| 280 | checkForLegacyModules(target); |
---|
| 281 | } |
---|
| 282 | if(!cache[target]){ |
---|
| 283 | doLoad(require, bundlePathAndName, bundlePath, bundleName, locale, finish); |
---|
| 284 | }else{ |
---|
| 285 | finish(); |
---|
| 286 | } |
---|
| 287 | }); |
---|
| 288 | }; |
---|
| 289 | |
---|
| 290 | if(has("dojo-unit-tests")){ |
---|
| 291 | var unitTests = thisModule.unitTests = []; |
---|
| 292 | } |
---|
| 293 | |
---|
| 294 | if(has("dojo-preload-i18n-Api") || has("dojo-v1x-i18n-Api")){ |
---|
| 295 | var normalizeLocale = thisModule.normalizeLocale = function(locale){ |
---|
| 296 | var result = locale ? locale.toLowerCase() : dojo.locale; |
---|
| 297 | return result == "root" ? "ROOT" : result; |
---|
| 298 | }, |
---|
| 299 | |
---|
| 300 | isXd = function(mid, contextRequire){ |
---|
| 301 | return (has("dojo-sync-loader") && has("dojo-v1x-i18n-Api")) ? |
---|
| 302 | contextRequire.isXdUrl(require.toUrl(mid + ".js")) : |
---|
| 303 | true; |
---|
| 304 | }, |
---|
| 305 | |
---|
| 306 | preloading = 0, |
---|
| 307 | |
---|
| 308 | preloadWaitQueue = [], |
---|
| 309 | |
---|
| 310 | preloadL10n = thisModule._preloadLocalizations = function(/*String*/bundlePrefix, /*Array*/localesGenerated, /*boolean?*/ guaranteedAmdFormat, /*function?*/ contextRequire){ |
---|
| 311 | // summary: |
---|
| 312 | // Load available flattened resource bundles associated with a particular module for dojo/locale and all dojo/config.extraLocale (if any) |
---|
| 313 | // description: |
---|
| 314 | // Only called by built layer files. The entire locale hierarchy is loaded. For example, |
---|
| 315 | // if locale=="ab-cd", then ROOT, "ab", and "ab-cd" are loaded. This is different than v1.6- |
---|
| 316 | // in that the v1.6- would only load ab-cd...which was *always* flattened. |
---|
| 317 | // |
---|
| 318 | // If guaranteedAmdFormat is true, then the module can be loaded with require thereby circumventing the detection algorithm |
---|
| 319 | // and the extra possible extra transaction. |
---|
| 320 | |
---|
| 321 | // If this function is called from legacy code, then guaranteedAmdFormat and contextRequire will be undefined. Since the function |
---|
| 322 | // needs a require in order to resolve module ids, fall back to the context-require associated with this dojo/i18n module, which |
---|
| 323 | // itself may have been mapped. |
---|
| 324 | contextRequire = contextRequire || require; |
---|
| 325 | |
---|
| 326 | function doRequire(mid, callback){ |
---|
| 327 | if(isXd(mid, contextRequire) || guaranteedAmdFormat){ |
---|
| 328 | contextRequire([mid], callback); |
---|
| 329 | }else{ |
---|
| 330 | syncRequire([mid], callback, contextRequire); |
---|
| 331 | } |
---|
| 332 | } |
---|
| 333 | |
---|
| 334 | function forEachLocale(locale, func){ |
---|
| 335 | // given locale= "ab-cd-ef", calls func on "ab-cd-ef", "ab-cd", "ab", "ROOT"; stops calling the first time func returns truthy |
---|
| 336 | var parts = locale.split("-"); |
---|
| 337 | while(parts.length){ |
---|
| 338 | if(func(parts.join("-"))){ |
---|
| 339 | return; |
---|
| 340 | } |
---|
| 341 | parts.pop(); |
---|
| 342 | } |
---|
| 343 | func("ROOT"); |
---|
| 344 | } |
---|
| 345 | |
---|
| 346 | function preloadingAddLock(){ |
---|
| 347 | preloading++; |
---|
| 348 | } |
---|
| 349 | |
---|
| 350 | function preloadingRelLock(){ |
---|
| 351 | --preloading; |
---|
| 352 | while(!preloading && preloadWaitQueue.length){ |
---|
| 353 | load.apply(null, preloadWaitQueue.shift()); |
---|
| 354 | } |
---|
| 355 | } |
---|
| 356 | |
---|
| 357 | function cacheId(path, name, loc, require){ |
---|
| 358 | // path is assumed to have a trailing "/" |
---|
| 359 | return require.toAbsMid(path + name + "/" + loc) |
---|
| 360 | } |
---|
| 361 | |
---|
| 362 | function preload(locale){ |
---|
| 363 | locale = normalizeLocale(locale); |
---|
| 364 | forEachLocale(locale, function(loc){ |
---|
| 365 | if(array.indexOf(localesGenerated, loc) >= 0){ |
---|
| 366 | var mid = bundlePrefix.replace(/\./g, "/") + "_" + loc; |
---|
| 367 | preloadingAddLock(); |
---|
| 368 | doRequire(mid, function(rollup){ |
---|
| 369 | for(var p in rollup){ |
---|
| 370 | var bundle = rollup[p], |
---|
| 371 | match = p.match(/(.+)\/([^\/]+)$/), |
---|
| 372 | bundleName, bundlePath; |
---|
| 373 | |
---|
| 374 | // If there is no match, the bundle is not a regular bundle from an AMD layer. |
---|
| 375 | if (!match){continue;} |
---|
| 376 | |
---|
| 377 | bundleName = match[2]; |
---|
| 378 | bundlePath = match[1] + "/"; |
---|
| 379 | |
---|
| 380 | // backcompat |
---|
| 381 | bundle._localized = bundle._localized || {}; |
---|
| 382 | |
---|
| 383 | var localized; |
---|
| 384 | if(loc === "ROOT"){ |
---|
| 385 | var root = localized = bundle._localized; |
---|
| 386 | delete bundle._localized; |
---|
| 387 | root.root = bundle; |
---|
| 388 | cache[require.toAbsMid(p)] = root; |
---|
| 389 | }else{ |
---|
| 390 | localized = bundle._localized; |
---|
| 391 | cache[cacheId(bundlePath, bundleName, loc, require)] = bundle; |
---|
| 392 | } |
---|
| 393 | |
---|
| 394 | if(loc !== locale){ |
---|
| 395 | // capture some locale variables |
---|
| 396 | function improveBundle(bundlePath, bundleName, bundle, localized){ |
---|
| 397 | // locale was not flattened and we've fallen back to a less-specific locale that was flattened |
---|
| 398 | // for example, we had a flattened 'fr', a 'fr-ca' is available for at least this bundle, and |
---|
| 399 | // locale==='fr-ca'; therefore, we must improve the bundle as retrieved from the rollup by |
---|
| 400 | // manually loading the fr-ca version of the bundle and mixing this into the already-retrieved 'fr' |
---|
| 401 | // version of the bundle. |
---|
| 402 | // |
---|
| 403 | // Remember, different bundles may have different sets of locales available. |
---|
| 404 | // |
---|
| 405 | // we are really falling back on the regular algorithm here, but--hopefully--starting with most |
---|
| 406 | // of the required bundles already on board as given by the rollup and we need to "manually" load |
---|
| 407 | // only one locale from a few bundles...or even better...we won't find anything better to load. |
---|
| 408 | // This algorithm ensures there is nothing better to load even when we can only load a less-specific rollup. |
---|
| 409 | // |
---|
| 410 | // note: this feature is only available in async mode |
---|
| 411 | |
---|
| 412 | // inspect the loaded bundle that came from the rollup to see if something better is available |
---|
| 413 | // for any bundle in a rollup, more-specific available locales are given at localized. |
---|
| 414 | var requiredBundles = [], |
---|
| 415 | cacheIds = []; |
---|
| 416 | forEachLocale(locale, function(loc){ |
---|
| 417 | if(localized[loc]){ |
---|
| 418 | requiredBundles.push(require.toAbsMid(bundlePath + loc + "/" + bundleName)); |
---|
| 419 | cacheIds.push(cacheId(bundlePath, bundleName, loc, require)); |
---|
| 420 | } |
---|
| 421 | }); |
---|
| 422 | |
---|
| 423 | if(requiredBundles.length){ |
---|
| 424 | preloadingAddLock(); |
---|
| 425 | contextRequire(requiredBundles, function(){ |
---|
| 426 | for(var i = 0; i < requiredBundles.length; i++){ |
---|
| 427 | bundle = lang.mixin(lang.clone(bundle), arguments[i]); |
---|
| 428 | cache[cacheIds[i]] = bundle; |
---|
| 429 | } |
---|
| 430 | // this is the best possible (maybe a perfect match, maybe not), accept it |
---|
| 431 | cache[cacheId(bundlePath, bundleName, locale, require)] = lang.clone(bundle); |
---|
| 432 | preloadingRelLock(); |
---|
| 433 | }); |
---|
| 434 | }else{ |
---|
| 435 | // this is the best possible (definitely not a perfect match), accept it |
---|
| 436 | cache[cacheId(bundlePath, bundleName, locale, require)] = bundle; |
---|
| 437 | } |
---|
| 438 | } |
---|
| 439 | improveBundle(bundlePath, bundleName, bundle, localized); |
---|
| 440 | } |
---|
| 441 | } |
---|
| 442 | preloadingRelLock(); |
---|
| 443 | }); |
---|
| 444 | return true; |
---|
| 445 | } |
---|
| 446 | return false; |
---|
| 447 | }); |
---|
| 448 | } |
---|
| 449 | |
---|
| 450 | preload(); |
---|
| 451 | array.forEach(dojo.config.extraLocale, preload); |
---|
| 452 | }, |
---|
| 453 | |
---|
| 454 | waitForPreloads = function(id, require, load){ |
---|
| 455 | if(preloading){ |
---|
| 456 | preloadWaitQueue.push([id, require, load]); |
---|
| 457 | } |
---|
| 458 | return preloading; |
---|
| 459 | }, |
---|
| 460 | |
---|
| 461 | checkForLegacyModules = function() |
---|
| 462 | {}; |
---|
| 463 | } |
---|
| 464 | |
---|
| 465 | if(has("dojo-v1x-i18n-Api")){ |
---|
| 466 | // this code path assumes the dojo loader and won't work with a standard AMD loader |
---|
| 467 | var amdValue = {}, |
---|
| 468 | evalBundle = |
---|
| 469 | // use the function ctor to keep the minifiers away (also come close to global scope, but this is secondary) |
---|
| 470 | new Function( |
---|
| 471 | "__bundle", // the bundle to evalutate |
---|
| 472 | "__checkForLegacyModules", // a function that checks if __bundle defined __mid in the global space |
---|
| 473 | "__mid", // the mid that __bundle is intended to define |
---|
| 474 | "__amdValue", |
---|
| 475 | |
---|
| 476 | // returns one of: |
---|
| 477 | // 1 => the bundle was an AMD bundle |
---|
| 478 | // a legacy bundle object that is the value of __mid |
---|
| 479 | // instance of Error => could not figure out how to evaluate bundle |
---|
| 480 | |
---|
| 481 | // used to detect when __bundle calls define |
---|
| 482 | "var define = function(mid, factory){define.called = 1; __amdValue.result = factory || mid;}," |
---|
| 483 | + " require = function(){define.called = 1;};" |
---|
| 484 | |
---|
| 485 | + "try{" |
---|
| 486 | + "define.called = 0;" |
---|
| 487 | + "eval(__bundle);" |
---|
| 488 | + "if(define.called==1)" |
---|
| 489 | // bundle called define; therefore signal it's an AMD bundle |
---|
| 490 | + "return __amdValue;" |
---|
| 491 | |
---|
| 492 | + "if((__checkForLegacyModules = __checkForLegacyModules(__mid)))" |
---|
| 493 | // bundle was probably a v1.6- built NLS flattened NLS bundle that defined __mid in the global space |
---|
| 494 | + "return __checkForLegacyModules;" |
---|
| 495 | |
---|
| 496 | + "}catch(e){}" |
---|
| 497 | // evaulating the bundle was *neither* an AMD *nor* a legacy flattened bundle |
---|
| 498 | // either way, re-eval *after* surrounding with parentheses |
---|
| 499 | |
---|
| 500 | + "try{" |
---|
| 501 | + "return eval('('+__bundle+')');" |
---|
| 502 | + "}catch(e){" |
---|
| 503 | + "return e;" |
---|
| 504 | + "}" |
---|
| 505 | ), |
---|
| 506 | |
---|
| 507 | syncRequire = function(deps, callback, require){ |
---|
| 508 | var results = []; |
---|
| 509 | array.forEach(deps, function(mid){ |
---|
| 510 | var url = require.toUrl(mid + ".js"); |
---|
| 511 | |
---|
| 512 | function load(text){ |
---|
| 513 | var result = evalBundle(text, checkForLegacyModules, mid, amdValue); |
---|
| 514 | if(result===amdValue){ |
---|
| 515 | // the bundle was an AMD module; re-inject it through the normal AMD path |
---|
| 516 | // we gotta do this since it could be an anonymous module and simply evaluating |
---|
| 517 | // the text here won't provide the loader with the context to know what |
---|
| 518 | // module is being defined()'d. With browser caching, this should be free; further |
---|
| 519 | // this entire code path can be circumvented by using the AMD format to begin with |
---|
| 520 | results.push(cache[url] = amdValue.result); |
---|
| 521 | }else{ |
---|
| 522 | if(result instanceof Error){ |
---|
| 523 | console.error("failed to evaluate i18n bundle; url=" + url, result); |
---|
| 524 | result = {}; |
---|
| 525 | } |
---|
| 526 | // nls/<locale>/<bundle-name> indicates not the root. |
---|
| 527 | results.push(cache[url] = (/nls\/[^\/]+\/[^\/]+$/.test(url) ? result : {root:result, _v1x:1})); |
---|
| 528 | } |
---|
| 529 | } |
---|
| 530 | |
---|
| 531 | if(cache[url]){ |
---|
| 532 | results.push(cache[url]); |
---|
| 533 | }else{ |
---|
| 534 | var bundle = require.syncLoadNls(mid); |
---|
| 535 | // don't need to check for legacy since syncLoadNls returns a module if the module |
---|
| 536 | // (1) was already loaded, or (2) was in the cache. In case 1, if syncRequire is called |
---|
| 537 | // from getLocalization --> load, then load will have called checkForLegacyModules() before |
---|
| 538 | // calling syncRequire; if syncRequire is called from preloadLocalizations, then we |
---|
| 539 | // don't care about checkForLegacyModules() because that will be done when a particular |
---|
| 540 | // bundle is actually demanded. In case 2, checkForLegacyModules() is never relevant |
---|
| 541 | // because cached modules are always v1.7+ built modules. |
---|
| 542 | if(bundle){ |
---|
| 543 | results.push(bundle); |
---|
| 544 | }else{ |
---|
| 545 | if(!xhr){ |
---|
| 546 | try{ |
---|
| 547 | require.getText(url, true, load); |
---|
| 548 | }catch(e){ |
---|
| 549 | results.push(cache[url] = {}); |
---|
| 550 | } |
---|
| 551 | }else{ |
---|
| 552 | xhr.get({ |
---|
| 553 | url:url, |
---|
| 554 | sync:true, |
---|
| 555 | load:load, |
---|
| 556 | error:function(){ |
---|
| 557 | results.push(cache[url] = {}); |
---|
| 558 | } |
---|
| 559 | }); |
---|
| 560 | } |
---|
| 561 | } |
---|
| 562 | } |
---|
| 563 | }); |
---|
| 564 | callback && callback.apply(null, results); |
---|
| 565 | }; |
---|
| 566 | |
---|
| 567 | checkForLegacyModules = function(target){ |
---|
| 568 | // legacy code may have already loaded [e.g] the raw bundle x/y/z at x.y.z; when true, push into the cache |
---|
| 569 | for(var result, names = target.split("/"), object = dojo.global[names[0]], i = 1; object && i<names.length-1; object = object[names[i++]]){} |
---|
| 570 | if(object){ |
---|
| 571 | result = object[names[i]]; |
---|
| 572 | if(!result){ |
---|
| 573 | // fallback for incorrect bundle build of 1.6 |
---|
| 574 | result = object[names[i].replace(/-/g,"_")]; |
---|
| 575 | } |
---|
| 576 | if(result){ |
---|
| 577 | cache[target] = result; |
---|
| 578 | } |
---|
| 579 | } |
---|
| 580 | return result; |
---|
| 581 | }; |
---|
| 582 | |
---|
| 583 | thisModule.getLocalization = function(moduleName, bundleName, locale){ |
---|
| 584 | var result, |
---|
| 585 | l10nName = getBundleName(moduleName, bundleName, locale); |
---|
| 586 | load( |
---|
| 587 | l10nName, |
---|
| 588 | |
---|
| 589 | // isXd() and syncRequire() need a context-require in order to resolve the mid with respect to a reference module. |
---|
| 590 | // Since this legacy function does not have the concept of a reference module, resolve with respect to this |
---|
| 591 | // dojo/i18n module, which, itself may have been mapped. |
---|
| 592 | (!isXd(l10nName, require) ? function(deps, callback){ syncRequire(deps, callback, require); } : require), |
---|
| 593 | |
---|
| 594 | function(result_){ result = result_; } |
---|
| 595 | ); |
---|
| 596 | return result; |
---|
| 597 | }; |
---|
| 598 | |
---|
| 599 | if(has("dojo-unit-tests")){ |
---|
| 600 | unitTests.push(function(doh){ |
---|
| 601 | doh.register("tests.i18n.unit", function(t){ |
---|
| 602 | var check; |
---|
| 603 | |
---|
| 604 | check = evalBundle("{prop:1}", checkForLegacyModules, "nonsense", amdValue); |
---|
| 605 | t.is({prop:1}, check); t.is(undefined, check[1]); |
---|
| 606 | |
---|
| 607 | check = evalBundle("({prop:1})", checkForLegacyModules, "nonsense", amdValue); |
---|
| 608 | t.is({prop:1}, check); t.is(undefined, check[1]); |
---|
| 609 | |
---|
| 610 | check = evalBundle("{'prop-x':1}", checkForLegacyModules, "nonsense", amdValue); |
---|
| 611 | t.is({'prop-x':1}, check); t.is(undefined, check[1]); |
---|
| 612 | |
---|
| 613 | check = evalBundle("({'prop-x':1})", checkForLegacyModules, "nonsense", amdValue); |
---|
| 614 | t.is({'prop-x':1}, check); t.is(undefined, check[1]); |
---|
| 615 | |
---|
| 616 | check = evalBundle("define({'prop-x':1})", checkForLegacyModules, "nonsense", amdValue); |
---|
| 617 | t.is(amdValue, check); t.is({'prop-x':1}, amdValue.result); |
---|
| 618 | |
---|
| 619 | check = evalBundle("define('some/module', {'prop-x':1})", checkForLegacyModules, "nonsense", amdValue); |
---|
| 620 | t.is(amdValue, check); t.is({'prop-x':1}, amdValue.result); |
---|
| 621 | |
---|
| 622 | check = evalBundle("this is total nonsense and should throw an error", checkForLegacyModules, "nonsense", amdValue); |
---|
| 623 | t.is(check instanceof Error, true); |
---|
| 624 | }); |
---|
| 625 | }); |
---|
| 626 | } |
---|
| 627 | } |
---|
| 628 | |
---|
| 629 | return lang.mixin(thisModule, { |
---|
| 630 | dynamic:true, |
---|
| 631 | normalize:normalize, |
---|
| 632 | load:load, |
---|
| 633 | cache:cache, |
---|
| 634 | getL10nName: getL10nName |
---|
| 635 | }); |
---|
| 636 | }); |
---|