Command-line interface for scanning files and extracting translation keys. The CLI tool supports glob patterns, configuration files, and flexible output options for batch processing of translation key extraction.
i18next-scanner [options] <file ...>
Options:
--config <config> Path to the config file (default: i18next-scanner.config.js)
--output <path> Path to the output directory (default: .)
-V, --version output the version number
-h, --help output usage informationUsage Examples:
# Scan single file
i18next-scanner src/app.js
# Scan multiple files
i18next-scanner src/app.js src/utils.js public/index.html
# Scan with glob patterns (use quotes)
i18next-scanner 'src/**/*.{js,jsx}'
# Scan with custom config
i18next-scanner --config my-scanner.config.js 'src/**/*.js'
# Scan with custom output directory
i18next-scanner --output ./translations 'src/**/*.js'
# Combined options
i18next-scanner --config custom.config.js --output ./i18n 'src/**/*.{js,jsx,ts,tsx}'The CLI uses a JavaScript configuration file that exports an object with input files, output directory, and parser options.
// i18next-scanner.config.js structure
module.exports = {
input: string | string[]; // Input file patterns
output: string; // Output directory path
options: ParserOptions; // Parser configuration
transform?: CustomTransform; // Optional custom transform function
flush?: CustomFlush; // Optional custom flush function
}Usage Examples:
// i18next-scanner.config.js
const fs = require('fs');
const chalk = require('chalk');
module.exports = {
input: [
'src/**/*.{js,jsx,ts,tsx}',
'public/**/*.html',
// Exclude test files and node_modules
'!src/**/*.spec.{js,jsx,ts,tsx}',
'!src/**/*.test.{js,jsx,ts,tsx}',
'!**/node_modules/**'
],
output: './locales',
options: {
debug: true,
sort: true,
func: {
list: ['i18next.t', 'i18n.t', 't', '__', '_t'],
extensions: ['.js', '.jsx', '.ts', '.tsx']
},
trans: {
component: 'Trans',
i18nKey: 'i18nKey',
defaultsKey: 'defaults',
extensions: ['.jsx', '.tsx'],
fallbackKey: function(ns, value) {
// Use the default value as fallback key
return value;
},
supportBasicHtmlNodes: true,
keepBasicHtmlNodesFor: ['br', 'strong', 'i', 'p']
},
attr: {
list: ['data-i18n'],
extensions: ['.html', '.htm']
},
lngs: ['en', 'de', 'fr', 'es'],
ns: ['common', 'validation', 'navigation', 'errors'],
defaultLng: 'en',
defaultNs: 'common',
defaultValue: '__STRING_NOT_TRANSLATED__',
resource: {
loadPath: 'locales/{{lng}}/{{ns}}.json',
savePath: 'locales/{{lng}}/{{ns}}.json',
jsonIndent: 2,
lineEnding: '\\n'
},
nsSeparator: ':',
keySeparator: '.',
interpolation: {
prefix: '{{',
suffix: '}}'
},
allowDynamicKeys: false
},
// Custom transform function
transform: function customTransform(file, enc, done) {
const parser = this.parser;
const content = fs.readFileSync(file.path, enc);
let count = 0;
// Parse custom translation functions
parser.parseFuncFromString(content, {
list: ['i18next._', 'i18next.__']
}, (key, options) => {
parser.set(key, Object.assign({}, options, {
nsSeparator: false,
keySeparator: false
}));
++count;
});
if (count > 0) {
console.log(`i18next-scanner: count=${chalk.cyan(count)}, file=${chalk.yellow(JSON.stringify(file.relative))}`);
}
done();
}
};The CLI supports powerful glob patterns for flexible file selection.
Glob Pattern Examples:
# Match all JavaScript files recursively
i18next-scanner 'src/**/*.js'
# Match multiple file types
i18next-scanner 'src/**/*.{js,jsx,ts,tsx}'
# Match specific directories
i18next-scanner 'src/components/**/*.jsx' 'src/pages/**/*.jsx'
# Exclude patterns with negation
i18next-scanner 'src/**/*.js' '!src/**/*.test.js' '!src/**/*.spec.js'
# Match files in root directory only
i18next-scanner 'src/*.js'
# Match with single character wildcard
i18next-scanner 'src/page?.js' # Matches page1.js, pageA.js, etc.Package.json Scripts:
{
"scripts": {
"i18n:scan": "i18next-scanner",
"i18n:scan-dev": "i18next-scanner --config i18next-scanner.dev.js",
"i18n:scan-prod": "i18next-scanner --config i18next-scanner.prod.js --output ./dist/locales",
"i18n:watch": "nodemon --watch src --ext js,jsx,ts,tsx --exec \"npm run i18n:scan\""
}
}CI/CD Integration:
#!/bin/bash
# Extract translation keys
i18next-scanner --config production.config.js 'src/**/*.{js,jsx,ts,tsx}'
# Verify no untranslated keys in production build
if grep -r "__STRING_NOT_TRANSLATED__" locales/; then
echo "Error: Found untranslated keys"
exit 1
fiDevelopment Configuration:
// i18next-scanner.dev.js
module.exports = {
input: ['src/**/*.{js,jsx}'],
output: './dev-locales',
options: {
debug: true,
defaultValue: '__DEV_PLACEHOLDER__',
lngs: ['en'],
ns: ['translation']
}
};Production Configuration:
// i18next-scanner.prod.js
module.exports = {
input: [
'src/**/*.{js,jsx,ts,tsx}',
'!src/**/*.{test,spec}.{js,jsx,ts,tsx}'
],
output: './dist/locales',
options: {
debug: false,
sort: true,
lngs: ['en', 'de', 'fr', 'es', 'ja'],
ns: ['common', 'validation', 'navigation'],
defaultValue: '',
resource: {
jsonIndent: 0, // Minified JSON for production
lineEnding: '\\n'
}
}
};CLI arguments take precedence over configuration file settings for input files and output directory.
Usage Examples:
# Override input files (ignores config.input)
i18next-scanner --config prod.config.js 'custom/**/*.js'
# Override output directory (ignores config.output)
i18next-scanner --output ./custom-output
# Use config for options but override input and output
i18next-scanner --config prod.config.js --output ./build/i18n 'src/critical/*.js'The CLI provides helpful error messages for common issues:
Common Error Scenarios:
# Missing configuration file
$ i18next-scanner --config missing.config.js
# Error: Cannot find module '/path/to/missing.config.js'
# Invalid glob patterns
$ i18next-scanner 'invalid[pattern'
# Error: Invalid glob pattern
# No input files specified and no config
$ i18next-scanner
# Displays help and exits
# Invalid output directory permissions
$ i18next-scanner --output /restricted/path 'src/*.js'
# Error: Permission denied writing to output directoryThe CLI generates translation resource files in the specified output directory with the configured structure.
Default Output Structure:
output/
├── i18n/
│ ├── en/
│ │ ├── translation.json
│ │ └── validation.json
│ ├── de/
│ │ ├── translation.json
│ │ └── validation.json
│ └── fr/
│ ├── translation.json
│ └── validation.jsonResource File Format:
{
"welcome": "Welcome!",
"user": {
"name": "Name",
"email": "Email"
},
"button": {
"save": "Save",
"cancel": "Cancel"
},
"validation": {
"required": "This field is required",
"email": "Please enter a valid email"
}
}