Gathering detailed insights and metrics for i18next-parser
Gathering detailed insights and metrics for i18next-parser
Gathering detailed insights and metrics for i18next-parser
Gathering detailed insights and metrics for i18next-parser
i18next-parser-workspaces
Run i18next-parser over all source files in your TypeScript project
handlebars-i18next-parser
Parser/lexer for the combination of handlebars-i18next and i18next-parser
i18next
i18next internationalization framework
react-i18next
Internationalization for react done right. Using the i18next i18n ecosystem.
Parse your code to extract translation keys/values and manage your catalog files
npm install i18next-parser
Module System
Min. Node Version
Typescript Support
Node Version
NPM Version
487 Stars
1,223 Commits
199 Forks
6 Watching
17 Branches
100 Contributors
Updated on 26 Nov 2024
JavaScript (98.03%)
HTML (0.76%)
TypeScript (0.71%)
Handlebars (0.5%)
Cumulative downloads
Total Downloads
Last day
6%
47,263
Compared to previous day
Last week
3.9%
229,297
Compared to previous week
Last month
14.8%
952,317
Compared to previous month
Last year
13.8%
9,818,745
Compared to previous year
17
When translating an application, maintaining the translation catalog by hand is painful. This package parses your code and automates this process.
Finally, if you want to make this process even less painful, I invite you to check Locize. They are a sponsor of this project. Actually, if you use this package and like it, supporting me on Patreon would mean a great deal!
namespace_old.json
catalog_old
file if the one in the translation file is emptykey_context
key_zero
, key_one
, key_two
, key_few
, key_many
and key_other
as described hereYou can find information about major releases on the dedicated page. The migration documentation will help you figure out the breaking changes between versions.
9.x
is tested on Node 18, 20 and 22.8.x
is tested on Node 16, 18 and 20.7.x
is tested on Node 14, 16 and 18.6.x
is tested on Node 14 and 16.You can use the CLI with the package installed locally but if you want to use it from anywhere, you better install it globally:
yarn global add i18next-parser
npm install -g i18next-parser
i18next 'app/**/*.{js,hbs}' 'lib/**/*.{js,hbs}' [-oc]
Multiple globbing patterns are supported to specify complex file selections. You can learn how to write globs here. Note that glob must be wrapped with single quotes when passed as arguments.
IMPORTANT NOTE: If you pass the globs as CLI argument, they must be relative to where you run the command (aka relative to process.cwd()
). If you pass the globs via the input
option of the config file, they must be relative to the config file.
Save the package to your devDependencies:
yarn add -D i18next-parser
npm install --save-dev i18next-parser
Gulp defines itself as the streaming build system. Put simply, it is like Grunt, but performant and elegant.
1import { gulp as i18nextParser } from 'i18next-parser' 2 3gulp.task('i18next', function () { 4 gulp 5 .src('app/**') 6 .pipe( 7 new i18nextParser({ 8 locales: ['en', 'de'], 9 output: 'locales/$LOCALE/$NAMESPACE.json', 10 }) 11 ) 12 .pipe(gulp.dest('./')) 13})
IMPORTANT: output
is required to know where to read the catalog from. You might think that gulp.dest()
is enough though it does not inform the transform where to read the existing catalog from.
Save the package to your devDependencies:
yarn add -D i18next-parser
npm install --save-dev i18next-parser
Broccoli.js defines itself as a fast, reliable asset pipeline, supporting constant-time rebuilds and compact build definitions.
1import Funnel from 'broccoli-funnel' 2import { broccoli as i18nextParser } from 'i18next-parser' 3 4const appRoot = 'broccoli' 5 6let i18n = new Funnel(appRoot, { 7 files: ['handlebars.hbs', 'javascript.js'], 8 annotation: 'i18next-parser', 9}) 10 11i18n = new i18nextParser([i18n], { 12 output: 'broccoli/locales/$LOCALE/$NAMESPACE.json', 13}) 14 15export default i18n
Note: You may need to configure Broccoli to place temporary files (option: tmpdir) within the current working directory as I18next-parser does not traverse down beyond that.
Using a config file gives you fine-grained control over how i18next-parser treats your files. Here's an example config showing all config options with their defaults.
1// i18next-parser.config.js 2 3export default { 4 contextSeparator: '_', 5 // Key separator used in your translation keys 6 7 createOldCatalogs: true, 8 // Save the \_old files 9 10 defaultNamespace: 'translation', 11 // Default namespace used in your i18next config 12 13 defaultValue: '', 14 // Default value to give to keys with no value 15 // You may also specify a function accepting the locale, namespace, key, and value as arguments 16 17 indentation: 2, 18 // Indentation of the catalog files 19 20 keepRemoved: false, 21 // Keep keys from the catalog that are no longer in code 22 // You may either specify a boolean to keep or discard all removed keys. 23 // You may also specify an array of patterns: the keys from the catalog that are no long in the code but match one of the patterns will be kept. 24 // The patterns are applied to the full key including the namespace, the parent keys and the separators. 25 26 keySeparator: '.', 27 // Key separator used in your translation keys 28 // If you want to use plain english keys, separators such as `.` and `:` will conflict. You might want to set `keySeparator: false` and `namespaceSeparator: false`. That way, `t('Status: Loading...')` will not think that there are a namespace and three separator dots for instance. 29 30 // see below for more details 31 lexers: { 32 hbs: ['HandlebarsLexer'], 33 handlebars: ['HandlebarsLexer'], 34 35 htm: ['HTMLLexer'], 36 html: ['HTMLLexer'], 37 38 mjs: ['JavascriptLexer'], 39 js: ['JavascriptLexer'], // if you're writing jsx inside .js files, change this to JsxLexer 40 ts: ['JavascriptLexer'], 41 jsx: ['JsxLexer'], 42 tsx: ['JsxLexer'], 43 44 default: ['JavascriptLexer'], 45 }, 46 47 lineEnding: 'auto', 48 // Control the line ending. See options at https://github.com/ryanve/eol 49 50 locales: ['en', 'fr'], 51 // An array of the locales in your applications 52 53 namespaceSeparator: ':', 54 // Namespace separator used in your translation keys 55 // If you want to use plain english keys, separators such as `.` and `:` will conflict. You might want to set `keySeparator: false` and `namespaceSeparator: false`. That way, `t('Status: Loading...')` will not think that there are a namespace and three separator dots for instance. 56 57 output: 'locales/$LOCALE/$NAMESPACE.json', 58 // Supports $LOCALE and $NAMESPACE injection 59 // Supports JSON (.json) and YAML (.yml) file formats 60 // Where to write the locale files relative to process.cwd() 61 62 pluralSeparator: '_', 63 // Plural separator used in your translation keys 64 // If you want to use plain english keys, separators such as `_` might conflict. You might want to set `pluralSeparator` to a different string that does not occur in your keys. 65 // If you don't want to generate keys for plurals (for example, in case you are using ICU format), set `pluralSeparator: false`. 66 67 input: undefined, 68 // An array of globs that describe where to look for source files 69 // relative to the location of the configuration file 70 71 sort: false, 72 // Whether or not to sort the catalog. Can also be a [compareFunction](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#parameters) 73 74 verbose: false, 75 // Display info about the parsing including some stats 76 77 failOnWarnings: false, 78 // Exit with an exit code of 1 on warnings 79 80 failOnUpdate: false, 81 // Exit with an exit code of 1 when translations are updated (for CI purpose) 82 83 customValueTemplate: null, 84 // If you wish to customize the value output the value as an object, you can set your own format. 85 // 86 // - ${defaultValue} is the default value you set in your translation function. 87 // - ${filePaths} will be expanded to an array that contains the absolute 88 // file paths where the translations originated in, in case e.g., you need 89 // to provide translators with context 90 // 91 // Any other custom property will be automatically extracted from the 2nd 92 // argument of your `t()` function or tOptions in <Trans tOptions={...} /> 93 // 94 // Example: 95 // For `t('my-key', {maxLength: 150, defaultValue: 'Hello'})` in 96 // /path/to/your/file.js, 97 // 98 // Using the following customValueTemplate: 99 // 100 // customValueTemplate: { 101 // message: "${defaultValue}", 102 // description: "${maxLength}", 103 // paths: "${filePaths}", 104 // } 105 // 106 // Will result in the following item being extracted: 107 // 108 // "my-key": { 109 // "message": "Hello", 110 // "description": 150, 111 // "paths": ["/path/to/your/file.js"] 112 // } 113 114 resetDefaultValueLocale: null, 115 // The locale to compare with default values to determine whether a default value has been changed. 116 // If this is set and a default value differs from a translation in the specified locale, all entries 117 // for that key across locales are reset to the default value, and existing translations are moved to 118 // the `_old` file. 119 120 i18nextOptions: null, 121 // If you wish to customize options in internally used i18next instance, you can define an object with any 122 // configuration property supported by i18next (https://www.i18next.com/overview/configuration-options). 123 // { compatibilityJSON: 'v3' } can be used to generate v3 compatible plurals. 124 125 yamlOptions: null, 126 // If you wish to customize options for yaml output, you can define an object here. 127 // Configuration options are here (https://github.com/nodeca/js-yaml#dump-object---options-). 128 // Example: 129 // { 130 // lineWidth: -1, 131 // } 132}
The lexers
option let you configure which Lexer to use for which extension. Here is the default:
Note the presence of a default
which will catch any extension that is not listed.
There are 4 lexers available: HandlebarsLexer
, HTMLLexer
, JavascriptLexer
and
JsxLexer
. Each has configurations of its own. Typescript is supported via JavascriptLexer
and JsxLexer
.
If you need to change the defaults, you can do it like so:
The Javascript lexer uses Typescript compiler to walk through your code and extract translation functions.
The default configuration is below:
1{ 2 // JavascriptLexer default config (js, mjs) 3 js: [{ 4 lexer: 'JavascriptLexer', 5 functions: ['t'], // Array of functions to match 6 namespaceFunctions: ['useTranslation', 'withTranslation'], // Array of functions to match for namespace 7 }], 8}
The JSX lexer builds off of the Javascript lexer and extends it with support for JSX syntax.
Default configuration:
1{ 2 // JsxLexer default config (jsx) 3 // JsxLexer can take all the options of the JavascriptLexer plus the following 4 jsx: [{ 5 lexer: 'JsxLexer', 6 attr: 'i18nKey', // Attribute for the keys 7 componentFunctions: ['Trans'], // Array of components to match 8 }], 9}
If your JSX files have .js
extension (e.g. create-react-app projects) you should override the default js
lexer with JsxLexer
to enable jsx parsing from js files:
1{ 2 js: [{ 3 lexer: 'JsxLexer' 4 }], 5}
If you're working with i18next in Typescript, you might be using a helper function to make sure that objects in
1const SomeComponent = (props) => ( 2 <Trans> 3 Visit 4 <Link to='/user/john'>{castAsString({ name: props.name })}'s profile</Link> 5 {/* Equivalent to, but resolves typechecker errors with */} 6 <Link to='/user/john'>{{ name: props.name }}'s profile</Link> 7 </Trans> 8) 9 10function castAsString(record: Record<string, unknown>): string { 11 return record as unknown as string 12}
In order for the parser to extract variables properly, you can add a list of such functions to the lexer options:
{
js: [{
lexer: 'JsxLexer',
transIdentityFunctionsToIgnore: ['castAsString']
}],
}
Typescript is supported via Javascript and Jsx lexers. If you are using Javascript syntax (e.g. with React), follow the steps in Jsx section, otherwise Javascript section.
1{ 2 // HandlebarsLexer default config (hbs, handlebars) 3 handlebars: [ 4 { 5 lexer: 'HandlebarsLexer', 6 functions: ['t'], // Array of functions to match 7 }, 8 ] 9}
1{ 2 // HTMLLexer default config (htm, html) 3 html: [{ 4 lexer: 'HTMLLexer', 5 attr: 'data-i18n' // Attribute for the keys 6 optionAttr: 'data-i18n-options' // Attribute for the options 7 }] 8}
You can provide function instead of string as a custom lexer.
1import CustomJsLexer from './CustomJsLexer'; 2 3// ... 4{ 5 js: [CustomJsLexer], 6 jsx: [{ 7 lexer: CustomJsLexer, 8 customOption: true // Custom attribute passed to CustomJsLexer class constructor 9 }] 10} 11// ...
While i18next extracts translation keys in runtime, i18next-parser doesn't run the code, so it can't interpolate values in these expressions:
t(key)
t('key' + id)
t(`key${id}`)
As a workaround you should specify possible static values in comments anywhere in your file:
// t('key_1')
// t('key_2')
t(key)
/*
t('key1')
t('key2')
*/
t('key' + id)
The transform emits a reading
event for each file it parses:
.pipe( i18next().on('reading', (file) => {}) )
The transform emits a error:json
event if the JSON.parse on json files fail:
.pipe( i18next().on('error:json', (path, error) => {}) )
The transform emits a warning
event if the file has a key that is not a string litteral or an option object with a spread operator:
.pipe( i18next().on('warning', (path, key) => {}) )
Here is a list of the warnings:
t(variable)
, the parser will throw a warning.t('key', {defaultValue: 'foo'})
and t('key', {defaultValue: 'bar'})
. The parser will select the latest one.t('parent', {defaultValue: 'foo'})
and t('parent.child', {defaultValue: 'bar'})
. parent
is both a translation and an object for child
.Any contribution is welcome. Please read the guidelines first.
Thanks a lot to all the previous contributors.
If you use this package and like it, supporting me on Patreon is another great way to contribute!
No vulnerabilities found.
Reason
no dangerous workflow patterns detected
Reason
no binaries found in the repo
Reason
license file detected
Details
Reason
Found 6/8 approved changesets -- score normalized to 7
Reason
5 existing vulnerabilities detected
Details
Reason
0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0
Reason
detected GitHub workflow tokens with excessive permissions
Details
Reason
dependency not pinned by hash detected -- score normalized to 0
Details
Reason
no effort to earn an OpenSSF best practices badge detected
Reason
project is not fuzzed
Details
Reason
branch protection not enabled on development/release branches
Details
Reason
security policy file not detected
Details
Reason
SAST tool is not run on all commits -- score normalized to 0
Details
Score
Last Scanned on 2024-11-25
The Open Source Security Foundation is a cross-industry collaboration to improve the security of open source software (OSS). The Scorecard provides security health metrics for open source projects.
Learn More