Skip to content

Commit f3b9bb3

Browse files
committed
i18n plugin now working with nls layer + tests
1 parent 095cafb commit f3b9bb3

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+5744
-234
lines changed

Gruntfile.js

Lines changed: 43 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,35 +2,48 @@
22

33
module.exports = function (grunt) {
44

5-
// Project configuration.
6-
grunt.initConfig({
7-
jshint: {
8-
all: [
9-
"Gruntfile.js",
10-
"plugins/**/*.js"
11-
],
12-
options: {
13-
jshintrc: ".jshintrc",
14-
},
15-
},
16-
17-
jsbeautifier: {
18-
files: ["Gruntfile.js", "plugins/**/*.js"],
19-
options: {
20-
config: ".jshintrc",
21-
js: {
22-
jslintHappy: true,
23-
}
24-
}
25-
}
26-
});
27-
28-
// These plugins provide necessary tasks.
29-
grunt.loadNpmTasks("grunt-contrib-jshint");
30-
grunt.loadNpmTasks("grunt-jsbeautifier");
31-
32-
33-
// By default, lint and run all tests.
34-
grunt.registerTask("default", ["jsbeautifier", "jshint"]);
5+
// Project configuration.
6+
grunt.initConfig({
7+
jshint: {
8+
all: [
9+
"Gruntfile.js",
10+
"*.js",
11+
"i18n/*.js"
12+
],
13+
options: {
14+
jshintrc: ".jshintrc",
15+
},
16+
},
17+
18+
jsbeautifier: {
19+
files: ["Gruntfile.js", "*.js", "i18n/*.js"],
20+
options: {
21+
config: ".jshintrc",
22+
js: {
23+
jslintHappy: true,
24+
indentWithTabs: true
25+
}
26+
}
27+
},
28+
29+
intern: {
30+
all: {
31+
options: {
32+
runType: 'runner', // defaults to 'client'
33+
config: 'tests/locale.js',
34+
reporters: ['console', 'lcov']
35+
}
36+
}
37+
}
38+
});
39+
40+
// These plugins provide necessary tasks.
41+
grunt.loadNpmTasks("grunt-contrib-jshint");
42+
grunt.loadNpmTasks("grunt-jsbeautifier");
43+
grunt.loadNpmTasks('intern');
44+
45+
46+
// By default, lint and run all tests.
47+
grunt.registerTask("default", ["jsbeautifier", "jshint", "intern"]);
3548

3649
};

