UNPKG

7.13 kBJavaScriptView Raw
1/**
2 * @license RequireJS i18n 1.0.0 Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved.
3 * Available via the MIT or new BSD license.
4 * see: http://github.com/jrburke/requirejs for details
5 */
6/*jslint regexp: false, nomen: false, plusplus: false, strict: false */
7/*global require: false, navigator: false, define: false */
8
9/**
10 * This plugin handles i18n! prefixed modules. It does the following:
11 *
12 * 1) A regular module can have a dependency on an i18n bundle, but the regular
13 * module does not want to specify what locale to load. So it just specifies
14 * the top-level bundle, like "i18n!nls/colors".
15 *
16 * This plugin will load the i18n bundle at nls/colors, see that it is a root/master
17 * bundle since it does not have a locale in its name. It will then try to find
18 * the best match locale available in that master bundle, then request all the
19 * locale pieces for that best match locale. For instance, if the locale is "en-us",
20 * then the plugin will ask for the "en-us", "en" and "root" bundles to be loaded
21 * (but only if they are specified on the master bundle).
22 *
23 * Once all the bundles for the locale pieces load, then it mixes in all those
24 * locale pieces into each other, then finally sets the context.defined value
25 * for the nls/colors bundle to be that mixed in locale.
26 *
27 * 2) A regular module specifies a specific locale to load. For instance,
28 * i18n!nls/fr-fr/colors. In this case, the plugin needs to load the master bundle
29 * first, at nls/colors, then figure out what the best match locale is for fr-fr,
30 * since maybe only fr or just root is defined for that locale. Once that best
31 * fit is found, all of its locale pieces need to have their bundles loaded.
32 *
33 * Once all the bundles for the locale pieces load, then it mixes in all those
34 * locale pieces into each other, then finally sets the context.defined value
35 * for the nls/fr-fr/colors bundle to be that mixed in locale.
36 */
37(function () {
38 //regexp for reconstructing the master bundle name from parts of the regexp match
39 //nlsRegExp.exec("foo/bar/baz/nls/en-ca/foo") gives:
40 //["foo/bar/baz/nls/en-ca/foo", "foo/bar/baz/nls/", "/", "/", "en-ca", "foo"]
41 //nlsRegExp.exec("foo/bar/baz/nls/foo") gives:
42 //["foo/bar/baz/nls/foo", "foo/bar/baz/nls/", "/", "/", "foo", ""]
43 //so, if match[5] is blank, it means this is the top bundle definition.
44 var nlsRegExp = /(^.*(^|\/)nls(\/|$))([^\/]*)\/?([^\/]*)/,
45 empty = {};
46
47 //Helper function to avoid repeating code. Lots of arguments in the
48 //desire to stay functional and support RequireJS contexts without having
49 //to know about the RequireJS contexts.
50 function addPart(locale, master, needed, toLoad, prefix, suffix) {
51 if (master[locale]) {
52 needed.push(locale);
53 if (master[locale] === true || master[locale] === 1) {
54 toLoad.push(prefix + locale + '/' + suffix);
55 }
56 }
57 }
58
59 function addIfExists(req, locale, toLoad, prefix, suffix) {
60 var fullName = prefix + locale + '/' + suffix;
61 if (require._fileExists(req.toUrl(fullName))) {
62 toLoad.push(fullName);
63 }
64 }
65
66 /**
67 * Simple function to mix in properties from source into target,
68 * but only if target does not already have a property of the same name.
69 * This is not robust in IE for transferring methods that match
70 * Object.prototype names, but the uses of mixin here seem unlikely to
71 * trigger a problem related to that.
72 */
73 function mixin(target, source, force) {
74 for (var prop in source) {
75 if (!(prop in empty) && (!(prop in target) || force)) {
76 target[prop] = source[prop];
77 }
78 }
79 }
80
81 define({
82 version: '1.0.0',
83 /**
84 * Called when a dependency needs to be loaded.
85 */
86 load: function (name, req, onLoad, config) {
87 config = config || {};
88
89 var masterName,
90 match = nlsRegExp.exec(name),
91 prefix = match[1],
92 locale = match[4],
93 suffix = match[5],
94 parts = locale.split("-"),
95 toLoad = [],
96 value = {},
97 i, part, current = "";
98
99 //If match[5] is blank, it means this is the top bundle definition,
100 //so it does not have to be handled. Locale-specific requests
101 //will have a match[4] value but no match[5]
102 if (match[5]) {
103 //locale-specific bundle
104 prefix = match[1];
105 masterName = prefix + suffix;
106 } else {
107 //Top-level bundle.
108 masterName = name;
109 suffix = match[4];
110 locale = config.locale || (config.locale =
111 typeof navigator === "undefined" ? "root" :
112 (navigator.language ||
113 navigator.userLanguage || "root").toLowerCase());
114 parts = locale.split("-");
115 }
116
117 if (config.isBuild) {
118 //Check for existence of all locale possible files and
119 //require them if exist.
120 toLoad.push(masterName);
121 addIfExists(req, "root", toLoad, prefix, suffix);
122 for (i = 0; (part = parts[i]); i++) {
123 current += (current ? "-" : "") + part;
124 addIfExists(req, current, toLoad, prefix, suffix);
125 }
126
127 req(toLoad, function () {
128 onLoad();
129 });
130 } else {
131 //First, fetch the master bundle, it knows what locales are available.
132 req([masterName], function (master) {
133 //Figure out the best fit
134 var needed = [];
135
136 //Always allow for root, then do the rest of the locale parts.
137 addPart("root", master, needed, toLoad, prefix, suffix);
138 for (i = 0; (part = parts[i]); i++) {
139 current += (current ? "-" : "") + part;
140 addPart(current, master, needed, toLoad, prefix, suffix);
141 }
142
143 //Load all the parts missing.
144 req(toLoad, function () {
145 var i, partBundle;
146 for (i = needed.length - 1; i > -1 && (part = needed[i]); i--) {
147 partBundle = master[part];
148 if (partBundle === true || partBundle === 1) {
149 partBundle = req(prefix + part + '/' + suffix);
150 }
151 mixin(value, partBundle);
152 }
153
154 //All done, notify the loader.
155 onLoad(value);
156 });
157 });
158 }
159 }
160 });
161}());