UNPKG

6.66 kBJavaScriptView Raw
1var fs = require("fs");
2var path = require("path");
3
4/**
5 * Convert a string to a regexp string
6 */
7function regExpText(text) {
8 return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
9}
10
11/**
12 * Find files for locals.
13 *
14 * @param folder the path to the folder
15 * @param filePostfix the filename of the root locale
16 * @param readdir fs.readDir or a equivalent function
17 * @param stat fs.stat or a equivalent function
18 */
19function findFilesCommon(folder, filePostfix, callback, readdir, stat) {
20 // build a RegExp from the filePostfix
21 var regExp = new RegExp("^(?:(.+)\\.)"+regExpText(filePostfix)+"$")
22 // get files in directory
23 readdir(folder, function(err, files) {
24 if(err) return callback(err);
25
26 // check each file async if it is a file and matches the RegExp
27 // then call the callback with a array of found files.
28 var result = [];
29 var errors = [];
30 var count = files.length;
31 files.forEach(function(file) {
32 stat(path.join(folder, file), function(err, stat) {
33 if(err || !stat) return endOne(err);
34 if(stat.isDirectory()) {
35 endOne();
36 } else {
37 if(regExp.test(file)) {
38 var match = regExp.exec(file);
39 var name = match[1];
40 result.push(name);
41 }
42 endOne();
43 }
44 });
45 });
46 function endOne(err) {
47 if(err) errors.push(err);
48 count--;
49 if(count == 0) {
50 if(errors.length > 0)
51 callback(new Error("cannot find files: " + errors.join("\n")));
52 else
53 callback(null, result);
54 }
55 }
56 });
57}
58
59// async version
60function findFiles(folder, filePostfix, callback) {
61 findFilesCommon(folder, filePostfix, callback, fs.readdir, fs.stat);
62}
63
64// sync version
65function findFilesSync(folder, filePostfix, callback) {
66 findFilesCommon(folder, filePostfix, callback, function(folder, cb) {
67 try {
68 cb(null, fs.readdirSync(folder));
69 } catch(e) { cb(e); }
70 }, function(path, cb) {
71 try {
72 cb(null, fs.statSync(path));
73 } catch(e) { cb(e); }
74 });
75}
76
77/**
78 * the factory function for i18n loaders
79 *
80 * @param rootLoader the loader to load the root locale
81 * @param localeLoader the loader to load the other locales
82 * @param requireAsync put the locales in a chunk
83 * @param chuckPrefix prefix of the chunk name (default to "i18n")
84 */
85module.exports = function(rootLoader, localeLoader, requireAsync, chuckPrefix) {
86 chuckPrefix = chuckPrefix || "i18n";
87 var loader = function(content) {
88 // split the request into i18n-loader, locale loader, directory and filename
89 var loaderSign = this.request.indexOf("!");
90 var remReq = this.request.substr(loaderSign);
91 var fileMatch = /^(.*!)([^!]+?)$/.exec(remReq);
92 remReq = fileMatch[1];
93 var file = fileMatch[2];
94 var filedir = path.dirname(file);
95 var filebase = path.basename(file);
96 var cb = this.async();
97 // read locale names from config if availible
98 var configuredLocales = this.options && this.options.i18n && this.options.i18n.locales;
99 // read "bundleTogether" from config
100 var dontBundleTogether = this.options && this.options.i18n && (this.options.i18n.bundleTogether === false);
101 var sync = !cb;
102 cb = cb || this.callback;
103 (sync ? findFilesSync : findFiles) // choose the fitting findFiles function
104 (filedir, filebase, function(err, files) {
105 if(err) return cb(err);
106 var buf = [];
107 // export a promise if async
108 if(requireAsync) {
109 buf.push("var cbs = [];\n");
110 buf.push("exports = module.exports = function(cb) {\n");
111 buf.push(" if(cbs) cbs.push(cb);\n");
112 buf.push(" else cb(exports);\n");
113 buf.push("}\n");
114 buf.push("\n");
115 }
116 // create mapping for locales
117 buf.push("var map = {\n");
118 function addLocale(locale, file) {
119 buf.push(JSON.stringify(locale));
120 buf.push(": function() {\n");
121 requireAsync && buf.push(" require.ensure([], function(require) {\n");
122 buf.push(" use(require(");
123 // the locale is required with the specified locale loader
124 if(file)
125 buf.push(JSON.stringify(localeLoader + remReq + filedir + "/" + file + "." + filebase));
126 else
127 buf.push(JSON.stringify(rootLoader + remReq + filedir + "/" + filebase));
128 buf.push("));\n");
129 if(requireAsync) {
130 buf.push(" }");
131 if(!dontBundleTogether) {
132 buf.push(", ");
133 buf.push(JSON.stringify(chuckPrefix + (locale ? "-" + locale : "")));
134 }
135 buf.push(");\n");
136 }
137 buf.push("},\n");
138 }
139 addLocale("", "");
140 var locales = files.slice(0);
141 if(configuredLocales) configuredLocales.forEach(function(locale) {
142 if(locales.indexOf(locale) == -1)
143 locales.push(locale);
144 });
145 locales.forEach(function(locale) {
146 file = locale;
147 if(files.indexOf(file) == -1) {
148 file = file.split("-");
149 for(var i = file.length; i >= 0; i--)
150 if(files.indexOf(file.join("-")) != -1) {
151 file = file.join("-");
152 break;
153 }
154 if(i == -1) file = "";
155 }
156 addLocale(locale, file);
157 });
158 buf.push("};\n");
159 buf.push("\n");
160 // determine the locale from the browser
161 // and execute the corresponding function in the mapping
162 buf.push("var nav = window.navigator, lang = nav.userLanguage || nav.language;\n");
163 buf.push("lang = lang && lang.split('-') || [];\n");
164 buf.push("(function() {\n");
165 buf.push(" for(var i = lang.length; i >= 0; i--) {\n");
166 buf.push(" var l = lang.slice(0, i).join('-');\n");
167 buf.push(" if(map[l]) return map[l]();\n");
168 buf.push(" }\n");
169 buf.push(" map['']();\n");
170 buf.push("}())\n");
171 buf.push("\n");
172 // use function is called with the locale
173 buf.push("function use(locale) {\n");
174 if(requireAsync) {
175 // async: copy stuff exported by the locale to the promise function
176 // if a function is exported, we create "call" and "apply" functions on the promise
177 buf.push(" if(typeof locale === 'function') {\n");
178 buf.push(" exports.call = function() {\n");
179 buf.push(" return locale.call.apply(locale, arguments);\n");
180 buf.push(" }\n");
181 buf.push(" exports.apply = function() {\n");
182 buf.push(" return locale.apply.apply(locale, arguments);\n");
183 buf.push(" }\n");
184 buf.push(" }\n");
185 buf.push(" for(var p in locale) exports[p] = locale[p];\n");
186 buf.push(" var c = cbs; cbs = null;\n");
187 buf.push(" for(var i = 0; i < c.length; i++) c[i](exports);\n");
188 } else {
189 // sync: simple exporting of the locale
190 buf.push(" module.exports = locale;\n");
191 }
192 buf.push("}\n");
193 cb(null, buf.join(""));
194 });
195 }
196 loader.seperable = true;
197 return loader;
198}
\No newline at end of file