i18n.js

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
/**
2+
* Plugin based on requirejs i18n
3+
* see: http://github.com/requirejs/i18n for details
4+
*/
5+
define(['./i18n/common', "module"], function (common, module) {
6+
7+
// regexp for reconstructing the master bundle name from parts of the regexp match
8+
// nlsRegExp.exec("foo/bar/baz/nls/en-ca/foo") gives:
9+
// ["foo/bar/baz/nls/en-ca/foo", "foo/bar/baz/nls/", "/", "/", "en-ca", "foo"]
10+
// nlsRegExp.exec("foo/bar/baz/nls/foo") gives:
11+
// ["foo/bar/baz/nls/foo", "foo/bar/baz/nls/", "/", "/", "foo", ""]
12+
// so, if match[5] is blank, it means this is the top bundle definition.
13+
var nlsRegExp = /(^.*(^|\/)nls(\/|$))([^\/]*)\/?([^\/]*)/,
14+
15+
16+
// Simple function to mix in properties from source into target,
17+
// but only if target does not already have a property of the same name.
18+
// This is not robust in IE for transferring methods that match
19+
// Object.prototype names, but the uses of mixin here seem unlikely to
20+
// trigger a problem related to that.
21+
mixin = function (target, source, force) {
22+
var prop;
23+
for (prop in source) {
24+
if (source.hasOwnProperty(prop) && (!target.hasOwnProperty(prop) || force)) {
25+
target[prop] = source[prop];
26+
} else if (typeof source[prop] === 'object') {
27+
if (!target[prop] && source[prop]) {
28+
target[prop] = {};
29+
}
30+
mixin(target[prop], source[prop], force);
31+
}
32+
}
33+
},
34+
35+
// Use nlsRegExp to parse the resource mid and return a usable object.
36+
parseName = function (name) {
37+
var match = nlsRegExp.exec(name);
38+
39+
// If match[5] is blank, it means this is the top bundle definition,
40+
// hence suffix is match[4] and locale is null.
41+
return {
42+
prefix: match[1],
43+
masterLocale: "root",
44+
requestedLocale: match[5] ? match[4] : null,
45+
suffix: match[5] || match[4]
46+
};
47+
},
48+
49+
getMasterMid = function (name) {
50+
return name.masterLocale === "root" ? name.prefix + name.suffix :
51+
name.prefix + name.masterLocale + "/" + name.suffix;
52+
},
53+
54+
55+
// Transform a bundle from a language layer to a root bundle.
56+
rootify = function (bundle, locale) {
57+
var result = {};
58+
if (bundle._pseudoRoot) {
59+
result[locale] = {};
60+
mixin(result, bundle._pseudoRoot);
61+
delete bundle._pseudoRoot;
62+
mixin(result[locale], bundle);
63+
bundle = result;
64+
}
65+
return bundle;
66+
},
67+
68+
// Construct the best language bundle by merging from most specific locale to less specific locale.
69+
resolveAMD = function (name, req, onLoad) {
70+
var masterMid = getMasterMid(name);
71+
72+
//First, fetch the master bundle, it knows what locales are available.
73+
req([masterMid], function (master) {
74+
var getBundleAndMixin = function (prefix, suffix, locale, value) {
75+
var mixBundle = function (bundle) {
76+
mixin(value, bundle);
77+
locale = common.getParentLocale(locale);
78+
if (!bundle._flattened && locale) {
79+
getBundleAndMixin(prefix, suffix, locale, value);
80+
} else {
81+
value._flattened = true;
82+
onLoad(value);
83+
}
84+
};
85+
86+
if (master[locale] === true || master[locale] === 1) {
87+
req([prefix + locale + '/' + suffix], mixBundle);
88+
} else {
89+
// locale is on the master bundle or locale is unexisting
90+
mixBundle(master[locale] || {});
91+
}
92+
};
93+
94+
master = rootify(master, name.masterLocale);
95+
getBundleAndMixin(name.prefix, name.suffix, name.requestedLocale, {});
96+
});
97+
},
98+
99+
getLayer = function (name, layer, moduleConfig, getParentLocale, req, onLoad) {
100+
var locale = name.requestedLocale,
101+
localesMap = moduleConfig.localesMap[layer];
102+
103+
while (locale && !localesMap[locale]) {
104+
locale = getParentLocale(locale);
105+
}
106+
107+
if (locale) {
108+
name.masterLocale = locale;
109+
110+
req([layer + "_" + locale], function () {
111+
pickFromLayer(name, moduleConfig, req, onLoad);
112+
});
113+
} else {
114+
console.log("i18n: no relevant layer " + layer + " found for locale " + name.requestedLocale + ".");
115+
onLoad();
116+
}
117+
},
118+
119+
120+
tryLayer = function (name, layer, moduleConfig, getParentLocale, req, onLoad) {
121+
var helper = function (locale) {
122+
if (locale) {
123+
req(["maybe!" + layer + "_" + locale], function (bundle) {
124+
if (bundle) {
125+
name.masterLocale = locale;
126+
pickFromLayer(name, moduleConfig, req, onLoad);
127+
} else {
128+
helper(getParentLocale(locale));
129+
}
130+
});
131+
} else {
132+
console.log("i18n: no relevant layer " + layer + " found for locale " + name.requestedLocale + ".");
133+
onLoad();
134+
}
135+
};
136+
137+
helper(name.requestedLocale);
138+
},
139+
140+
pickFromLayer = function (name, moduleConfig, req, onLoad) {
141+
var masterMid = getMasterMid(name);
142+
143+
if (name.requestedLocale === name.masterLocale || moduleConfig.layerOnly || !moduleConfig.enhanceLayer) {
144+
req([masterMid], function (bundle) {
145+
if (bundle.root) {
146+
bundle = bundle.root;
147+
}
148+
onLoad(bundle);
149+
});
150+
} else {
151+
resolveAMD(name, req, onLoad);
152+
}
153+
};
154+
155+
return {
156+
load: function (name, req, onLoad, config) {
157+
config = config || {};
158+
159+
var moduleConfig = module.config(),
160+
masterMid,
161+
layer;
162+
163+
// Parse name and set the locale if a top level bundle is required
164+
name = parseName(name);
165+
name.requestedLocale = name.requestedLocale || common.getLocale(config);
166+
masterMid = getMasterMid(name);
167+
168+
// If there is no layer, classic AMD mode
169+
if (!moduleConfig.bundlesMap) {
170+
resolveAMD(name, req, onLoad);
171+
return;
172+
}
173+
174+
// From now on there is at least one layer available
175+
176+
// Check if requested module is in a layer
177+
layer = moduleConfig.bundlesMap[masterMid];
178+
if (!layer && moduleConfig.layerOnly) {
179+
console.log("i18n: module " + masterMid + " not found in layer.");
180+
onLoad();
181+
return;
182+
} else if (!layer) {
183+
resolveAMD(name, req, onLoad);
184+
return;
185+
}
186+
187+
// The module is in a layer
188+
189+
if (moduleConfig.languagePack) {
190+
// Drop language pack mode, hence need to try every possible layer
191+
tryLayer(name, layer, moduleConfig, common.getParentLocale, req, onLoad);
192+
return;
193+
} else {
194+
// There is a locale list for the layer
195+
getLayer(name, layer, moduleConfig, common.getParentLocale, req, onLoad);
196+
return;
197+
}
198+
}
199+
};
200+
});

i18n/common.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
define(["../maybe!./parentLocale"], function (parentLocale) {
2+
parentLocale = parentLocale || {};
3+
4+
return {
5+
getLocale: function (config) {
6+
var locale = config.locale;
7+
if (!locale) {
8+
locale = typeof navigator === "undefined" ? "root" :
9+
(navigator.language ||
10+
navigator.userLanguage || "root");
11+
}
12+
// just to be extra-sure
13+
return locale.toLowerCase();
14+
},
15+
16+
getParentLocale: function (loc) {
17+
if (!loc || loc === "root") {
18+
return undefined;
19+
}
20+
if (parentLocale[loc]) {
21+
return parentLocale[loc];
22+
}
23+
24+
var parts = loc.split("-");
25+
parts.pop();
26+
return (parts.length > 0) ? parts.join("-") : "root";
27+
}
28+
};
29+
});

0 commit comments

Comments
 (0)