├── README.md ├── choose.loader.js ├── concat.loader.js ├── index.js ├── index.loader.js ├── locale ├── concat.js └── merge.js ├── merge.loader.js ├── package.json └── sync ├── choose.loader.js ├── concat.loader.js ├── index.loader.js └── merge.loader.js /README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webpack-contrib/i18n-loader/96eb74b697a13311f7cacd681e47fb553e479ba3/README.md -------------------------------------------------------------------------------- /choose.loader.js: -------------------------------------------------------------------------------- 1 | module.exports = require("./")("", "", true) -------------------------------------------------------------------------------- /concat.loader.js: -------------------------------------------------------------------------------- 1 | var path = require("path"); 2 | module.exports = require("./")("", path.join(__dirname, "locale/concat"), true) -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var fs = require("fs"); 2 | var path = require("path"); 3 | 4 | /** 5 | * Convert a string to a regexp string 6 | */ 7 | function 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 | */ 19 | function 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 60 | function findFiles(folder, filePostfix, callback) { 61 | findFilesCommon(folder, filePostfix, callback, fs.readdir, fs.stat); 62 | } 63 | 64 | // sync version 65 | function 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 | */ 85 | module.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 | } -------------------------------------------------------------------------------- /index.loader.js: -------------------------------------------------------------------------------- 1 | var path = require("path"); 2 | module.exports = require("./")("json", path.join(__dirname, "locale/merge") + "!json", true) -------------------------------------------------------------------------------- /locale/concat.js: -------------------------------------------------------------------------------- 1 | require = require("enhanced-require")(module); 2 | 3 | module.exports = function() { 4 | var loaderSign = this.request.indexOf("!"); 5 | var req = this.request.substr(loaderSign+1); 6 | var match = /^(.*)[\/\\]([^\.\/\\]+)\.([^\/\\!]+)$/.exec(req); 7 | var path = match[1]; 8 | var locale = match[2].split("-"); 9 | var filename = match[3]; 10 | var result = []; 11 | var cb = this.async() || this.callback; 12 | var context = this; 13 | function next(i) { 14 | if(i <= locale.length) { 15 | var request = path + "/" + (i == 0 ? "" : locale.slice(0, i).join("-") + ".") + filename; 16 | context.resolve(context.context, request, function(err, resolveRequest) { 17 | if(err) return cb(err); 18 | var part = require("raw!"+resolveRequest); 19 | result.push(part); 20 | next(i+1); 21 | }); 22 | } else { 23 | cb(null, result.join("\n\n")); 24 | } 25 | } 26 | next(0); 27 | } -------------------------------------------------------------------------------- /locale/merge.js: -------------------------------------------------------------------------------- 1 | require = require("enhanced-require")(module); 2 | 3 | function mergeObj(target, source) { 4 | for(var p in source) { 5 | if(Object.hasOwnProperty.call(source, p)) { 6 | if(typeof source[p] === "object" && typeof target[p] === "object") { 7 | mergeObj(target[p], source[p]); 8 | } else 9 | target[p] = source[p]; 10 | } 11 | } 12 | } 13 | 14 | module.exports = function() { 15 | var loaderSign = this.request.indexOf("!"); 16 | var req = this.request.substr(loaderSign+1); 17 | var match = /^(.*)[\/\\]([^\.\/\\]+)\.([^\/\\!]+)$/.exec(req); 18 | var path = match[1]; 19 | var locale = match[2].split("-"); 20 | var filename = match[3]; 21 | var result = {}; 22 | var cb = this.async() || this.callback; 23 | var context = this; 24 | function next(i) { 25 | if(i <= locale.length) { 26 | var request = path + "/" + (i == 0 ? "" : locale.slice(0, i).join("-") + ".") + filename; 27 | context.resolve(context.context, request, function(err, resolveRequest) { 28 | if(err) return cb(err); 29 | var part = require(resolveRequest); 30 | mergeObj(result, part); 31 | next(i+1); 32 | }); 33 | } else { 34 | cb(null, "module.exports = " + JSON.stringify(result, undefined, "\t") + ";"); 35 | } 36 | } 37 | next(0); 38 | } -------------------------------------------------------------------------------- /merge.loader.js: -------------------------------------------------------------------------------- 1 | var path = require("path"); 2 | module.exports = require("./")("", path.join(__dirname, "locale/merge"), true) -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "i18n-loader", 3 | "version": "0.3.0", 4 | "author": "Tobias Koppers @sokra", 5 | "description": "i18n loader module for webpack", 6 | "licenses": [ 7 | { 8 | "type": "MIT", 9 | "url": "http://www.opensource.org/licenses/mit-license.php" 10 | } 11 | ], 12 | "license": "MIT" 13 | } -------------------------------------------------------------------------------- /sync/choose.loader.js: -------------------------------------------------------------------------------- 1 | module.exports = require("../")("", "", false) -------------------------------------------------------------------------------- /sync/concat.loader.js: -------------------------------------------------------------------------------- 1 | var path = require("path"); 2 | module.exports = require("../")("", path.join(__dirname, "../locale/concat"), false) -------------------------------------------------------------------------------- /sync/index.loader.js: -------------------------------------------------------------------------------- 1 | var path = require("path"); 2 | module.exports = require("../")("json", path.join(__dirname, "../locale/merge") + "!json", false) -------------------------------------------------------------------------------- /sync/merge.loader.js: -------------------------------------------------------------------------------- 1 | var path = require("path"); 2 | module.exports = require("../")("", path.join(__dirname, "../locale/merge"), false) --------------------------------------------------------------------------------