UNPKG

@gctools-components/i18n-translation-webpack-plugin

Version:

Complete i18n translation solution for webpack, including code-splitting and automatic generation of translation files

226 lines (182 loc) 14.2 kB
'use strict'; var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); /* * Author: National Research Council Canada * Website: http://www.nrc-cnrc.gc.ca/eng/rd/ict/ * * License: MIT * Copyright: Her Majesty the Queen in Right of Canada, as represented by * the Minister of National Research Council, 2017 */ var _ConstDependency = require('webpack/lib/dependencies/ConstDependency'); var _ConstDependency2 = _interopRequireDefault(_ConstDependency); var _NullFactory = require('webpack/lib/NullFactory'); var _NullFactory2 = _interopRequireDefault(_NullFactory); var _ParserHelpers = require('webpack/lib/ParserHelpers'); var _ParserHelpers2 = _interopRequireDefault(_ParserHelpers); var _I18nNormalModuleFactory = require('./I18nNormalModuleFactory'); var _I18nNormalModuleFactory2 = _interopRequireDefault(_I18nNormalModuleFactory); var _I18nDependency = require('./I18nDependency'); var _I18nDependency2 = _interopRequireDefault(_I18nDependency); var _I18nInterpolatedDependency = require('./I18nInterpolatedDependency'); var _I18nInterpolatedDependency2 = _interopRequireDefault(_I18nInterpolatedDependency); var _poBuilder = require('./po-builder'); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var path = require('path'); /** * I18N Translation Webpack plugin * * Provides automatic creation of familiar gettext based translation files, * while automatically injecting the translation strings into your application * bundle. Various levels of code splitting are supported. * */ var I18nTranslationWebpackPlugin = function () { function I18nTranslationWebpackPlugin(options) { _classCallCheck(this, I18nTranslationWebpackPlugin); this.pot_files = {}; this.options = Object.assign({ languages: ['en_CA', 'fr_CA'], i18n_dir: 'i18n', extract_text_test: /\.(js|jsx)$/, extract_text_exclude: /node_modules/, codeSplitting: 'language', translate_global: '__', interpolate_global: '___', localizer_global: 'localizer', localizer_window: false }, options); } _createClass(I18nTranslationWebpackPlugin, [{ key: 'apply', value: function apply(compiler) { var _this = this; var context = compiler.context || process.cwd(); var i18nDir = path.join(context, this.options.i18n_dir); (0, _poBuilder.ensureDirExists)(i18nDir); this.pot_files = (0, _poBuilder.buildPotHash)(i18nDir); this.gtc_config = (0, _poBuilder.createGettextConfig)(context, this.options.i18n_dir); if (!compiler.options.module.rules) { // eslint-disable-next-line no-param-reassign compiler.options.module.rules = []; } compiler.options.module.rules.push({ test: this.options.extract_text_test, use: { loader: 'gettext-loader' }, exclude: this.options.extract_text_exclude }); compiler.hooks.done.tap('i18n_plugin', function () { if (_this.gtc_config) { _this.gtc_config.remove(); delete _this.gtc_config; } }); compiler.hooks.compilation.tap('i18n-plugin', function (compilation, _ref) { var normalModuleFactory = _ref.normalModuleFactory; var i18nFactory = new _I18nNormalModuleFactory2.default(normalModuleFactory); compilation.dependencyFactories.set(_I18nDependency2.default, i18nFactory); compilation.dependencyFactories.set(_I18nInterpolatedDependency2.default, i18nFactory); compilation.dependencyFactories.set(_ConstDependency2.default, new _NullFactory2.default()); compilation.dependencyTemplates.set(_I18nDependency2.default, new _I18nDependency2.default.Template()); compilation.dependencyTemplates.set(_I18nInterpolatedDependency2.default, new _I18nInterpolatedDependency2.default.Template()); compilation.dependencyTemplates.set(_ConstDependency2.default, new _ConstDependency2.default.Template()); var handler = function handler(parser) { var _options = _this.options, name = _options.translate_global, interpolate = _options.interpolate_global, loc = _options.localizer_global, availableLanguages = _options.languages; var interpolateReplaces = []; parser.hooks.call.for(name).tap('i18n-plugin', function (expr) { var ikey = interpolateReplaces.indexOf(expr.range[0]) >= 0; var resource = parser.state.module.resource; var dep = false; if (expr.arguments.length === 1) { var _expr$arguments = _slicedToArray(expr.arguments, 1), arg = _expr$arguments[0]; var key = parser.evaluateExpression(arg).string; var domain = path.relative(context, resource); dep = new _I18nDependency2.default(expr, key, domain, undefined, ikey, _this.options); dep.loc = expr.loc; } else if (expr.arguments.length === 2) { var _expr$arguments2 = _slicedToArray(expr.arguments, 2), arg1 = _expr$arguments2[0], arg2 = _expr$arguments2[1]; var _key = parser.evaluateExpression(arg1).string; var value = parser.evaluateExpression(arg2).number; var _domain = path.relative(context, resource); dep = new _I18nDependency2.default(expr, _key, _domain, value, ikey, _this.options); dep.loc = expr.loc; } if (dep) { parser.state.current.addDependency(dep); } return true; }); parser.hooks.call.for(interpolate).tap('i18n-plugin', function (expr) { var resource = parser.state.module.resource; if (expr.arguments.length > 1) { var domain = path.relative(context, resource); var dep = new _I18nInterpolatedDependency2.default(expr, name, domain, _this.options); dep.loc = expr.loc; parser.state.current.addDependency(dep); // Interpolate takes a literal as it's first parameter, but // only key's prefixed by the `translate_global` are processed. // To solve this, if the first argument to the interpolate // function is `translate_global`, it is marked and later removed var findLocalizedKey = function findLocalizedKey(node) { if (node.type === 'CallExpression') { if (node.callee.name === dep.tglobal) { interpolateReplaces.push(node.callee.range[0]); } if (node.arguments) { for (var x = 0; x < node.arguments.length; x += 1) { findLocalizedKey(node.arguments[x]); } } } }; findLocalizedKey(dep.expr.arguments[0]); } // must return false to continue parsing inside function return false; }); parser.hooks.expression.for(loc).tap('i18n-plugin', function (expr) { var pathToLocalizer = path.join(__dirname, 'localizer.js'); var config = { availableLanguages: availableLanguages }; var ex = '\n require(' + JSON.stringify(pathToLocalizer) + ').default(\n ' + JSON.stringify(config) + '\n )\n '; if (!_ParserHelpers2.default.addParsedVariableToModule(parser, loc, ex)) { return false; } _ParserHelpers2.default.toConstantDependency(parser, loc).bind(parser)(expr); return true; }); }; normalModuleFactory.hooks.parser.for('javascript/auto').tap('i18n-plugin', handler); normalModuleFactory.hooks.parser.for('javascript/dynamic').tap('i18n-plugin', handler); }); compiler.hooks.afterResolvers.tap('i18n-plugin-ar', function () { compiler.resolverFactory.hooks.resolver.for('normal').tap('i18n', function (resolver) { resolver.hooks.resolve.tapAsync('i18n', function (params, _, callback) { if (params.request === './<I18nWebpackPlugin>') { _this.pot_files = (0, _poBuilder.buildPoFiles)(path.join(context, _this.options.i18n_dir), _this.pot_files, _this.options.languages); var filename = path.join(__dirname, 'localizer.js'); callback(null, { path: filename, module: true, file: false, resolved: true, query: '?options=' + encodeURIComponent(JSON.stringify(_this.options)) }); } else { callback(); } }); }); }); } }]); return I18nTranslationWebpackPlugin; }(); module.exports = I18nTranslationWebpackPlugin;