{"_id":"@4rthem/i18next-scanner","_rev":"3-2ab7281f42e9055952aac7fcc79f45a2","name":"@4rthem/i18next-scanner","dist-tags":{"latest":"0.0.3"},"versions":{"0.0.1":{"name":"@4rthem/i18next-scanner","version":"0.0.1","keywords":["i18n","i18next","gruntplugin","gulpplugin","gettext","hash","sha1","crc32","md5"],"author":{"name":"Cheton Wu","email":"cheton@gmail.com"},"license":"MIT","_id":"@4rthem/i18next-scanner@0.0.1","maintainers":[{"name":"4rthem","email":"arthurdemoulins@gmail.com"}],"contributors":[{"url":"https://github.com/cheton","name":"Cheton Wu","email":"cheton@gmail.com"}],"homepage":"https://github.com/4rthem/i18next-scanner","bugs":{"url":"https://github.com/4rthem/i18next-scanner/issues"},"bin":{"i18next-scanner":"bin/cli.js"},"dist":{"shasum":"5545ac0efa2e2cd2e5bf23433d2c2b7d6b2a77bf","tarball":"https://registry.npmjs.org/@4rthem/i18next-scanner/-/i18next-scanner-0.0.1.tgz","fileCount":11,"integrity":"sha512-SboeB5YOuuaHbMt/Psj+gUWDULBCdiRkcQXFwnrqpWfWwxNFavXR5qon+6p0VLvND9++BHIRavl4TSANJJ20rA==","signatures":[{"sig":"MEYCIQDPhrpPa+opBt6p3KQpz+CaHtcUjHximwAc/tZFsMkQcAIhAJjjZFbh47CJeWNGa+dIS2M2T5ACeRstqcO0lFJT58nL","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":101740},"jest":{"testMatch":["<rootDir>/test/**/?(*.)(spec|test).js?(x)"],"setupFiles":[],"testTimeout":30000,"collectCoverage":true,"testEnvironment":"node","coverageReporters":["lcov","text","html"],"setupFilesAfterEnv":["<rootDir>/jest.setup.js"],"collectCoverageFrom":["<rootDir>/src/**/*.js","!**/node_modules/**","!**/test/**"],"testPathIgnorePatterns":[],"unmockedModulePathPatterns":["react"]},"main":"lib/index.js","engines":{"node":">=12"},"gitHead":"7f814818b67ae9ebbf772e1f398414147d85f32d","private":false,"scripts":{"test":"jest --no-cache","build":"babel ./src --out-dir ./lib","clean":"del lib .nyc_output","eslint":"eslint ./src","eslint-fix":"eslint --fix ./src ./test/*.js","prepublishOnly":"npm run eslint && npm run build"},"_npmUser":{"name":"4rthem","email":"arthurdemoulins@gmail.com"},"repository":{"url":"git+ssh://git@github.com/4rthem/i18next-scanner.git","type":"git"},"_npmVersion":"10.2.3","description":"Scan your code, extract translation keys/values, and merge them into i18n resource files.","directories":{},"_nodeVersion":"18.19.0","dependencies":{"eol":"^0.9.1","acorn":"^8.0.4","chalk":"^4.1.0","vinyl":"^3.0.0","lodash":"^4.0.0","parse5":"^6.0.0","i18next":"*","through2":"^4.0.0","vinyl-fs":"^4.0.0","acorn-jsx":"^5.3.1","commander":"^9.0.0","deepmerge":"^4.0.0","gulp-sort":"^2.0.0","acorn-walk":"^8.0.0","clone-deep":"^4.0.0","sortobject":"^4.0.0","ensure-type":"^1.5.0","acorn-stage3":"^4.0.0","esprima-next":"^5.7.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"gulp":"^4.0.2","jest":"^28.1.1","sha1":"^1.1.1","react":"^18.0.0","eslint":"^8.18.0","codecov":"^3.8.3","core-js":"^3.23.1","del-cli":"^4.0.1","gulp-tap":"^2.0.0","gulp-util":"^3.0.8","react-dom":"^18.0.0","@babel/cli":"^7.17.10","text-table":"^0.2.0","@babel/core":"^7.18.5","jest-extended":"^2.0.0","react-i18next":"^11.12.0","@babel/register":"^7.17.7","@babel/preset-env":"^7.18.2","eslint-plugin-jest":"~26.5.3","@babel/preset-react":"^7.17.12","eslint-plugin-react":"^7.30.0","@babel/eslint-parser":"^7.18.2","@babel/eslint-plugin":"^7.17.7","eslint-plugin-import":"^2.26.0","@testing-library/react":"^13.3.0","eslint-plugin-jsx-a11y":"^6.5.1","jest-environment-jsdom":"^28.1.1","@trendmicro/babel-config":"^1.0.2","eslint-config-trendmicro":"^3.1.0","@testing-library/jest-dom":"^5.16.4","@babel/plugin-transform-runtime":"^7.18.5"},"_npmOperationalInternal":{"tmp":"tmp/i18next-scanner_0.0.1_1737731235499_0.45331046865883007","host":"s3://npm-registry-packages-npm-production"}},"0.0.2":{"name":"@4rthem/i18next-scanner","version":"0.0.2","keywords":["i18n","i18next","gruntplugin","gulpplugin","gettext","hash","sha1","crc32","md5"],"author":{"name":"Cheton Wu","email":"cheton@gmail.com"},"license":"MIT","_id":"@4rthem/i18next-scanner@0.0.2","maintainers":[{"name":"4rthem","email":"arthurdemoulins@gmail.com"}],"contributors":[{"url":"https://github.com/cheton","name":"Cheton Wu","email":"cheton@gmail.com"}],"homepage":"https://github.com/i18next/i18next-scanner","bugs":{"url":"https://github.com/i18next/i18next-scanner/issues"},"bin":{"i18next-scanner":"bin/cli.js"},"dist":{"shasum":"8401eb0c3f7275b851d636e7cdbf14cb881bb38f","tarball":"https://registry.npmjs.org/@4rthem/i18next-scanner/-/i18next-scanner-0.0.2.tgz","fileCount":11,"integrity":"sha512-otxFtuhx3pEHG68Mhf9b1cwP4jiTYl9WNzQkS3jtLvfPl4tUmcLnz9gLsILpQkexKoPou/2Gl8tb0HREp3Pzng==","signatures":[{"sig":"MEYCIQD/sES+Nc3i4ff9yEg6r7X1khHRzwlmhdbbxlYljiX/yAIhAJSxTQIv4iFWLODmcjBWV5xxAcHQcm3s27K8jfBg5/KZ","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":101691},"jest":{"testMatch":["<rootDir>/test/**/?(*.)(spec|test).js?(x)"],"setupFiles":[],"testTimeout":30000,"collectCoverage":true,"testEnvironment":"node","coverageReporters":["lcov","text","html"],"setupFilesAfterEnv":["<rootDir>/jest.setup.js"],"collectCoverageFrom":["<rootDir>/src/**/*.js","!**/node_modules/**","!**/test/**"],"testPathIgnorePatterns":[],"unmockedModulePathPatterns":["react"]},"main":"lib/index.js","engines":{"node":">=12"},"gitHead":"5ee842d279b42f627b01a801b05a5e41d38834c4","scripts":{"test":"jest --no-cache","build":"babel ./src --out-dir ./lib","clean":"del lib .nyc_output","eslint":"eslint ./src","eslint-fix":"eslint --fix ./src ./test/*.js","prepublishOnly":"npm run eslint && npm run build"},"_npmUser":{"name":"4rthem","email":"arthurdemoulins@gmail.com"},"repository":{"url":"git+ssh://git@github.com/i18next/i18next-scanner.git","type":"git"},"_npmVersion":"11.6.0","description":"Scan your code, extract translation keys/values, and merge them into i18n resource files.","directories":{},"_nodeVersion":"24.8.0","dependencies":{"eol":"^0.9.1","acorn":"^8.0.4","chalk":"^4.1.0","vinyl":"^3.0.0","lodash":"^4.0.0","parse5":"^6.0.0","i18next":"*","through2":"^4.0.0","vinyl-fs":"^4.0.0","acorn-jsx":"^5.3.1","commander":"^9.0.0","deepmerge":"^4.0.0","gulp-sort":"^2.0.0","acorn-walk":"^8.0.0","clone-deep":"^4.0.0","sortobject":"^4.0.0","ensure-type":"^1.5.0","acorn-stage3":"^4.0.0","esprima-next":"^5.7.0"},"_hasShrinkwrap":false,"devDependencies":{"gulp":"^4.0.2","jest":"^28.1.1","sha1":"^1.1.1","react":"^18.0.0","eslint":"^8.18.0","codecov":"^3.8.3","core-js":"^3.23.1","del-cli":"^4.0.1","gulp-tap":"^2.0.0","gulp-util":"^3.0.8","react-dom":"^18.0.0","@babel/cli":"^7.17.10","text-table":"^0.2.0","@babel/core":"^7.18.5","jest-extended":"^2.0.0","react-i18next":"^11.12.0","@babel/register":"^7.17.7","@babel/preset-env":"^7.18.2","eslint-plugin-jest":"~26.5.3","@babel/preset-react":"^7.17.12","eslint-plugin-react":"^7.30.0","@babel/eslint-parser":"^7.18.2","@babel/eslint-plugin":"^7.17.7","eslint-plugin-import":"^2.26.0","@testing-library/react":"^13.3.0","eslint-plugin-jsx-a11y":"^6.5.1","jest-environment-jsdom":"^28.1.1","@trendmicro/babel-config":"^1.0.2","eslint-config-trendmicro":"^3.1.0","@testing-library/jest-dom":"^5.16.4","@babel/plugin-transform-runtime":"^7.18.5"},"_npmOperationalInternal":{"tmp":"tmp/i18next-scanner_0.0.2_1763641639001_0.9565178326387653","host":"s3://npm-registry-packages-npm-production"}},"0.0.3":{"name":"@4rthem/i18next-scanner","version":"0.0.3","description":"Scan your code, extract translation keys/values, and merge them into i18n resource files.","homepage":"https://github.com/i18next/i18next-scanner","author":{"name":"Cheton Wu","email":"cheton@gmail.com"},"contributors":[{"name":"Cheton Wu","email":"cheton@gmail.com","url":"https://github.com/cheton"}],"bin":{"i18next-scanner":"bin/cli.js"},"main":"lib/index.js","scripts":{"prepublishOnly":"npm run eslint && npm run build","build":"babel ./src --out-dir ./lib","clean":"del lib .nyc_output","eslint":"eslint ./src","eslint-fix":"eslint --fix ./src ./test/*.js","test":"jest --no-cache"},"repository":{"type":"git","url":"git+ssh://git@github.com/i18next/i18next-scanner.git"},"license":"MIT","engines":{"node":">=12"},"keywords":["i18n","i18next","gruntplugin","gulpplugin","gettext","hash","sha1","crc32","md5"],"dependencies":{"acorn":"^8.0.4","acorn-jsx":"^5.3.1","acorn-stage3":"^4.0.0","acorn-walk":"^8.0.0","chalk":"^4.1.0","clone-deep":"^4.0.0","commander":"^9.0.0","deepmerge":"^4.0.0","ensure-type":"^1.5.0","eol":"^0.9.1","esprima-next":"^5.7.0","gulp-sort":"^2.0.0","i18next":"*","lodash":"^4.0.0","parse5":"^6.0.0","sortobject":"^4.0.0","through2":"^4.0.0","vinyl":"^3.0.0","vinyl-fs":"^4.0.0"},"devDependencies":{"@babel/cli":"^7.17.10","@babel/core":"^7.18.5","@babel/eslint-parser":"^7.18.2","@babel/eslint-plugin":"^7.17.7","@babel/plugin-transform-runtime":"^7.18.5","@babel/preset-env":"^7.18.2","@babel/preset-react":"^7.17.12","@babel/register":"^7.17.7","@testing-library/jest-dom":"^5.16.4","@testing-library/react":"^13.3.0","@trendmicro/babel-config":"^1.0.2","codecov":"^3.8.3","core-js":"^3.23.1","del-cli":"^4.0.1","eslint":"^8.18.0","eslint-config-trendmicro":"^3.1.0","eslint-plugin-import":"^2.26.0","eslint-plugin-jest":"~26.5.3","eslint-plugin-jsx-a11y":"^6.5.1","eslint-plugin-react":"^7.30.0","gulp":"^4.0.2","gulp-tap":"^2.0.0","gulp-util":"^3.0.8","jest":"^28.1.1","jest-environment-jsdom":"^28.1.1","jest-extended":"^2.0.0","react":"^18.0.0","react-dom":"^18.0.0","react-i18next":"^11.12.0","sha1":"^1.1.1","text-table":"^0.2.0"},"jest":{"setupFiles":[],"setupFilesAfterEnv":["<rootDir>/jest.setup.js"],"unmockedModulePathPatterns":["react"],"testMatch":["<rootDir>/test/**/?(*.)(spec|test).js?(x)"],"collectCoverage":true,"collectCoverageFrom":["<rootDir>/src/**/*.js","!**/node_modules/**","!**/test/**"],"coverageReporters":["lcov","text","html"],"testPathIgnorePatterns":[],"testTimeout":30000,"testEnvironment":"node"},"_id":"@4rthem/i18next-scanner@0.0.3","gitHead":"08a49e6de985b9c69301dfacc75c763a9423ba43","bugs":{"url":"https://github.com/i18next/i18next-scanner/issues"},"_nodeVersion":"24.8.0","_npmVersion":"11.6.0","dist":{"integrity":"sha512-x2Q7buLRmhPFgKvZOFIXq2eDmtoHcY1w14qQc/y3l+a23Mh//1sIwpx/OcLo37n2FJYyyxNqh7wt7LWyXscMcA==","shasum":"cd21f2717771111f610594c47409a85c8ff50f79","tarball":"https://registry.npmjs.org/@4rthem/i18next-scanner/-/i18next-scanner-0.0.3.tgz","fileCount":11,"unpackedSize":101690,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDoSmS1hQne5GNDwZ1C7JoW1VPOeqVBrqMwi6/H58DShAIhAO0gDowPuM7JQukaiIdAqY806snrDLIU14xELqz08Wdf"}]},"_npmUser":{"name":"4rthem","email":"arthurdemoulins@gmail.com"},"directories":{},"maintainers":[{"name":"4rthem","email":"arthurdemoulins@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/i18next-scanner_0.0.3_1763643486452_0.4079562876843579"},"_hasShrinkwrap":false}},"time":{"created":"2025-01-24T15:07:15.373Z","modified":"2025-11-20T12:58:06.831Z","0.0.1":"2025-01-24T15:07:15.746Z","0.0.2":"2025-11-20T12:27:19.246Z","0.0.3":"2025-11-20T12:58:06.664Z"},"bugs":{"url":"https://github.com/i18next/i18next-scanner/issues"},"author":{"name":"Cheton Wu","email":"cheton@gmail.com"},"license":"MIT","homepage":"https://github.com/i18next/i18next-scanner","keywords":["i18n","i18next","gruntplugin","gulpplugin","gettext","hash","sha1","crc32","md5"],"repository":{"type":"git","url":"git+ssh://git@github.com/i18next/i18next-scanner.git"},"description":"Scan your code, extract translation keys/values, and merge them into i18n resource files.","contributors":[{"name":"Cheton Wu","email":"cheton@gmail.com","url":"https://github.com/cheton"}],"maintainers":[{"name":"4rthem","email":"arthurdemoulins@gmail.com"}],"readme":"# i18next-scanner [![build status](https://travis-ci.org/i18next/i18next-scanner.svg?branch=master)](https://travis-ci.org/i18next/i18next-scanner) [![Coverage Status](https://coveralls.io/repos/i18next/i18next-scanner/badge.svg?branch=master&service=github)](https://coveralls.io/github/i18next/i18next-scanner?branch=master)\n\n[![NPM](https://nodei.co/npm/i18next-scanner.png?downloads=true&stars=true)](https://www.npmjs.com/package/i18next-scanner)\n\nScan your code, extract translation keys/values, and merge them into i18n resource files.\n\nTurns your code\n```js\ni18n._('Loading...');\ni18n._('Backslashes in single quote: \\' \\\\ \\'');\ni18n._('This is \\\na multiline \\\nstring');\n\ni18n.t('car', { context: 'blue', count: 1 }); // output: 'One blue car'\ni18n.t('car', { context: 'blue', count: 2 }); // output: '2 blue cars'\n\n<Trans i18nKey=\"some.key\">Default text</Trans>\n```\n\ninto resource files\n```js\n{\n  \"Loading...\": \"Wird geladen...\", // uses existing translation\n  \"Backslashes in single quote: ' \\\\ '\": \"__NOT_TRANSLATED__\", // returns a custom string\n  \"This is a multiline string\": \"this is a multiline string\", // returns the key as the default value\n  \"car\": \"car\",\n  \"car_blue\": \"One blue car\",\n  \"car_blue_plural\": \"{{count}} blue cars\",\n  \"some\": {\n    \"key\": \"Default text\"\n  }\n}\n```\n\n\n## Notice\nThere is a major breaking change since v1.0, and the API interface and options are not compatible with v0.x.\n\nCheckout [Migration Guide](https://github.com/i18next/i18next-scanner/wiki/Migration-Guide) while upgrading from earlier versions.\n\n## Features\n* Fully compatible with [i18next](https://github.com/i18next/i18next) - a full-featured i18n javascript library for translating your webapplication.\n* Support [react-i18next](https://github.com/i18next/react-i18next) for parsing the <b>Trans</b> component\n* Support [Key Based Fallback](https://www.i18next.com/principles/fallback#key-fallback/) to write your code without the need to maintain i18n keys. This feature is available since [i18next@^2.1.0](https://github.com/i18next/i18next/blob/master/CHANGELOG.md#210)\n* A standalone parser API\n* A transform stream that works with both Gulp and Grunt task runner.\n* Support custom transform and flush functions.\n\n## Installation\n\n```sh\nnpm install --save-dev i18next-scanner\n```\n\nor\n\n```sh\nnpm install -g i18next-scanner\n```\n\n## Usage\n\n### CLI Usage\n\n```sh\n$ i18next-scanner\n\n  Usage: i18next-scanner [options] <file ...>\n\n\n  Options:\n\n    -V, --version      output the version number\n    --config <config>  Path to the config file (default: i18next-scanner.config.js)\n    --output <path>    Path to the output directory (default: .)\n    -h, --help         output usage information\n\n  Examples:\n\n    $ i18next-scanner --config i18next-scanner.config.js --output /path/to/output 'src/**/*.{js,jsx}'\n    $ i18next-scanner --config i18next-scanner.config.js 'src/**/*.{js,jsx}'\n    $ i18next-scanner '/path/to/src/app.js' '/path/to/assets/index.html'\n```\n\nGlobbing patterns are supported for specifying file paths:\n* `*` matches any number of characters, but not `/`\n* `?` matches a single character, but not `/`\n* `**` matches any number of characters, including `/`, as long as it's the only thing in a path part\n* `{}` allows for a comma-separated list of \"or\" expressions\n* `!` at the beginning of a pattern will negate the match\n\n_Note: Globbing patterns should be wrapped in single quotes._\n\n#### Examples\n\n* [examples/i18next-scanner.config.js](https://github.com/i18next/i18next-scanner/blob/master/examples/i18next-scanner.config.js)\n\n```js\nconst fs = require('fs');\nconst chalk = require('chalk');\n\nmodule.exports = {\n    input: [\n        'app/**/*.{js,jsx}',\n        // Use ! to filter out files or directories\n        '!app/**/*.spec.{js,jsx}',\n        '!app/i18n/**',\n        '!**/node_modules/**',\n    ],\n    output: './',\n    options: {\n        debug: true,\n        func: {\n            list: ['i18next.t', 'i18n.t'],\n            extensions: ['.js', '.jsx']\n        },\n        trans: {\n            component: 'Trans',\n            i18nKey: 'i18nKey',\n            defaultsKey: 'defaults',\n            extensions: ['.js', '.jsx'],\n            fallbackKey: function(ns, value) {\n                return value;\n            },\n\n            // https://react.i18next.com/latest/trans-component#usage-with-simple-html-elements-like-less-than-br-greater-than-and-others-v10.4.0\n            supportBasicHtmlNodes: true, // Enables keeping the name of simple nodes (e.g. <br/>) in translations instead of indexed keys.\n            keepBasicHtmlNodesFor: ['br', 'strong', 'i', 'p'], // Which nodes are allowed to be kept in translations during defaultValue generation of <Trans>.\n\n            // https://github.com/acornjs/acorn/tree/master/acorn#interface\n            acorn: {\n                ecmaVersion: 2020,\n                sourceType: 'module', // defaults to 'module'\n            }\n        },\n        lngs: ['en','de'],\n        ns: [\n            'locale',\n            'resource'\n        ],\n        defaultLng: 'en',\n        defaultNs: 'resource',\n        defaultValue: '__STRING_NOT_TRANSLATED__',\n        resource: {\n            loadPath: 'i18n/{{lng}}/{{ns}}.json',\n            savePath: 'i18n/{{lng}}/{{ns}}.json',\n            jsonIndent: 2,\n            lineEnding: '\\n'\n        },\n        nsSeparator: false, // namespace separator\n        keySeparator: false, // key separator\n        interpolation: {\n            prefix: '{{',\n            suffix: '}}'\n        },\n        metadata: {},\n        allowDynamicKeys: false,\n    },\n    transform: function customTransform(file, enc, done) {\n        \"use strict\";\n        const parser = this.parser;\n        const content = fs.readFileSync(file.path, enc);\n        let count = 0;\n\n        parser.parseFuncFromString(content, { list: ['i18next._', 'i18next.__'] }, (key, options) => {\n            parser.set(key, Object.assign({}, options, {\n                nsSeparator: false,\n                keySeparator: false\n            }));\n            ++count;\n        });\n\n        if (count > 0) {\n            console.log(`i18next-scanner: count=${chalk.cyan(count)}, file=${chalk.yellow(JSON.stringify(file.relative))}`);\n        }\n\n        done();\n    }\n};\n```\n\n### Standard API\n```js\nconst fs = require('fs');\nconst Parser = require('i18next-scanner').Parser;\n\nconst customHandler = function(key) {\n    parser.set(key, '__TRANSLATION__');\n};\n\nconst parser = new Parser();\nlet content = '';\n\n// Parse Translation Function\n// i18next.t('key');\ncontent = fs.readFileSync('/path/to/app.js', 'utf-8');\nparser\n    .parseFuncFromString(content, customHandler) // pass a custom handler\n    .parseFuncFromString(content, { list: ['i18next.t']}) // override `func.list`\n    .parseFuncFromString(content, { list: ['i18next.t']}, customHandler)\n    .parseFuncFromString(content); // use default options and handler\n\n// Parse Trans component\ncontent = fs.readFileSync('/path/to/app.jsx', 'utf-8');\nparser\n    .parseTransFromString(content, customHandler) // pass a custom handler\n    .parseTransFromString(content, { component: 'Trans', i18nKey: 'i18nKey', defaultsKey: 'defaults' })\n    .parseTransFromString(content, { fallbackKey: true }) // Uses defaultValue as the fallback key when the i18nKey attribute is missing\n    .parseTransFromString(content); // use default options and handler\n\n// Parse HTML Attribute\n// <div data-i18n=\"key\"></div>\ncontent = fs.readFileSync('/path/to/index.html', 'utf-8');\nparser\n    .parseAttrFromString(content, customHandler) // pass a custom handler\n    .parseAttrFromString(content, { list: ['data-i18n'] }) // override `attr.list`\n    .parseAttrFromString(content, { list: ['data-i18n'] }, customHandler)\n    .parseAttrFromString(content); // using default options and handler\n\nconsole.log(parser.get());\nconsole.log(parser.get({ sort: true }));\nconsole.log(parser.get('translation:key', { lng: 'en'}));\n```\n\n### Transform Stream API\nThe main entry function of [i18next-scanner](https://github.com/i18next/i18next-scanner) is a transform stream. You can use [vinyl-fs](https://github.com/wearefractal/vinyl) to create a readable stream, pipe the stream through [i18next-scanner](https://github.com/i18next/i18next-scanner) to transform your code into an i18n resource object, and write to a destination folder.\n\nHere is a simple example showing how that works:\n```js\nconst scanner = require('i18next-scanner');\nconst vfs = require('vinyl-fs');\nconst sort = require('gulp-sort');\nconst options = {\n    // See options at https://github.com/i18next/i18next-scanner#options\n};\nvfs.src(['/path/to/src'])\n    .pipe(sort()) // Sort files in stream by path\n    .pipe(scanner(options))\n    .pipe(vfs.dest('/path/to/dest'));\n```\n\nAlternatively, you can get a transform stream by calling createStream() as show below:\n```js\nvfs.src(['/path/to/src'])\n    .pipe(sort()) // Sort files in stream by path\n    .pipe(scanner.createStream(options))\n    .pipe(vfs.dest('/path/to/dest'));\n```\n\n### Gulp\nNow you are ready to set up a minimal configuration, and get started with Gulp. For example:\n```js\nconst gulp = require('gulp');\nconst sort = require('gulp-sort');\nconst scanner = require('i18next-scanner');\n\ngulp.task('i18next', function() {\n    return gulp.src(['src/**/*.{js,html}'])\n        .pipe(sort()) // Sort files in stream by path\n        .pipe(scanner({\n            lngs: ['en', 'de'], // supported languages\n            resource: {\n                // the source path is relative to current working directory\n                loadPath: 'assets/i18n/{{lng}}/{{ns}}.json',\n\n                // the destination path is relative to your `gulp.dest()` path\n                savePath: 'i18n/{{lng}}/{{ns}}.json'\n            }\n        }))\n        .pipe(gulp.dest('assets'));\n});\n```\n\n### Grunt\nOnce you've finished the installation, add this line to your project's Gruntfile:\n```js\ngrunt.loadNpmTasks('i18next-scanner');\n```\n\nIn your project's Gruntfile, add a section named `i18next` to the data object passed into `grunt.initConfig()`, like so:\n```js\ngrunt.initConfig({\n    i18next: {\n        dev: {\n            src: 'src/**/*.{js,html}',\n            dest: 'assets',\n            options: {\n                lngs: ['en', 'de'],\n                resource: {\n                    loadPath: 'assets/i18n/{{lng}}/{{ns}}.json',\n                    savePath: 'i18n/{{lng}}/{{ns}}.json'\n                }\n            }\n        }\n    }\n});\n```\n\n## API\n\nThere are two ways to use i18next-scanner:\n\n### Standard API\n```js\nconst Parser = require('i18next-scanner').Parser;\nconst parser = new Parser(options);\n\nconst code = \"i18next.t('key'); ...\";\nparser.parseFuncFromString(code);\n\nconst jsx = '<Trans i18nKey=\"some.key\">Default text</Trans>';\nparser.parseTransFromString(jsx);\n\nconst html = '<div data-i18n=\"key\"></div>';\nparser.parseAttrFromString(html);\n\nparser.get();\n```\n\n#### parser.parseFuncFromString\nParse translation key from JS function\n```js\nparser.parseFuncFromString(content)\n\nparser.parseFuncFromString(content, { list: ['_t'] });\n\nparser.parseFuncFromString(content, function(key, options) {\n    options.defaultValue = key; // use key as the value\n    parser.set(key, options);\n});\n\nparser.parseFuncFromString(content, { list: ['_t'] }, function(key, options) {\n    parser.set(key, options); // use defaultValue\n});\n```\n\n#### parser.parseTransFromString\nParse translation key from the [Trans component](https://github.com/i18next/react-i18next)\n```js\nparser.parseTransFromString(content);\n\nparser.parseTransFromString(context, { component: 'Trans', i18nKey: 'i18nKey' });\n\n// Uses defaultValue as the fallback key when the i18nKey attribute is missing\nparser.parseTransFromString(content, { fallbackKey: true });\n\nparser.parseTransFromString(content, {\n    fallbackKey: function(ns, value) {\n        // Returns a hash value as the fallback key\n        return sha1(value);\n    }\n});\n\nparser.parseTransFromString(content, function(key, options) {\n    options.defaultValue = key; // use key as the value\n    parser.set(key, options);\n});\n```\n\n#### parser.parseAttrFromString\nParse translation key from HTML attribute\n```js\nparser.parseAttrFromString(content)\n\nparser.parseAttrFromString(content, { list: ['data-i18n'] });\n\nparser.parseAttrFromString(content, function(key) {\n    const defaultValue = key; // use key as the value\n    parser.set(key, defaultValue);\n});\n\nparser.parseAttrFromString(content, { list: ['data-i18n'] }, function(key) {\n    parser.set(key); // use defaultValue\n});\n```\n\n#### parser.get\nGet the value of a translation key or the whole i18n resource store\n```js\n// Returns the whole i18n resource store\nparser.get();\n\n// Returns the resource store with the top-level keys sorted by alphabetical order\nparser.get({ sort: true });\n\n// Returns a value in fallback language (@see options.fallbackLng) with namespace and key\nparser.get('ns:key');\n\n// Returns a value with namespace, key, and lng\nparser.get('ns:key', { lng: 'en' });\n```\n\n#### parser.set\nSet a translation key with an optional defaultValue to i18n resource store\n\n```js\n// Set a translation key\nparser.set(key);\n\n// Set a translation key with default value\nparser.set(key, defaultValue);\n\n// Set a translation key with default value using options\nparser.set(key, {\n    defaultValue: defaultValue\n});\n```\n\n### Transform Stream API\n```js\nconst scanner = require('i18next-scanner');\nscanner.createStream(options, customTransform /* optional */, customFlush /* optional */);\n```\n\n#### customTransform\nThe optional `customTransform` function is provided as the 2nd argument for the transform stream API. It must have the following signature: `function (file, encoding, done) {}`. A minimal implementation should call the `done()` function to indicate that the transformation is done, even if that transformation means discarding the file.\nFor example:\n```js\nconst scanner = require('i18next-scanner');\nconst vfs = require('vinyl-fs');\nconst customTransform = function _transform(file, enc, done) {\n    const parser = this.parser;\n    const content = fs.readFileSync(file.path, enc);\n\n    // add your code\n    done();\n};\n\nvfs.src(['/path/to/src'])\n    .pipe(scanner(options, customTransform))\n    .pipe(vfs.dest('path/to/dest'));\n```\n\nTo parse a translation key, call `parser.set(key, defaultValue)` to assign the key with an optional `defaultValue`.\nFor example:\n```js\nconst customTransform = function _transform(file, enc, done) {\n    const parser = this.parser;\n    const content = fs.readFileSync(file.path, enc);\n\n    parser.parseFuncFromString(content, { list: ['i18n.t'] }, function(key) {\n        const defaultValue = '__L10N__';\n        parser.set(key, defaultValue);\n    });\n\n    done();\n};\n```\n\nAlternatively, you may call `parser.set(defaultKey, value)` to assign the value with a default key. The `defaultKey` should be unique string and can never be `null`, `undefined`, or empty.\nFor example:\n```js\nconst hash = require('sha1');\nconst customTransform = function _transform(file, enc, done) {\n    const parser = this.parser;\n    const content = fs.readFileSync(file.path, enc);\n\n    parser.parseFuncFromString(content, { list: ['i18n._'] }, function(key) {\n        const value = key;\n        const defaultKey = hash(value);\n        parser.set(defaultKey, value);\n    });\n\n    done();\n};\n```\n\n#### customFlush\nThe optional `customFlush` function is provided as the last argument for the transform stream API, it is called just prior to the stream ending. You can implement your `customFlush` function to override the default `flush` function. When everything's done, call the `done()` function to indicate the stream is finished.\nFor example:\n```js\nconst scanner = require('i18next-scanner');\nconst vfs = require('vinyl-fs');\nconst customFlush = function _flush(done) {\n    const parser = this.parser;\n    const resStore = parser.getResourceStore();\n\n    // loop over the resStore\n    Object.keys(resStore).forEach(function(lng) {\n        const namespaces = resStore[lng];\n        Object.keys(namespaces).forEach(function(ns) {\n            const obj = namespaces[ns];\n            // add your code\n        });\n    });\n\n    done();\n};\n\nvfs.src(['/path/to/src'])\n    .pipe(scanner(options, customTransform, customFlush))\n    .pipe(vfs.dest('/path/to/dest'));\n```\n\n\n## Default Options\n\nBelow are the configuration options with their default values:\n\n```javascript\n{\n    compatibilityJSON: 'v3', // One of: 'v1', 'v2', 'v3', 'v4\n    debug: false,\n    removeUnusedKeys: false,\n    sort: false,\n    attr: {\n        list: ['data-i18n'],\n        extensions: ['.html', '.htm'],\n    },\n    func: {\n        list: ['i18next.t', 'i18n.t'],\n        extensions: ['.js', '.jsx'],\n    },\n    trans: {\n        component: 'Trans',\n        i18nKey: 'i18nKey',\n        defaultsKey: 'defaults',\n        extensions: ['.js', '.jsx'],\n        fallbackKey: false,\n\n        // https://react.i18next.com/latest/trans-component#usage-with-simple-html-elements-like-less-than-br-greater-than-and-others-v10.4.0\n        supportBasicHtmlNodes: true, // Enables keeping the name of simple nodes (e.g. <br/>) in translations instead of indexed keys.\n        keepBasicHtmlNodesFor: ['br', 'strong', 'i', 'p'], // Which nodes are allowed to be kept in translations during defaultValue generation of <Trans>.\n\n        // https://github.com/acornjs/acorn/tree/master/acorn#interface\n        acorn: {\n            ecmaVersion: 2020,\n            sourceType: 'module', // defaults to 'module'\n        },\n    },\n    lngs: ['en'],\n    ns: ['translation'],\n    defaultLng: 'en',\n    defaultNs: 'translation',\n    defaultValue: '',\n    resource: {\n        loadPath: 'i18n/{{lng}}/{{ns}}.json',\n        savePath: 'i18n/{{lng}}/{{ns}}.json',\n        jsonIndent: 2,\n        lineEnding: '\\n',\n    },\n    nsSeparator: ':',\n    keySeparator: '.',\n    pluralSeparator: '_',\n    contextSeparator: '_',\n    contextDefaultValues: [],\n    interpolation: {\n        prefix: '{{',\n        suffix: '}}',\n    },\n    metadata: {},\n    allowDynamicKeys: false,\n}\n```\n\n#### compatibilityJSON\n\nType: `String` Default: `'v3'`\n\nThe `compatibilityJSON` version to use for plural suffixes.\n\nSee https://www.i18next.com/misc/json-format for details.\n\n#### debug\n\nType: `Boolean` Default: `false`\n\nSet to `true` to turn on debug output.\n\n#### removeUnusedKeys\n\nType: `Boolean` or `Function` Default: `false`\n\nSet to `true` to remove unused translation keys from i18n resource files. By default, this is set to `false`.\n```js\n{ // Default\n    removeUnusedKeys: false,\n}\n```\n\nIf a function is provided, it will be used to decide whether an unused translation key should be removed.\n```js\n// Available since 4.6.0\n//\n// @param {string} lng The language of the unused translation key.\n// @param {string} ns The namespace of the unused translation key.\n// @param {array} key The translation key in its array form.\n// @return {boolean} Returns true if the unused translation key should be removed.\nremoveUnusedKeys: function(lng, ns, key) {\n  if (ns === 'resource') {\n    return true;\n  }\n  return false;\n}\n```\n\n#### sort\n\nType: `Boolean` Default: `false`\n\nSet to `true` if you want to sort translation keys in ascending order.\n\n#### attr\n\nType: `Object` or `false`\n\nIf an `Object` is supplied, you can either specify a list of attributes and extensions, or override the default.\n```js\n{ // Default\n    attr: {\n        list: ['data-i18n'],\n        extensions: ['.html', '.htm']\n    }\n}\n```\n\nYou can set `attr` to `false` to disable parsing attribute as below:\n```js\n{\n    attr: false\n}\n```\n\n#### func\n\nType: `Object` or `false`\n\nIf an `Object` is supplied, you can either specify a list of translation functions and extensions, or override the default.\n```js\n{ // Default\n    func: {\n        list: ['i18next.t', 'i18n.t'],\n        extensions: ['.js', '.jsx']\n    }\n}\n```\n\nYou can set `func` to `false` to disable parsing translation function as below:\n```js\n{\n    func: false\n}\n```\n\n#### trans\n\nType: `Object` or `false`\n\nIf an `Object` is supplied, you can specify a list of extensions, or override the default.\n```js\n{ // Default\n    trans: {\n        component: 'Trans',\n        i18nKey: 'i18nKey',\n        defaultsKey: 'defaults',\n        extensions: ['.js', '.jsx'],\n        fallbackKey: false,\n\n        // https://react.i18next.com/latest/trans-component#usage-with-simple-html-elements-like-less-than-br-greater-than-and-others-v10.4.0\n        supportBasicHtmlNodes: true, // Enables keeping the name of simple nodes (e.g. <br/>) in translations instead of indexed keys.\n        keepBasicHtmlNodesFor: ['br', 'strong', 'i', 'p'], // Which nodes are allowed to be kept in translations during defaultValue generation of <Trans>.\n\n        // https://github.com/acornjs/acorn/tree/master/acorn#interface\n        acorn: {\n            ecmaVersion: 2020,\n            sourceType: 'module', // defaults to 'module'\n        },\n    }\n}\n```\n\nYou can set `trans` to `false` to disable parsing Trans component as below:\n```js\n{\n    trans: false\n}\n```\n\nThe `fallbackKey` can either be a boolean value, or a function like so:\n```js\nfallbackKey: function(ns, value) {\n    // Returns a hash value as the fallback key\n    return sha1(value);\n}\n```\n\nYou can pass RexExp to `trans.component` in case you want to match multiple things:\n```js\ncomponent: /Trans$/\n```\n\n#### lngs\n\nType: `Array` Default: `['en']`\n\nAn array of supported languages.\n\n#### ns\n\nType: `String` or `Array` Default: `['translation']`\n\nA namespace string or an array of namespaces.\n\n#### defaultLng\n\nType: `String` Default: `'en'`\n\nThe default language used for checking default values.\n\n#### defaultNs\n\nType: `String` Default: `'translation'`\n\nThe default namespace used if not passed to translation function.\n\n#### defaultValue\n\nType: `String` or `Function` Default: `''`\n\nThe default value used if not passed to `parser.set`.\n\n##### Examples\nProvides the default value with a string:\n```js\n{\n    defaultValue: '__NOT_TRANSLATED__'\n}\n```\n\nProvides the default value as a callback function:\n```js\n{\n    // @param {string} lng The language currently used.\n    // @param {string} ns The namespace currently used.\n    // @param {string} key The translation key.\n    // @return {string} Returns a default value for the translation key.\n    defaultValue: function(lng, ns, key) {\n        if (lng === 'en') {\n            // Return key as the default value for English language\n            return key;\n        }\n        // Return the string '__NOT_TRANSLATED__' for other languages\n        return '__NOT_TRANSLATED__';\n    }\n}\n```\n\n#### resource\n\nType: `Object`\n\nResource options:\n```js\n{ // Default\n    resource: {\n        // The path where resources get loaded from. Relative to current working directory.\n        loadPath: 'i18n/{{lng}}/{{ns}}.json',\n\n        // The path to store resources. Relative to the path specified by `gulp.dest(path)`.\n        savePath: 'i18n/{{lng}}/{{ns}}.json',\n\n        // Specify the number of space characters to use as white space to insert into the output JSON string for readability purpose.\n        jsonIndent: 2,\n\n        // Normalize line endings to '\\r\\n', '\\r', '\\n', or 'auto' for the current operating system. Defaults to '\\n'.\n        // Aliases: 'CRLF', 'CR', 'LF', 'crlf', 'cr', 'lf'\n        lineEnding: '\\n'\n    }\n}\n```\n\n`loadPath` and `savePath` can be both be defined as `Function` with parameters `lng` and `ns`\n\n```js\n{ // Default\n    resource: {\n        // The path where resources get loaded from. Relative to current working directory.\n        loadPath: function(lng, ns) {\n            return 'i18n/'+lng+'/'+ns+'.json';\n        },\n\n        // The path to store resources. Relative to the path specified by `gulp.dest(path)`.\n        savePath: function(lng, ns) {\n            return 'i18n/'+lng+'/'+ns+'.json';\n        },\n\n        // Specify the number of space characters to use as white space to insert into the output JSON string for readability purpose.\n        jsonIndent: 2,\n\n        // Normalize line endings to '\\r\\n', '\\r', '\\n', or 'auto' for the current operating system. Defaults to '\\n'.\n        // Aliases: 'CRLF', 'CR', 'LF', 'crlf', 'cr', 'lf'\n        lineEnding: '\\n'\n    }\n}\n```\n\n#### keySeparator\n\nType: `String` or `false` Default: `'.'`\n\nKey separator used in translation keys.\n\nSet to `false` to disable key separator if you prefer having keys as the fallback for translation (e.g. gettext). This feature is supported by [i18next@2.1.0](https://github.com/i18next/i18next/blob/master/CHANGELOG.md#210). Also see <strong>Key based fallback</strong> at https://www.i18next.com/principles/fallback#key-fallback.\n\n#### nsSeparator\n\nType: `String` or `false` Default: `':'`\n\nNamespace separator used in translation keys.\n\nSet to `false` to disable namespace separator if you prefer having keys as the fallback for translation (e.g. gettext). This feature is supported by [i18next@2.1.0](https://github.com/i18next/i18next/blob/master/CHANGELOG.md#210). Also see <strong>Key based fallback</strong> at https://www.i18next.com/principles/fallback#key-fallback.\n\n#### context\n\nType: `Boolean` or `Function` Default: `true`\n\nWhether to add context form key.\n\n```js\ncontext: function(lng, ns, key, options) {\n    return true;\n}\n```\n\n#### contextFallback\n\nType: `Boolean` Default: `true`\n\nWhether to add a fallback key as well as the context form key.\n\n#### contextSeparator\n\nType: `String` Default: `'_'`\n\nThe character to split context from key.\n\n#### contextDefaultValues\n\nType: `Array` Default: `[]`\n\nA list of default context values, used when the scanner encounters dynamic value as a `context`.\nFor a list of `['male', 'female']` the scanner will generate an entry for each value.\n\n#### plural\n\nType: `Boolean` or `Function` Default: `true`\n\nWhether to add plural form key.\n\n```js\nplural: function(lng, ns, key, options) {\n    return true;\n}\n```\n\n#### pluralFallback\n\nType: `Boolean` Default: `true`\n\nWhether to add a fallback key as well as the plural form key.\n\n#### pluralSeparator\n\nType: `String` Default: `'_'`\n\nThe character to split plural from key.\n\n#### interpolation\n\nType: `Object`\n\ninterpolation options\n```js\n{ // Default\n    interpolation: {\n        // The prefix for variables\n        prefix: '{{',\n\n        // The suffix for variables\n        suffix: '}}'\n    }\n}\n```\n\n#### metadata\n\nType: `Object` Default: `{}`\n\nThis can be used to pass any additional information regarding the string.\n\n#### allowDynamicKeys\n\nType: `Boolean` Default: `false`\n\nThis can be used to allow dynamic keys e.g. `friend${DynamicValue}`\n\nExample Usage:\n\n```\n  transform: function customTransform(file, enc, done) {\n    'use strict';\n    const parser = this.parser;\n\n    const contexts = {\n      compact: ['compact'],\n      max: ['Max'],\n    };\n\n    const keys = {\n        difficulty: { list: ['Normal', 'Hard'] },\n        minMax: { list: ['Min', 'Max'] },\n    };\n\n    const content = fs.readFileSync(file.path, enc);\n\n    parser.parseFuncFromString(content, { list: ['i18next.t', 'i18n.t'] }, (key, options) => {\n      // Add context based on metadata\n      if (options.metadata?.context) {\n        delete options.context;\n        const context = contexts[options.metadata?.context];\n        parser.set(key, options);\n        for (let i = 0; i < context?.length; i++) {\n          parser.set(`${key}${parser.options.contextSeparator}${context[i]}`, options);\n        }\n      }\n\n      // Add keys based on metadata (dynamic or otherwise)\n      if (options.metadata?.keys) {\n        const list = keys[options.metadata?.keys].list;\n        for (let i = 0; i < list?.length; i++) {\n          parser.set(`${key}${list[i]}`, options);\n        }\n      }\n\n      // Add all other non-metadata related keys\n      if (!options.metadata) {\n        parser.set(key, options);\n      }\n    });\n\n    done();\n```\n\n## Integration Guide\nCheckout [Integration Guide](https://github.com/i18next/i18next-scanner/wiki/Integration-Guide) to learn how to integrate with [React](https://github.com/i18next/i18next-scanner/wiki/Integration-Guide#react), [Gettext Style I18n](https://github.com/i18next/i18next-scanner/wiki/Integration-Guide#gettext-style-i18n), and [Handlebars](https://github.com/i18next/i18next-scanner/wiki/Integration-Guide#handlebars).\n\n## License\n\nMIT\n","readmeFilename":"README.md"}