source: Dev/trunk/src/client/util/buildscripts/jslib/i18nUtil.js @ 483

Last change on this file since 483 was 483, checked in by hendrikvanantwerpen, 11 years ago

Added Dojo 1.9.3 release.

File size: 12.5 KB
Line 
1i18nUtil = {};
2
3i18nUtil.setup = function(/*Object*/kwArgs){
4        // summary:
5        //              loads dojo so we can use it for i18n bundle flattening.
6       
7        //Do the setup only if it has not already been done before.
8        if(typeof djConfig == "undefined" || !(typeof dojo != "undefined" && dojo["i18n"])){
9                djConfig={
10                        locale: 'xx',
11                        extraLocale: kwArgs.localeList,
12                        baseUrl: buildScriptsPath + "../../dojo/"
13                };
14
15                load(buildScriptsPath + '../../dojo/dojo.js');
16
17                //Now set baseUrl so it is current directory, since all the prefixes
18                //will be relative to the release dir from this directory.
19                dojo.baseUrl = "./";
20
21                //Also be sure we register the right paths for module prefixes.
22                buildUtil.configPrefixes(kwArgs.profileProperties.dependencies.prefixes);
23
24                dojo.require("dojo.i18n");
25        }
26}
27
28i18nUtil.flattenLayerFileBundles = function(/*String*/fileName, /*String*/fileContents, /*Object*/kwArgs){
29        // summary:
30        //              This little utility is invoked by the build to flatten all of the JSON resource bundles used
31        //              by dojo.requireLocalization(), much like the main build itself, to optimize so that multiple
32        //              web hits will not be necessary to load these resources.  Normally, a request for a particular
33        //              bundle in a locale like "en-us" would result in three web hits: one looking for en_us/ another
34        //              for en/ and another for ROOT/.  All of this multiplied by the number of bundles used can result
35        //              in a lot of web hits and latency.  This script uses Dojo to actually load the resources into
36        //              memory, then flatten the object and spit it out using dojo.toJson.  The bootstrap
37        //              will be modified to download exactly one of these files, whichever is closest to the user's
38        //              locale.
39        // fileName:
40        //              The name of the file to process (like dojo.js). This function will use
41        //              it to determine the best resource name to give the flattened bundle.
42        // fileContents:
43        //              The contents of the file to process (like dojo.js). This function will look in
44        //              the contents for dojo.requireLocation() calls.
45        // kwArgs:
46        //              The build's kwArgs.
47       
48        var destDirName = fileName.replace(/\/[^\/]+$/, "/") + "nls";
49        var nlsNamePrefix = fileName.replace(/\.js$/, "");
50        nlsNamePrefix = nlsNamePrefix.substring(nlsNamePrefix.lastIndexOf("/") + 1, nlsNamePrefix.length);
51
52        i18nUtil.setup(kwArgs);
53        var djLoadedBundles = [];
54       
55        //TODO: register plain function handler (output source) in jsonRegistry?
56        var drl = dojo.requireLocalization;
57        var dupes = {};
58        dojo.requireLocalization = function(modulename, bundlename, locale){
59                var dupName = [modulename, bundlename, locale].join(":");
60                if(!dupes[dupName]){
61                        drl(modulename, bundlename, locale);
62                        djLoadedBundles.push({modulename: modulename, module: eval(modulename), bundlename: bundlename});
63                        dupes[dupName] = 1;
64                }
65        };
66       
67        var requireStatements = fileContents.match(/dojo\.requireLocalization\(.*\)\;/g);
68        if(requireStatements){
69                eval(requireStatements.join(";"));
70
71                //print("loaded bundles: "+djLoadedBundles.length);
72               
73                var djBundlesByLocale = {};
74                var jsLocale, entry, bundle;
75               
76                for (var i = 0; i < djLoadedBundles.length; i++){
77                        entry = djLoadedBundles[i];
78                        bundle = entry.module.nls[entry.bundlename];
79                        for (jsLocale in bundle){
80                                if (!djBundlesByLocale[jsLocale]){djBundlesByLocale[jsLocale]=[];}
81                                djBundlesByLocale[jsLocale].push(entry);
82                        }
83                }
84               
85                localeList = [];
86               
87                //Save flattened bundles used by dojo.js.
88                var mkdir = false;
89                var dir = new java.io.File(destDirName);
90                var modulePrefix = buildUtil.mapPathToResourceName(fileName, kwArgs.profileProperties.dependencies.prefixes);
91
92                //Adjust modulePrefix to include the nls part before the last segment.
93                var lastDot = modulePrefix.lastIndexOf(".");
94                if(lastDot != -1){
95                        modulePrefix = modulePrefix.substring(0, lastDot + 1) + "nls." + modulePrefix.substring(lastDot + 1, modulePrefix.length);
96                }else{
97                        throw "Invalid module prefix for flattened bundle: " + modulePrefix;
98                }
99
100                for (jsLocale in djBundlesByLocale){
101                        var locale = jsLocale.replace(/\_/g, '-');
102                        if(!mkdir){ dir.mkdir(); mkdir = true; }
103                       
104                        var outFile = new java.io.File(dir, nlsNamePrefix + "_" + locale + ".js");
105                        //Make sure we can create the final file.
106                        var parentDir = outFile.getParentFile();
107                        if(!parentDir.exists()){
108                                if(!parentDir.mkdirs()){
109                                        throw "Could not create directory: " + parentDir.getAbsolutePath();
110                                }
111                        }
112
113                        var os = new java.io.BufferedWriter(
114                                        new java.io.OutputStreamWriter(new java.io.FileOutputStream(outFile), "utf-8"));
115                        try{
116                                os.write("dojo.provide(\""+modulePrefix+"_"+locale+"\");");
117                                for (var j = 0; j < djLoadedBundles.length; j++){
118                                        entry = djLoadedBundles[j];
119                                        var bundlePkg = [entry.modulename,"nls",entry.bundlename].join(".");
120                                        var translationPkg = [bundlePkg,jsLocale].join(".");
121                                        bundle = entry.module.nls[entry.bundlename];
122                                        if(bundle[jsLocale]){ //FIXME:redundant check?
123                                                os.write("dojo.provide(\""+bundlePkg+"\");");
124                                                os.write(bundlePkg+"._built=true;");
125                                                os.write("dojo.provide(\""+translationPkg+"\");");
126                                                os.write(translationPkg+"="+dojo.toJson(bundle[jsLocale])+";");
127                                        }
128                                }
129                        }finally{
130                                os.close();
131                        }
132                        localeList.push(locale);
133                }
134               
135                //Remove dojo.requireLocalization calls from the file.
136                fileContents = fileContents.replace(/dojo\.requireLocalization\(.*\)\;/g, "");
137
138
139                var preloadCall = '\ndojo.i18n._preloadLocalizations("' + modulePrefix + '", ' + dojo.toJson(localeList.sort()) + ');\n';
140                //Inject the dojo._preloadLocalizations call into the file.
141                //Do this at the end of the file, since we need to make sure dojo.i18n has been loaded.
142                //The assumption is that if dojo.i18n is not in this layer file, dojo.i18n is
143                //in one of the layer files this layer file depends on.
144                //Allow call to be inserted in the dojo.js closure, if that is in play.
145                i18nUtil.preloadInsertionRegExp.lastIndex = 0;
146                if(fileContents.match(i18nUtil.preloadInsertionRegExp)){
147                        i18nUtil.preloadInsertionRegExp.lastIndex = 0;
148                        fileContents = fileContents.replace(i18nUtil.preloadInsertionRegExp, preloadCall);
149                }else{
150                        fileContents += preloadCall;
151                }
152        }
153
154        return fileContents; //String
155}
156
157i18nUtil.preloadInsertionRegExp = /\/\/INSERT dojo.i18n._preloadLocalizations HERE/;
158
159i18nUtil.flattenDirBundles = function(/*String*/prefixName, /*String*/prefixDir, /*Object*/kwArgs, /*RegExp*/nlsIgnoreRegExp){
160        // summary:
161        //              Flattens the i18n bundles inside a directory so that only request
162        //              is needed per bundle. Does not handle resource flattening for dojo.js or
163        //              layered build files.
164
165        i18nUtil.setup(kwArgs);
166        var fileList = fileUtil.getFilteredFileList(prefixDir, /\.js$/, true);
167        var prefixes = kwArgs.profileProperties.dependencies.prefixes;
168        for(var i= 0; i < fileList.length; i++){
169                //Use new String so we get a JS string and not a Java string.
170                var jsFileName = String(fileList[i]);
171                var fileContents = null;
172               
173                //Files in nls directories, except for layer bundles that already have been processed.
174                if(jsFileName.match(/\/nls\//) && !jsFileName.match(nlsIgnoreRegExp)){
175                        fileContents = "(" + i18nUtil.makeFlatBundleContents(prefixName, prefixDir, jsFileName) + ")";
176                }else{
177                        fileContents = i18nUtil.modifyRequireLocalization(readText(jsFileName), prefixes);
178                }
179
180                if(fileContents){
181                        fileUtil.saveUtf8File(jsFileName, fileContents);
182                }
183        }
184}
185
186i18nUtil.modifyRequireLocalization = function(/*String*/fileContents, /*Array*/prefixes){
187        // summary:
188        //              Modifies any dojo.requireLocalization calls in the fileContents to have the
189        //              list of supported locales as part of the call. This allows the i18n loading functions
190        //              to only make request(s) for locales that actually exist on disk.
191        var dependencies = [];
192       
193        //Make sure we have a JS string, and not a Java string.
194        fileContents = String(fileContents);
195       
196        var modifiedContents = fileContents;
197       
198        if(fileContents.match(buildUtil.globalRequireLocalizationRegExp)){
199                modifiedContents = fileContents.replace(buildUtil.globalRequireLocalizationRegExp, function(matchString){
200                        var replacement = matchString;
201                        var partMatches = matchString.match(buildUtil.requireLocalizationRegExp);
202                        var depCall = partMatches[1];
203                        var depArgs = partMatches[2];
204       
205                        if(depCall == "requireLocalization"){
206                                //Need to find out what locales are available so the dojo loader
207                                //only has to do one script request for the closest matching locale.
208                                var reqArgs = i18nUtil.getRequireLocalizationArgsFromString(depArgs);
209                                if(reqArgs.moduleName){
210                                        //Find the list of locales supported by looking at the path names.
211                                        var locales = i18nUtil.getLocalesForBundle(reqArgs.moduleName, reqArgs.bundleName, prefixes);
212       
213                                        //Add the supported locales to the requireLocalization arguments.
214                                        if(!reqArgs.localeName){
215                                                depArgs += ", null";
216                                        }
217       
218                                        depArgs += ', "' + locales.join(",") + '"';
219                                       
220                                        replacement = "dojo." + depCall + "(" + depArgs + ")";
221                                }
222                        }
223                        return replacement;
224                });
225        }
226        return modifiedContents;
227}
228
229i18nUtil.makeFlatBundleContents = function(prefix, prefixPath, srcFileName){
230        // summary:
231        //              Given a nls file name, flatten the bundles from parent locales into the nls bundle.
232        var bundleParts = i18nUtil.getBundlePartsFromFileName(prefix, prefixPath, srcFileName);
233        if(!bundleParts){
234                return null;
235        }
236        var moduleName = bundleParts.moduleName;
237        var bundleName = bundleParts.bundleName;
238        var localeName = bundleParts.localeName;
239
240        dojo.requireLocalization(moduleName, bundleName, localeName);
241       
242        //Get the generated, flattened bundle.
243        var module = dojo.getObject(moduleName);
244        var bundleLocale = localeName ? localeName.replace(/-/g, "_") : "ROOT";
245        var flattenedBundle = module.nls[bundleName][bundleLocale];
246       
247        if(!flattenedBundle){
248                throw "Cannot create flattened bundle for src file: " + srcFileName;
249        }
250
251        return dojo.toJson(flattenedBundle);
252}
253
254//Given a module and bundle name, find all the supported locales.
255i18nUtil.getLocalesForBundle = function(moduleName, bundleName, prefixes){
256        //Build a path to the bundle directory and ask for all files that match
257        //the bundle name.
258        var filePath = buildUtil.mapResourceToPath(moduleName, prefixes);
259       
260        var bundleRegExp = new RegExp("nls[/]?([\\w\\-]*)/" + bundleName + ".js$");
261        var bundleFiles = fileUtil.getFilteredFileList(filePath + "nls/", bundleRegExp, true);
262       
263        //Find the list of locales supported by looking at the path names.
264        var locales = [];
265        for(var j = 0; j < bundleFiles.length; j++){
266                var bundleParts = bundleFiles[j].match(bundleRegExp);
267                if(bundleParts && bundleParts[1]){
268                        locales.push(bundleParts[1]);
269                }else{
270                        locales.push("ROOT");
271                }
272        }
273
274        return locales.sort();
275}
276
277i18nUtil.getRequireLocalizationArgsFromString = function(argString){
278        // summary:
279        //              Given a string of the arguments to a dojo.requireLocalization
280        //              call, separate the string into individual arguments.
281        var argResult = {
282                moduleName: null,
283                bundleName: null,
284                localeName: null
285        };
286       
287        var l10nMatches = argString.split(/\,\s*/);
288        if(l10nMatches && l10nMatches.length > 1){
289                argResult.moduleName = l10nMatches[0] ? l10nMatches[0].replace(/\"/g, "") : null;
290                argResult.bundleName = l10nMatches[1] ? l10nMatches[1].replace(/\"/g, "") : null;
291                argResult.localeName = l10nMatches[2];
292        }
293        return argResult;
294}
295
296i18nUtil.getBundlePartsFromFileName = function(prefix, prefixPath, srcFileName){
297        //Pull off any ../ values from prefix path to make matching easier.
298        var prefixPath = prefixPath.replace(/\.\.\//g, "");
299
300        //Strip off the prefix path so we can find the real resource and bundle names.
301        var prefixStartIndex = srcFileName.lastIndexOf(prefixPath);
302        if(prefixStartIndex != -1){
303                var startIndex = prefixStartIndex + prefixPath.length;
304               
305                //Need to add one if the prefiPath does not include an ending /. Otherwise,
306                //We'll get extra dots in our bundleName.
307                if(prefixPath.charAt(prefixPath.length) != "/"){
308                        startIndex += 1;
309                }
310                srcFileName = srcFileName.substring(startIndex, srcFileName.length);
311        }
312       
313        //var srcIndex = srcFileName.indexOf("src/");
314        //srcFileName = srcFileName.substring(srcIndex + 4, srcFileName.length);
315        var parts = srcFileName.split("/");
316
317        //Split up the srcFileName into arguments that can be used for dojo.requireLocalization()
318        var moduleParts = [prefix];
319        for(var i = 0; parts[i] != "nls"; i++){
320                moduleParts.push(parts[i]);
321        }
322        var moduleName = moduleParts.join(".");
323        if(parts[i+1].match(/\.js$/)){
324                var localeName = "";
325                var bundleName = parts[i+1];
326        }else{
327                var localeName = parts[i+1];
328                var bundleName = parts[i+2];
329        }
330
331        if(!bundleName || bundleName.indexOf(".js") == -1){
332                //Not a valid bundle. Could be something like a README file.
333                return null;
334        }else{
335                bundleName = bundleName.replace(/\.js/, "");
336        }
337
338        return {moduleName: moduleName, bundleName: bundleName, localeName: localeName};
339}
Note: See TracBrowser for help on using the repository browser.