source: Dev/branches/rest-dojo-ui/client/util/buildscripts/jslib/i18nUtil.js @ 275

Last change on this file since 275 was 256, checked in by hendrikvanantwerpen, 13 years ago

Reworked project structure based on REST interaction and Dojo library. As
soon as this is stable, the old jQueryUI branch can be removed (it's
kept for reference).

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