i18next-parser

Command Line tool for i18next

i18next Parser

A simple command line and gulp plugin that lets you parse your code and extract the translations keys in it.

The idea is to parse code files to retrieve the translation keys and create a catalog. You can use the command line or run in the background with Gulp while coding. It removes the pain of maintaining your translation catalog.

  • Parses a single file or a directory (recursively or not)
  • Parses template files (support for jade, handlebars and data-i18n attribute in html)
  • Creates one json file per locale and per namespace.
  • Remove old keys your code doesn't use anymore and place them in a namespace_old.json file. It is usefull to avoid losing translations you may want to reuse.
  • Restore keys from the _old file if the one in the translation file is empty.
  • Support most i18next features:
    • Handles context keys of the form key_context
    • Handles plural keys of the form key_plural and key_plural_0
    • Handles multiline array in catalog
  • Is a stream transform (so it works with gulp)

npm install i18next-parser -g
mocha --reporter nyan test/test.js

Any contribution is welcome. Just follow those quick guidelines:

  1. Fork and clone the project
  2. Create a branch git checkout -b feature/my-feature (use feature/ or hotfix/ branch prefixes accordingly)
  3. Push to your fork
  4. Make a pull request from your repository feature/my-feature branch to this repository master. Do not create both an issue ticket and a Pull Request.
  5. Wait, I am usually pretty fast to merge PRs :)

Thanks a lot to all the previous contributors.


i18next /path/to/file/or/dir [-orpfnl]

  • -o, --output : Where to write the locale files.
  • -r, --recursive: Is --output is a directory, parses files in sub directories.
  • -f, --function : Function names to parse. Defaults to t
  • -p, --parser : A custom regex for the parser to use.
  • -n, --namespace : Default namespace used in your i18next config. Defaults to translation
  • -s, --namespace-separator : Namespace separator used in your translation keys. Defaults to :
  • -k, --key-separator : Key separator used in your translation keys. Defaults to .
  • -l, --locales : The locales in your applications. Defaults to en,fr
  • --directoryFilter: Globs of folders to filter
  • --fileFilter: Globs of files to filter

Gulp defines itself as the streaming build system. Put simply, it is like Grunt, but performant and elegant.

var i18next = require('i18next-parser');
 
gulp.task('i18next', function() {
    gulp.src('app/**')
        .pipe(i18next({
            locales: ['en', 'de'],
            functions: ['__', '_e'],
            output: '../locales'
        }))
        .pipe(gulp.dest('locales'));
});
  • output: Where to write the locale files relative to the base (here app/). Defaults to locales
  • functions: An array of functions names to parse. Defaults to ['t']
  • namespace: Default namespace used in your i18next config. Defaults to translation
  • namespaceSeparator: Namespace separator used in your translation keys. Defaults to :
  • keySeparator: Key separator used in your translation keys. Defaults to .
  • locales: An array of the locales in your applications. Defaults to ['en','fr']
  • parser: A custom regex for the parser to use.

The way gulp works, it take a src(), applies some transformations to the files matched and then render the transformation using the dest() command to a path of your choice. With i18next-parser, the src() takes the path to the files to parse and the dest() takes the path where you want the catalogs of translation keys.

The problem is that the i18next() transform doesn't know about the path you specify in dest(). So it doesn't know where the catalogs are. So it can't merge the result of the parsing with the existing catalogs you may have there.

gulp.src('app/**')
    .pipe(i18next())
    .pipe(gulp.dest('custom/path'));

If you consider the code above, any file that match the app/** pattern will have of base set to app/. As per the vinyl-fs documentation (which powers gulp), the base is the folder relative to the cwd and defaults is where the glob begins.

Bare with me, the output option isn't defined, it defaults to locales. So the i18next() transform will look for files in the app/locales directory (the base plus the output directory). But in reality they are located in custom/path. So for the i18next-parser to find your catalogs, you need the output option:

gulp.src('app/**')
    .pipe(i18next({output: '../custom/path'}))
    .pipe(gulp.dest('custom/path'));

The output option is relative to the base. In our case, we have app/ as a base and we want custom/path. So the output option must be ../custom/path.

The transform emit a reading event for each file it parses:

.pipe( i18next().on('reading', function(path) { }) )

The transform emit a writing event for each file it passes to the stream:

.pipe( i18next().on('reading', function(path) { }) )

The transform emit a json_error event if the JSON.parse on json files fail. It is passed the error name (like SyntaxError) and the error message (like Unexpected token }):

.pipe( i18next().on('reading', function(name, message) { }) )


Change the output directory (cli and gulp)

Command line (the options are identical):

i18next /path/to/file/or/dir -o /output/directory

i18next /path/to/file/or/dir:/output/directory

Gulp:

.pipe(i18next({output: 'translations'}))

It will create the file in the specified folder (in case of gulp it doesn't actually create the files until you call dest()):

/output/directory/en/translation.json
...

Change the locales (cli and gulp)

Command line:

i18next /path/to/file/or/dir -l en,de,sp

Gulp:

.pipe(i18next({locales: ['en', 'de', 'sp']}))

This will create a directory per locale in the output folder:

locales/en/...
locales/de/...
locales/sp/...

Change the default namespace (cli and gulp)

Command line:

i18next /path/to/file/or/dir -n my_default_namespace

Gulp:

.pipe(i18next({namespace: 'my_default_namespace'}))

This will add all the translation from the default namespace in the following file:

locales/en/my_default_namespace.json
...

Change the namespace and key separators (cli and gulp)

Command line:

i18next /path/to/file/or/dir -s '?' -k '_'

Gulp:

.pipe(i18next({namespaceSeparator: '?', keySeparator: '_'}))

This parse the translation keys as follow:

namespace?key_subkey
 
namespace.json
{
    key: {
        subkey: ''
    }
}
...

Change the translation functions (cli and gulp)

Command line:

i18next /path/to/file/or/dir -f __,_e

Gulp:

.pipe(i18next({functions: ['__', '_e']}))

This will parse any of the following function calls in your code and extract the key:

__('key'
__ 'key'
__("key"
__ "key"
_e('key'
_e 'key'
_e("key"
_e "key"

Note1: we don't match the closing parenthesis as you may want to pass arguments to your translation function.

Note2: the parser is smart about escaped single or double quotes you may have in your key.

Change the regex (cli and gulp)

Command line:

i18next /path/to/file/or/dir -p "(.*)"

Gulp:

.pipe(i18next({parser: '(.*)'}))

If you use a custom regex, the functions option will be ignored. You need to write you regex to parse the functions you want parsed.

You must pass the regex as a string. That means that you will have to properly escape it. Also, the parser will consider the translation key to be the first truthy match of the regex; it means that you must use non capturing blocks (?:) for anything that is not the translation key.

The regex used by default is:

[^a-zA-Z0-9_](?:t)(?:\\(|\\s)\\s*(?:(?:\'((?:(?:\\\\\')?[^\']+)+[^\\\\])\')|(?:"((?:(?:\\\\")?[^"]+)+[^\\\\])"))/g

Filter files and folders (cli)

i18next /path/to/file/or/dir --fileFilter '*.hbs,*.js' --directoryFilter '!.git'

In recursive mode, it will parse *.hbs and *.js files and skip .git folder. This options is passed to readdirp. To learn more, read their documentation.