{"id":13527161,"url":"https://github.com/i18next/i18next-scanner","last_synced_at":"2025-10-06T23:13:51.159Z","repository":{"id":28872057,"uuid":"32396318","full_name":"i18next/i18next-scanner","owner":"i18next","description":"Scan your code, extract translation keys/values, and merge them into i18n resource files.","archived":false,"fork":false,"pushed_at":"2024-10-14T15:30:04.000Z","size":567,"stargazers_count":645,"open_issues_count":61,"forks_count":134,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-09-20T08:41:53.149Z","etag":null,"topics":["cli","i18n","i18next","scanner","stream","translation"],"latest_commit_sha":null,"homepage":"http://i18next.github.io/i18next-scanner","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/i18next.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":null,"patreon":null,"open_collective":"cheton","ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"custom":null}},"created_at":"2015-03-17T13:53:08.000Z","updated_at":"2025-09-16T08:17:37.000Z","dependencies_parsed_at":"2024-01-13T22:47:50.218Z","dependency_job_id":"33c29c3c-26a1-4a50-ba3e-3755e08a4ac0","html_url":"https://github.com/i18next/i18next-scanner","commit_stats":{"total_commits":474,"total_committers":46,"mean_commits":"10.304347826086957","dds":0.2573839662447257,"last_synced_commit":"f7203145829f2fce240e6660e03558027e1ba9e9"},"previous_names":["cheton/i18next-scanner"],"tags_count":93,"template":false,"template_full_name":null,"purl":"pkg:github/i18next/i18next-scanner","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/i18next%2Fi18next-scanner","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/i18next%2Fi18next-scanner/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/i18next%2Fi18next-scanner/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/i18next%2Fi18next-scanner/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/i18next","download_url":"https://codeload.github.com/i18next/i18next-scanner/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/i18next%2Fi18next-scanner/sbom","scorecard":{"id":476185,"data":{"date":"2025-08-11","repo":{"name":"github.com/i18next/i18next-scanner","commit":"138825a2adf93da158baa049a2d83d3033eddf08"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":4.2,"checks":[{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Code-Review","score":4,"reason":"Found 14/30 approved changesets -- score normalized to 4","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/node.js.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/node.js.yml:14: update your workflow using https://app.stepsecurity.io/secureworkflow/i18next/i18next-scanner/node.js.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/node.js.yml:16: update your workflow using https://app.stepsecurity.io/secureworkflow/i18next/i18next-scanner/node.js.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/node.js.yml:23: update your workflow using https://app.stepsecurity.io/secureworkflow/i18next/i18next-scanner/node.js.yml/master?enable=pin","Info:   0 out of   2 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   1 third-party GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 16 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-19T15:22:09.439Z","repository_id":28872057,"created_at":"2025-08-19T15:22:09.439Z","updated_at":"2025-08-19T15:22:09.439Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278693086,"owners_count":26029435,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-06T02:00:05.630Z","response_time":65,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cli","i18n","i18next","scanner","stream","translation"],"created_at":"2024-08-01T06:01:42.338Z","updated_at":"2025-10-06T23:13:51.130Z","avatar_url":"https://github.com/i18next.png","language":"JavaScript","funding_links":["https://opencollective.com/cheton"],"categories":["JavaScript","I18n"],"sub_categories":["React Components"],"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\u0026service=github)](https://coveralls.io/github/i18next/i18next-scanner?branch=master)\n\n[![NPM](https://nodei.co/npm/i18next-scanner.png?downloads=true\u0026stars=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\u003cTrans i18nKey=\"some.key\"\u003eDefault text\u003c/Trans\u003e\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 \u003cb\u003eTrans\u003c/b\u003e 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] \u003cfile ...\u003e\n\n\n  Options:\n\n    -V, --version      output the version number\n    --config \u003cconfig\u003e  Path to the config file (default: i18next-scanner.config.js)\n    --output \u003cpath\u003e    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. \u003cbr/\u003e) 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 \u003cTrans\u003e.\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) =\u003e {\n            parser.set(key, Object.assign({}, options, {\n                nsSeparator: false,\n                keySeparator: false\n            }));\n            ++count;\n        });\n\n        if (count \u003e 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// \u003cdiv data-i18n=\"key\"\u003e\u003c/div\u003e\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 = '\u003cTrans i18nKey=\"some.key\"\u003eDefault text\u003c/Trans\u003e';\nparser.parseTransFromString(jsx);\n\nconst html = '\u003cdiv data-i18n=\"key\"\u003e\u003c/div\u003e';\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. \u003cbr/\u003e) 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 \u003cTrans\u003e.\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. \u003cbr/\u003e) 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 \u003cTrans\u003e.\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 \u003cstrong\u003eKey based fallback\u003c/strong\u003e 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 \u003cstrong\u003eKey based fallback\u003c/strong\u003e 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) =\u003e {\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 \u003c 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 \u003c 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","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fi18next%2Fi18next-scanner","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fi18next%2Fi18next-scanner","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fi18next%2Fi18next-scanner/lists"}