UNPKG

amdi18n-loader

Version:

webpack i18n loader similar to require.js i18n plugin

216 lines (192 loc) 5.7 kB
/*global module*/ var loaderUtils = require('loader-utils'); module.exports = function (content) { var query = {}; if(this.query){ if(typeof this.query === 'string'){ query = loaderUtils.parseQuery(this.query); }else{ query = this.query; } } // whitelist / blacklist var enableList = []; var disableList = []; if(query.enable){ enableList = query.enable.replace(/[\[\]]/g,'').split('|').map(function(item){ return item.trim(); }); } if(query.disable){ disableList = query.disable.replace(/[\[\]]/g,'').split('|').map(function(item){ return item.trim(); }); } var fs = require('fs'); var path = require('path'); var target = this.resourcePath; var targetPath = path.dirname(target); var targetFileName = path.basename(target); if(!fs.existsSync(target)){ this.emitError(target + ' not exist!'); return; } // get lang definition from all formats of files var getJsonFromFile = function(content){ var sandbox = { json:'', module : {}, exports : {} }; var mockDefine = function(id, dependencies, factory){ if(!factory){ if(!dependencies){ factory = id; }else{ factory = dependencies; } } if(typeof factory === 'function'){ this.json = factory(); }else{ this.json = factory; } }; var vm = require('vm'); var context = vm.createContext(sandbox); var script; // get lang definition by file extension. if(/\.json/.test(targetFileName)){ script = 'json = ' + content; }else{ // turn esm to commonjs2 content = content.replace(/export[\s\r\n]+default[\s\r\n]*/, 'module.exports='); // define the 'define()' function script = 'var define=' + mockDefine.toString() + ';' + // execute 'define()' function content + ';' + // if it's amd, this.json is the correct value. // if it's not amd, give the commonjs result. 'if(!json && module.exports) json = module.exports;'; } var vmScript = new vm.Script(script); vmScript.runInContext(context); return sandbox.json; }; // get root var json = getJsonFromFile(content); // object with all langs var ret = {}; var coffee; var __content; // root lang ret.__root = json.root; if(query['expose-root'] || query.exposeRoot){ ret.root = json.root; } // merge // 1. langs in `root` // 2. enable list // 3. disable list var allLangs = []; for(var language in json){ if(language === 'root' && typeof json.root !== 'boolean') continue; if(enableList.length && enableList.indexOf(language) === -1) continue; if(disableList.indexOf(language) > -1) continue; if(json[language] === false) continue; // ignore disabled languages allLangs.push(language); } enableList.forEach(function(language){ if(allLangs.indexOf(language) === -1){ allLangs.push(language); } }); // deal all langs except root for(var i = 0; i < allLangs.length; i++){ var language = allLangs[i]; // get lang file. var targetFile = path.join(targetPath,language,targetFileName); if(!fs.existsSync(targetFile)){ this.emitError(targetFile + 'not exist!'); return; } // add targetFile to webpack dependencies // so they can be watched and live-reloaded // see #15 this.addDependency(targetFile); // lang file raw content __content = fs.readFileSync(targetFile,'utf8'); // compile coffee script if (targetFile.match(/\.coffee$/)){ if(!coffee) coffee = require('coffee-script'); __content = coffee.compile(__content,{ bare: true }); } // give this lang definition to ret ret['__' + language] = getJsonFromFile(__content); } // store found functions as strings var funcs = []; // amdi18n is the final lang definition. var retStr = 'var amdi18n=' + JSON.stringify(ret, function(key,value) { if(typeof value === 'function') { var vFunc = value.toString(); // store json version (function with quotes) funcs.push(JSON.stringify(vFunc)); return vFunc; } return value; }) + ';'; // replace function with quotes with raw function for(let func of funcs) { retStr = retStr.replace(func, JSON.parse(func)); } // this function would be exported // and running in browser // it's used to determin which lang to use // then copy all definition of that lang to the "root" level var init = function(language){ // get the default language if(!language){ if(typeof window !== 'undefined' && window._i18n && window._i18n.locale){ language = window._i18n.locale; }else if(typeof document !== 'undefined' && document.documentElement.lang){ language = document.documentElement.lang; }else if(typeof navigator !== 'undefined' && (navigator.languages || navigator.language || navigator.userLanguage)){ // navigator.languages does not exist in IE 11 language = (navigator.languages && navigator.languages[0]) || navigator.language || navigator.userLanguage; language = (language || 'root').toLowerCase(); }else{ language = 'root'; } } var target = this['__' + language] || this.__root; // copy definition to root level if (target) { for (var name in target) { this[name] = target[name]; } } // fallback to root var recursiveFallback = function(target, source){ for(var name in source){ switch(typeof target[name]){ case 'undefined': target[name] = source[name]; break; case 'object': recursiveFallback(target[name], source[name]); } } }; recursiveFallback(this, this.__root); }; // loader-related issue, nothing matters. retStr += 'amdi18n.init=' + init.toString() + ';'; retStr += 'amdi18n.init();'; retStr += 'module.exports=amdi18n;'; if(this.cacheable) this.cacheable(); this.value = content; return retStr; };