├── src ├── cjs.js ├── MissingLocalizationError.js ├── MakeLocalizeFunction.js └── index.js ├── .eslintignore ├── .gitattributes ├── test ├── cases │ ├── opts-function-name.code.js │ ├── opts-fail-on-missing.code.js │ ├── apply-translations.code.js │ ├── __snapshots__ │ │ ├── apply-translations.test.js.snap │ │ └── opts-function-name.test.js.snap │ ├── apply-translations.test.js │ ├── opts-fail-on-missing.test.js │ └── opts-function-name.test.js ├── i18n-webpack-plugin.test.js ├── cases.setup.js └── MakeLocalizeFunction.test.js ├── .gitignore ├── .editorconfig ├── .eslintrc ├── .github ├── PULL_REQUEST_TEMPLATE.md └── ISSUE_TEMPLATE.md ├── .babelrc ├── appveyor.yml ├── LICENSE ├── .travis.yml ├── package.json ├── CHANGELOG.md └── README.md /src/cjs.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./index').default; 2 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /dist 3 | /test/cases/*.tmp.js 4 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | package-lock.json -diff 2 | * text=auto 3 | bin/* eol=lf 4 | -------------------------------------------------------------------------------- /test/cases/opts-function-name.code.js: -------------------------------------------------------------------------------- 1 | /* globals i18n */ 2 | exports.key = i18n('key'); 3 | -------------------------------------------------------------------------------- /test/cases/opts-fail-on-missing.code.js: -------------------------------------------------------------------------------- 1 | /* globals __ */ 2 | exports.missingKey = __('missing-key'); 3 | -------------------------------------------------------------------------------- /test/cases/apply-translations.code.js: -------------------------------------------------------------------------------- 1 | /* globals __ */ 2 | exports.defaults = __('default value', 'missing-key1'); 3 | exports.missingKey = __('missing-key2'); 4 | exports.staticKey = __('static-key'); 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | logs 2 | *.log 3 | npm-debug.log* 4 | yarn-debug.log* 5 | .eslintcache 6 | /coverage 7 | /dist 8 | /local 9 | /reports 10 | /node_modules 11 | .DS_Store 12 | Thumbs.db 13 | .idea 14 | .vscode 15 | *.sublime-project 16 | *.sublime-workspace 17 | 18 | /test/cases/*.tmp.js 19 | -------------------------------------------------------------------------------- /test/i18n-webpack-plugin.test.js: -------------------------------------------------------------------------------- 1 | import { name as PROJECT_NAME } from '../package.json'; 2 | import I18nPlugin from '../src'; 3 | 4 | describe(PROJECT_NAME, () => { 5 | test('should export the loader', (done) => { 6 | expect(I18nPlugin).toBeInstanceOf(Function); 7 | done(); 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | end_of_line = lf 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | 12 | [.md] 13 | insert_final_newline = false 14 | trim_trailing_whitespace = false 15 | -------------------------------------------------------------------------------- /test/cases/__snapshots__/apply-translations.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`apply-translations should return translated keys 1`] = ` 4 | Object { 5 | "defaults": "default value", 6 | "missingKey": "missing-key2", 7 | "staticKey": "translated static key", 8 | } 9 | `; 10 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "webpack", 3 | "rules": { 4 | "import/no-unresolved": 0, 5 | "import/extensions": 0, 6 | "no-plusplus": 1, 7 | "consistent-return": 1, 8 | "no-multi-assign": 1, 9 | "no-param-reassign": 1, 10 | "prefer-destructuring": 1, 11 | "no-nested-ternary": 1, 12 | "prefer-rest-params": 1 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test/cases/__snapshots__/opts-function-name.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`options.functionName the object notation should return translated keys 1`] = ` 4 | Object { 5 | "key": "translated key", 6 | } 7 | `; 8 | 9 | exports[`options.functionName the string notation should return translated keys 1`] = ` 10 | Object { 11 | "key": "translated key", 12 | } 13 | `; 14 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /test/cases/apply-translations.test.js: -------------------------------------------------------------------------------- 1 | import processFile from '../cases.setup'; 2 | 3 | describe('apply-translations', () => { 4 | let translated; 5 | 6 | beforeAll(() => { 7 | const translations = { 8 | 'static-key': 'translated static key', 9 | }; 10 | 11 | return processFile('apply-translations.code.js', translations) 12 | .then(({ file }) => { 13 | translated = require.requireActual(file); 14 | }); 15 | }); 16 | 17 | it('should return translated keys', () => { 18 | expect(translated).toMatchSnapshot(); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "env", 5 | { 6 | "useBuiltIns": true, 7 | "targets": { 8 | "node": "4.3" 9 | }, 10 | "exclude": [ 11 | "transform-async-to-generator", 12 | "transform-regenerator" 13 | ] 14 | } 15 | ] 16 | ], 17 | "plugins": [ 18 | [ 19 | "transform-object-rest-spread", 20 | { 21 | "useBuiltIns": true 22 | } 23 | ] 24 | ], 25 | "env": { 26 | "test": { 27 | "presets": [ 28 | "env" 29 | ], 30 | "plugins": [ 31 | "transform-object-rest-spread" 32 | ] 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /test/cases/opts-fail-on-missing.test.js: -------------------------------------------------------------------------------- 1 | import MissingLocalizationError from '../../src/MissingLocalizationError'; 2 | import processFile from '../cases.setup'; 3 | 4 | describe('options.failOnMissing', () => { 5 | it('should return translated keys', () => { 6 | const translations = {}; 7 | const options = { 8 | failOnMissing: true, 9 | }; 10 | 11 | expect.assertions(3); 12 | return processFile('opts-fail-on-missing.code.js', translations, options) 13 | .catch((err) => { 14 | expect(err).toBeInstanceOf(Error); 15 | expect(err).toBeInstanceOf(MissingLocalizationError); 16 | expect(err.message).toBe('Missing localization: missing-key'); 17 | }); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | branches: 2 | only: 3 | - master 4 | init: 5 | - git config --global core.autocrlf input 6 | environment: 7 | matrix: 8 | - nodejs_version: '8' 9 | webpack_version: 2.6.0 10 | job_part: test 11 | - nodejs_version: '6' 12 | webpack_version: 2.6.0 13 | job_part: test 14 | - nodejs_version: '4.3' 15 | webpack_version: 2.6.0 16 | job_part: test 17 | build: 'off' 18 | matrix: 19 | fast_finish: true 20 | install: 21 | - ps: Install-Product node $env:nodejs_version x64 22 | - npm install 23 | before_test: 24 | - cmd: npm install webpack@^%webpack_version% 25 | test_script: 26 | - node --version 27 | - npm --version 28 | - cmd: npm run appveyor:%job_part% 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright JS Foundation and other contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | 'Software'), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 18 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 19 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 20 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /test/cases.setup.js: -------------------------------------------------------------------------------- 1 | import { basename, dirname, join } from 'path'; 2 | import webpack from 'webpack'; 3 | import I18nPlugin from '../src'; 4 | 5 | export default function processFile(entry, ...pluginOpts) { 6 | const resolvedEntry = join(__dirname, 'cases', entry); 7 | const resolvedOutput = join(dirname(resolvedEntry), `${basename(resolvedEntry, '.code.js')}.tmp.js`); 8 | 9 | const compiler = webpack({ 10 | entry: resolvedEntry, 11 | output: { 12 | filename: basename(resolvedOutput), 13 | path: dirname(resolvedOutput), 14 | 15 | libraryTarget: 'commonjs2', 16 | }, 17 | plugins: [ 18 | new I18nPlugin(...pluginOpts), 19 | ], 20 | }); 21 | 22 | return new Promise((resolve, reject) => { 23 | compiler.run((err, stats) => { 24 | if (err) { 25 | reject(err); 26 | return; 27 | } 28 | // stats.compilation.errors contains errors and warnings produced by plugin itself 29 | if (stats.compilation.errors.length) { 30 | reject(stats.compilation.errors[0]); 31 | return; 32 | } 33 | 34 | resolve({ 35 | file: resolvedOutput, 36 | stats, 37 | }); 38 | }); 39 | }); 40 | } 41 | -------------------------------------------------------------------------------- /src/MissingLocalizationError.js: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License http://www.opensource.org/licenses/mit-license.php 3 | Author Tobias Koppers @sokra 4 | */ 5 | 6 | class MissingLocalizationError extends Error { 7 | constructor(module, name, value) { 8 | super(); 9 | 10 | Error.captureStackTrace(this, MissingLocalizationError); 11 | this.name = 'MissingLocalizationError'; 12 | this.requests = [ 13 | { name, value }, 14 | ]; 15 | this.module = module; 16 | // small workaround for babel 17 | Object.setPrototypeOf(this, MissingLocalizationError.prototype); 18 | this._buildMessage(); 19 | } 20 | 21 | _buildMessage() { 22 | this.message = this.requests.map((request) => { 23 | if (request.name === request.value) { 24 | return `Missing localization: ${request.name}`; 25 | } 26 | return `Missing localization: (${request.name}) ${request.value}`; 27 | }).join('\n'); 28 | } 29 | 30 | add(name, value) { 31 | for (let i = 0; i < this.requests.length; i++) { 32 | if (this.requests[i].name === name) return; 33 | } 34 | this.requests.push({ name, value }); 35 | this._buildMessage(); 36 | } 37 | } 38 | 39 | export default MissingLocalizationError; 40 | -------------------------------------------------------------------------------- /test/cases/opts-function-name.test.js: -------------------------------------------------------------------------------- 1 | import processFile from '../cases.setup'; 2 | 3 | describe('options.functionName', () => { 4 | describe('the object notation', () => { 5 | let translated; 6 | 7 | beforeAll(() => { 8 | const translations = { 9 | key: 'translated key', 10 | }; 11 | const options = { 12 | functionName: 'i18n', 13 | }; 14 | 15 | return processFile('opts-function-name.code.js', translations, options) 16 | .then(({ file }) => { 17 | translated = require.requireActual(file); 18 | }); 19 | }); 20 | 21 | it('should return translated keys', () => { 22 | expect(translated).toMatchSnapshot(); 23 | }); 24 | }); 25 | 26 | describe('the string notation', () => { 27 | let translated; 28 | 29 | beforeAll(() => { 30 | const translations = { 31 | key: 'translated key', 32 | }; 33 | const options = 'i18n'; 34 | 35 | return processFile('opts-function-name.code.js', translations, options) 36 | .then(({ file }) => { 37 | translated = require.requireActual(file); 38 | }); 39 | }); 40 | 41 | it('should return translated keys', () => { 42 | expect(translated).toMatchSnapshot(); 43 | }); 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /src/MakeLocalizeFunction.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Convert the localization object into a function in case we need to support nested keys. 3 | * 4 | * @param {Object} localization the language object, 5 | * @param {Boolean} nested 6 | * 7 | * @returns {Function} 8 | */ 9 | function makeLocalizeFunction(localization, nested) { 10 | return function localizeFunction(key) { 11 | return nested ? byString(localization, key) : localization[key]; 12 | }; 13 | } 14 | 15 | /** 16 | * Find the key if the key is a path expressed with dots 17 | * 18 | * e.g. 19 | * Code: __("errors.connectionError") 20 | * Lang: {"errors": {"connectionError": "There was an error connecting."}} 21 | * New Code: "There was an error connecting." 22 | * 23 | * @param {Object} localization 24 | * @param {String} nestedKey The original key 25 | * 26 | * @returns {*} 27 | */ 28 | function byString(localization, nestedKey) { 29 | // remove a leading dot and split by dot 30 | const keys = nestedKey.replace(/^\./, '').split('.'); 31 | 32 | // loop through the keys to find the nested value 33 | for (let i = 0, length = keys.length; i < length; ++i) { 34 | const key = keys[i]; 35 | 36 | if (!(key in localization)) { return; } 37 | 38 | localization = localization[key]; 39 | } 40 | 41 | return localization; 42 | } 43 | 44 | export default makeLocalizeFunction; 45 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | dist: trusty 3 | language: node_js 4 | branches: 5 | only: 6 | - master 7 | jobs: 8 | fast_finish: true 9 | allow_failures: 10 | - env: WEBPACK_VERSION=canary 11 | include: 12 | - &test-latest 13 | stage: Webpack latest 14 | nodejs: 6 15 | env: WEBPACK_VERSION=latest JOB_PART=test 16 | script: npm run travis:$JOB_PART 17 | - <<: *test-latest 18 | nodejs: 4.3 19 | env: WEBPACK_VERSION=latest JOB_PART=test 20 | script: npm run travis:$JOB_PART 21 | - <<: *test-latest 22 | node_js: 8 23 | env: WEBPACK_VERSION=latest JOB_PART=lint 24 | script: npm run travis:$JOB_PART 25 | - <<: *test-latest 26 | node_js: 8 27 | env: WEBPACK_VERSION=latest JOB_PART=coverage 28 | script: npm run travis:$JOB_PART 29 | after_success: 'bash <(curl -s https://codecov.io/bash)' 30 | - stage: Webpack canary 31 | before_script: npm i --no-save git://github.com/webpack/webpack.git#master 32 | script: npm run travis:$JOB_PART 33 | node_js: 8 34 | env: WEBPACK_VERSION=canary JOB_PART=test 35 | before_install: 36 | - 'if [[ `npm -v` != 5* ]]; then npm i -g npm@^5.0.0; fi' 37 | - nvm --version 38 | - node --version 39 | - npm --version 40 | before_script: 41 | - |- 42 | if [ "$WEBPACK_VERSION" ]; then 43 | npm i --no-save webpack@$WEBPACK_VERSION 44 | fi 45 | script: 46 | - 'npm run travis:$JOB_PART' 47 | after_success: 48 | - 'bash <(curl -s https://codecov.io/bash)' 49 | -------------------------------------------------------------------------------- /test/MakeLocalizeFunction.test.js: -------------------------------------------------------------------------------- 1 | import makeLocalizeFunction from '../src/MakeLocalizeFunction'; 2 | 3 | describe('MakeLocalizeFunction', () => { 4 | describe('with nested support', () => { 5 | let localize; 6 | 7 | beforeEach(() => { 8 | const nested = true; 9 | const translations = { 10 | 'a.b.c': 'bar', 11 | a: { 12 | b: { 13 | c: 'Foo', 14 | }, 15 | }, 16 | }; 17 | 18 | localize = makeLocalizeFunction(translations, nested); 19 | }); 20 | 21 | it('should return `Foo` for `a.b.c`', () => { 22 | expect(localize('a.b.c')).toEqual('Foo'); 23 | }); 24 | 25 | it('should return `undefined` for the missing translation', () => { 26 | expect(localize('the missing translation')).toEqual(undefined); // eslint-disable-line no-undefined 27 | }); 28 | }); 29 | 30 | describe('without nested support', () => { 31 | let localize; 32 | 33 | beforeEach(() => { 34 | const nested = false; 35 | const translations = { 36 | 'a.b.c': 'bar', 37 | a: { 38 | b: { 39 | c: 'Foo', 40 | }, 41 | }, 42 | }; 43 | 44 | localize = makeLocalizeFunction(translations, nested); 45 | }); 46 | 47 | it('should return `bar` for `a.b.c`', () => { 48 | expect(localize('a.b.c')).toEqual('bar'); 49 | }); 50 | 51 | it('should return `undefined` for the missing translation', () => { 52 | expect(localize('the missing translation')).toEqual(undefined); // eslint-disable-line no-undefined 53 | }); 54 | }); 55 | }); 56 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "i18n-webpack-plugin", 3 | "version": "1.0.0", 4 | "author": "Tobias Koppers @sokra", 5 | "description": "Embed localization into your bundle", 6 | "main": "dist/cjs.js", 7 | "files": [ 8 | "dist" 9 | ], 10 | "license": "MIT", 11 | "scripts": { 12 | "start": "npm run build -- -w", 13 | "appveyor:test": "npm run test", 14 | "build": "cross-env NODE_ENV=production babel src -d dist --ignore 'src/**/*.test.js'", 15 | "clean": "del-cli dist", 16 | "clean:dist": "del-cli dist", 17 | "lint": "eslint --cache src test", 18 | "lint-staged": "lint-staged", 19 | "prebuild": "npm run clean", 20 | "prepublish": "npm run build", 21 | "release": "standard-version", 22 | "security": "nsp check", 23 | "serve:dev": "nodemon $2 --exec babel-node", 24 | "test": "jest", 25 | "test:watch": "jest --watch", 26 | "test:coverage": "jest --collectCoverageFrom='src/**/*.js' --coverage", 27 | "travis:coverage": "npm run test:coverage -- --runInBand", 28 | "travis:lint": "npm run lint && npm run security", 29 | "travis:test": "npm run test -- --runInBand", 30 | "webpack-defaults": "webpack-defaults" 31 | }, 32 | "devDependencies": { 33 | "babel-cli": "^6.24.0", 34 | "babel-jest": "^19.0.0", 35 | "babel-plugin-transform-object-rest-spread": "^6.23.0", 36 | "babel-polyfill": "^6.23.0", 37 | "babel-preset-env": "^1.5.1", 38 | "cross-env": "^5.0.1", 39 | "del-cli": "^1.0.0", 40 | "eslint": "^3.19.0", 41 | "eslint-config-webpack": "^1.2.1", 42 | "eslint-plugin-import": "^2.2.0", 43 | "jest": "^19.0.2", 44 | "lint-staged": "^3.6.0", 45 | "nsp": "^2.6.3", 46 | "pre-commit": "^1.2.2", 47 | "standard-version": "^4.0.0", 48 | "webpack-defaults": "^1.4.0" 49 | }, 50 | "peerDependencies": { 51 | "webpack": "^2.0.0 || >= 3.0.0-rc.0 || ^3.0.0" 52 | }, 53 | "engines": { 54 | "node": ">= 4.3 < 5.0.0 || >= 5.10" 55 | }, 56 | "homepage": "https://webpack.js.org", 57 | "repository": { 58 | "type": "git", 59 | "url": "git+https://github.com/webpack-contrib/i18n-webpack-plugin.git" 60 | }, 61 | "bugs": { 62 | "url": "https://github.com/webpack-contrib/i18n-webpack-plugin/issues" 63 | }, 64 | "pre-commit": "lint-staged", 65 | "lint-staged": { 66 | "*.js": [ 67 | "eslint --fix", 68 | "git add" 69 | ] 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | 6 | # [1.0.0](https://github.com/webpack-contrib/i18n-webpack-plugin/compare/v1.0.0-beta.1...v1.0.0) (2017-06-10) 7 | 8 | ### Bug Fixes 9 | 10 | * **index:** hide warnings when options.hideMessage is truthy ([#45](https://github.com/webpack-contrib/i18n-webpack-plugin/issues/45)) ([d4c7877](https://github.com/webpack-contrib/i18n-webpack-plugin/commit/d4c7877)) 11 | * Missing default update ([a6ba200](https://github.com/webpack-contrib/i18n-webpack-plugin/commit/a6ba200)) 12 | 13 | 14 | ### Code Refactoring 15 | 16 | * apply webpack-defaults ([#41](https://github.com/webpack-contrib/i18n-webpack-plugin/issues/41)) ([c8621a1](https://github.com/webpack-contrib/i18n-webpack-plugin/commit/c8621a1)) 17 | 18 | 19 | ### BREAKING CHANGES 20 | 21 | * Enforces a minimum NodeJS version of `4.3` via engines & drops support for Webpack v1.x. 22 | * Drops support for Webpack `1.x` via a peedDependency on Webpack of `^2.0.0 || >= 3.0.0-rc.0 || ^3.0.0`. 23 | 24 | 25 | # [1.0.0-beta.1](https://github.com/webpack-contrib/i18n-webpack-plugin/compare/v1.0.0-beta.0...v1.0.0-beta.1) (2017-06-06) 26 | 27 | 28 | ### Bug Fixes 29 | 30 | * Missing default update ([a6ba200](https://github.com/webpack-contrib/i18n-webpack-plugin/commit/a6ba200)) 31 | 32 | 33 | 34 | 35 | # [1.0.0-beta.0](https://github.com/webpack-contrib/i18n-webpack-plugin/compare/v0.3.0...v1.0.0-beta.0) (2017-05-01) 36 | 37 | 38 | ### Bug Fixes 39 | 40 | * **index:** hide warnings when options.hideMessage is truthy ([#45](https://github.com/webpack-contrib/i18n-webpack-plugin/issues/45)) ([d4c7877](https://github.com/webpack-contrib/i18n-webpack-plugin/commit/d4c7877)) 41 | 42 | 43 | ### Code Refactoring 44 | 45 | * apply webpack-defaults ([#41](https://github.com/webpack-contrib/i18n-webpack-plugin/issues/41)) ([c8621a1](https://github.com/webpack-contrib/i18n-webpack-plugin/commit/c8621a1)) 46 | 47 | 48 | ### BREAKING CHANGES 49 | 50 | * Enforces a minimum NodeJS version of `4.3` via engines & drops support for Webpack v1.x. 51 | 52 | 53 | 54 | # Change Log 55 | 56 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 57 | 58 | x.x.x / -- 59 | ================== 60 | 61 | * Bug fix - 62 | * Feature - 63 | * Chore - 64 | * Docs - 65 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![npm][npm]][npm-url] 2 | [![deps][deps]][deps-url] 3 | [![test][test]][test-url] 4 | [![coverage][cover]][cover-url] 5 | [![chat][chat]][chat-url] 6 | 7 |
8 | 9 | 10 | 12 | 13 |

i18n Plugin

14 |

i18n (localization) plugin for Webpack.

15 |

16 | 17 |

Install

18 | 19 | ```bash 20 | npm i -D i18n-webpack-plugin 21 | ``` 22 | 23 |

Usage

24 | 25 | This plugin creates bundles with translations baked in. So you can serve the translated bundle to your clients. 26 | 27 | see [webpack/webpack/examples/i18n](https://github.com/webpack/webpack/tree/master/examples/i18n). 28 | 29 |

Options

30 | 31 | ``` 32 | plugins: [ 33 | ... 34 | new I18nPlugin(languageConfig, optionsObj) 35 | ], 36 | ``` 37 | - `optionsObj.functionName`: the default value is `__`, you can change it to other function name. 38 | - `optionsObj.failOnMissing`: the default value is `false`, which will show a warning message, if the mapping text cannot be found. If set to `true`, the message will be an error message. 39 | - `optionsObj.hideMessage`: the default value is `false`, which will show the warning/error message. If set to `true`, the message will be hidden. 40 | - `optionsObj.nested`: the default value is `false`. If set to `true`, the keys in `languageConfig` can be nested. This option is interpreted only if `languageConfig` isn't a function. 41 | 42 |

Maintainers

43 | 44 | 45 | 46 | 47 | 53 | 59 | 65 | 71 | 72 | 73 |
48 | 50 |
51 | Juho Vepsäläinen 52 |
54 | 56 |
57 | Joshua Wiens 58 |
60 | 62 |
63 | Kees Kluskens 64 |
66 | 68 |
69 | Sean Larkin 70 |
74 | 75 | [npm]: https://img.shields.io/npm/v/i18n-webpack-plugin.svg 76 | [npm-url]: https://npmjs.com/package/i18n-webpack-plugin 77 | 78 | [deps]: https://david-dm.org/webpack-contrib/i18n-webpack-plugin.svg 79 | [deps-url]: https://david-dm.org/webpack-contrib/i18n-webpack-plugin 80 | 81 | [chat]: https://img.shields.io/badge/gitter-webpack%2Fwebpack-brightgreen.svg 82 | [chat-url]: https://gitter.im/webpack/webpack 83 | 84 | [test]: http://img.shields.io/travis/webpack-contrib/i18n-webpack-plugin.svg 85 | [test-url]: https://travis-ci.org/webpack-contrib/i18n-webpack-plugin 86 | 87 | [cover]: https://codecov.io/gh/webpack-contrib/i18n-webpack-plugin/branch/master/graph/badge.svg 88 | [cover-url]: https://codecov.io/gh/webpack-contrib/i18n-webpack-plugin 89 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License http://www.opensource.org/licenses/mit-license.php 3 | Author Tobias Koppers @sokra 4 | */ 5 | import ConstDependency from 'webpack/lib/dependencies/ConstDependency'; 6 | import NullFactory from 'webpack/lib/NullFactory'; 7 | import MissingLocalizationError from './MissingLocalizationError'; 8 | import makeLocalizeFunction from './MakeLocalizeFunction'; 9 | 10 | /** 11 | * 12 | * @param {object|function} localization 13 | * @param {object|string} Options object or obselete functionName string 14 | * @constructor 15 | */ 16 | class I18nPlugin { 17 | constructor(localization, options, failOnMissing) { 18 | // Backward-compatiblility 19 | if (typeof options === 'string') { 20 | options = { 21 | functionName: options, 22 | }; 23 | } 24 | 25 | if (typeof failOnMissing !== 'undefined') { 26 | options.failOnMissing = failOnMissing; 27 | } 28 | 29 | this.options = options || {}; 30 | this.localization = localization ? (typeof localization === 'function' ? localization : makeLocalizeFunction(localization, !!this.options.nested)) : null; 31 | this.functionName = this.options.functionName || '__'; 32 | this.failOnMissing = !!this.options.failOnMissing; 33 | this.hideMessage = this.options.hideMessage || false; 34 | } 35 | 36 | apply(compiler) { 37 | const { localization, failOnMissing, hideMessage } = this; // eslint-disable-line no-unused-vars 38 | const name = this.functionName; 39 | 40 | compiler.plugin('compilation', (compilation, params) => { // eslint-disable-line no-unused-vars 41 | compilation.dependencyFactories.set(ConstDependency, new NullFactory()); 42 | compilation.dependencyTemplates.set(ConstDependency, new ConstDependency.Template()); 43 | }); 44 | 45 | compiler.plugin('compilation', (compilation, data) => { 46 | data.normalModuleFactory.plugin('parser', (parser, options) => { // eslint-disable-line no-unused-vars 47 | // should use function here instead of arrow function due to save the Tapable's context 48 | parser.plugin(`call ${name}`, function i18nPlugin(expr) { 49 | let param; 50 | let defaultValue; 51 | switch (expr.arguments.length) { 52 | case 2: 53 | param = this.evaluateExpression(expr.arguments[1]); 54 | if (!param.isString()) return; 55 | param = param.string; 56 | defaultValue = this.evaluateExpression(expr.arguments[0]); 57 | if (!defaultValue.isString()) return; 58 | defaultValue = defaultValue.string; 59 | break; 60 | case 1: 61 | param = this.evaluateExpression(expr.arguments[0]); 62 | if (!param.isString()) return; 63 | defaultValue = param = param.string; 64 | break; 65 | default: 66 | return; 67 | } 68 | let result = localization ? localization(param) : defaultValue; 69 | 70 | if (typeof result === 'undefined') { 71 | let error = this.state.module[__dirname]; 72 | if (!error) { 73 | error = new MissingLocalizationError(this.state.module, param, defaultValue); 74 | this.state.module[__dirname] = error; 75 | 76 | if (failOnMissing) { 77 | this.state.module.errors.push(error); 78 | } else if (!hideMessage) { 79 | this.state.module.warnings.push(error); 80 | } 81 | } else if (!error.requests.includes(param)) { 82 | error.add(param, defaultValue); 83 | } 84 | result = defaultValue; 85 | } 86 | 87 | const dep = new ConstDependency(JSON.stringify(result), expr.range); 88 | dep.loc = expr.loc; 89 | this.state.current.addDependency(dep); 90 | return true; 91 | }); 92 | }); 93 | }); 94 | } 95 | } 96 | 97 | export default I18nPlugin; 98 | --------------------------------------------------------------------------------