translate-lint
v1.10.2
Published
Simple tools for checking translate keys in the whole app that uses regexp. Support for popular libraries and frameworks
Maintainers
Readme
translate-lint
Simple tools for checking translate keys in the whole app that uses regexp. Support for popular libraries and frameworks
for
desktopusengx-translate-editor
Table of Contents
Background
There are a lot of translation keys in the whole app. This repository contains a proposal to check all translation keys in the whole app which should exist in all languages files.
Support
| Library | Preset | Framework |
|------------------------------------------------------------------|-------------------------|-----------|
| ngx-translate | angular-ngx-translate | Angular |
| transloco | transloco | Angular |
| react-i18next | react-i18next | React |
| react-intl | react-intl | React |
| lingui | lingui-js | React |
| next-intl | next-intl | Next.js |
| next-i18next | next-i18next | Next.js |
| vue-i18n | vue-i18n | Vue |
| i18next-vue | i18next-vue | Vue |
| fluent-vue | fluent-vue | Vue |
| @nuxtjs/i18n | nuxtjs-i18n | Nuxt.js |
| svelte-i18n | svelte-i18n | Svelte |
| next-translate | next-translate | Next.js |
| remix-i18next | remix-i18next | Remix |
| sveltekit-i18n | sveltekit-i18n | SvelteKit |
| solid-i18n | solid-i18n | Solid.js |
| qwik-speak | qwik-speak | Qwik |
| i18next | i18next | Universal |
| i18n | i18n-node | Node.js |
| lit-translate | lit-translate | Lit |
Installation
NPM
npm install translate-lint -gUsage
CLI
Run translate-lint against your project and language files:
translate-lint -p './src/app/**/*.{html,ts,resx}' -l './src/assets/i18n/*.{json,yaml,yml}' -f angular-ngx-translateNOTE: For
projectandlanguagesoptions need to include file types like on the example.
Flags
Usage: translate-lint [options]
Simple tools for checking translate keys in the whole app that uses regexp.
Support for popular libraries and frameworks
Options:
-f, --frameworkPreset [enum] (required)
Preset of frameworks and libraries
Possible Values: <angular-ngx-translate|lingui-js|next-intl|react-intl|react-i18next|vue-i18n|fluent-vue|i18next-vue|transloco|next-i18next|nuxtjs-i18n|svelte-i18n|next-translate|i18next|remix-i18next|sveltekit-i18n|solid-i18n|qwik-speak|i18n-node|lit-translate>
-p, --project [glob]
The path to project folder. (default: './src/app/**/*.{html,ts,resx}')
Possible Values: <relative path|absolute path>
-l, --languages [glob]
The path to languages folder. (default: './src/assets/i18n/*.{json,yaml,yml}')
Possible Values: <relative path|absolute path|URL>
--kv, --keysOnViews [enum]
Described how to handle the error of missing keys on view. (default: "error")
Possible Values: <disable|warning|error>
--zk, --zombieKeys [enum]
Described how to handle the error of zombies keys. Zombie keys are keys that doesn't exist on any languages file but exist on project, or exist languages but doesn't exist on project. (default: "warning")
Possible Values: <disable|warning|error>
--ek, --emptyKeys [enum]
Described how to handle empty value on translate keys. Empty keys are keys that doesn't have any value on languages files. (default: "warning")
Possible Values: <disable|warning|error>
-i, --ignore [glob]
Ignore projects and languages files
Possible Values: <relative path|absolute path>
--maxWarning [glob]
Maximum number of warnings allowed across all files. If the total warning count exceeds this value, the run is treated as a failure (exit code 1). (default: 0)
Possible Values: <number>
--mk, --misprintKeys [enum]
Try to find matches with misprint keys on views and languages keys. Coefficient: 0.9. Can be longer process!! (default: "disable")
Possible Values: <disable|warning|error>
--ds, --deepSearch [enum]
Add each translate key to global regexp end try to find them on project. Can be longer process!! (default: "disable")
Possible Values: <disable|enable>
--mc, --misprintCoefficient [number]
Coefficient for misprint option can be from 0 to 1.0. (default: 0.9)
-c, --config [path]
Path to config via JSON or JS file
Possible Values: <relative path|absolute path>
--fz, --fixZombiesKeys [boolean]
Auto fix zombies keys on languages files
--dk, --duplicateKeys [enum]
Detect keys that are duplicated within the same language file. (default: "disable")
Possible Values: <disable|warning|error>
--mt, --missingTranslations [enum]
Detect keys that exist in one language file but are missing in others. (default: "disable")
Possible Values: <disable|warning|error>
--fm, --fixMissingKeys [boolean]
Auto-add missing keys to all language files with an empty value
--knc, --keyNamingConvention [enum]
Enforce a naming convention for translation key segments. (default: "disable")
Possible Values: <disable|warning|error>
--kncf, --keyNamingConventionFormat [enum]
Naming format used by the keyNamingConvention rule. (default: "SCREAMING_SNAKE")
Possible Values: <SCREAMING_SNAKE|camelCase|snake_case|kebab-case|PascalCase>
--ft, --format [enum]
Output format for lint results. (default: "stylish")
Possible Values: <stylish|json|junit>
--cv, --coverage [enum]
Described how to handle the error when project coverage (% of language keys actually used, see --minCoverage) drops below the configured minimum. (default: "disable")
Possible Values: <disable|warning|error>
--mcv, --minCoverage [number]
Minimum percentage of language keys that must be used in the project, checked by the --coverage rule. (default: 80)
--dv, --duplicateValues [enum]
Detect identical translation values reused under different keys in the same language file. (default: "disable")
Possible Values: <disable|warning|error>
--uv, --untranslatedValues [enum]
Detect values that are identical to the same key's value in --referenceLocale — a sign of a forgotten translation. (default: "disable")
Possible Values: <disable|warning|error>
--rl, --referenceLocale [glob]
Substring matched (case-insensitive) against language file names to pick the reference locale used by --untranslatedValues, e.g. "en". (default: none)
--cskc, --caseSensitiveKeyCollision [enum]
Detect keys in the same language file that differ only by letter case (e.g. 'Button.Save' vs 'button.save'). (default: "disable")
Possible Values: <disable|warning|error>
--sk, --sortKeys [enum]
Detect translation keys that are not sorted within their language file, at every nesting level (see --sortBy). (default: "disable")
Possible Values: <disable|warning|error>
--sb, --sortBy [enum]
Sort order used by the sortKeys rule. (default: "asc")
Possible Values: <asc|desc>
--fsk, --fixSortKeys [boolean]
Auto-sort keys in JSON language files that violate the sortKeys rule
--vc, --variableConsistency [enum]
Detect interpolation variables (e.g. '{name}', '{{count}}', '%s') that don't match across language files for the same key. (default: "disable")
Possible Values: <disable|warning|error>
--mvl, --maxValueLength [enum]
Detect translation values longer than --valueLength characters. (default: "disable")
Possible Values: <disable|warning|error>
--vl, --valueLength [number]
Maximum allowed translation value length, checked by the maxValueLength rule. (default: 200)
--fmvl, --fixMaxValueLength [boolean]
Auto-truncate values in JSON language files that exceed --valueLength
--nhiv, --noHtmlInValues [enum]
Detect HTML markup (e.g. '<b>text</b>') inside translation values. (default: "disable")
Possible Values: <disable|warning|error>
--fnhiv, --fixNoHtmlInValues [boolean]
Auto-strip HTML markup from values in JSON language files
--htc, --htmlTagConsistency [enum]
Detect an inconsistent set of HTML tags for the same key across language files. (default: "disable")
Possible Values: <disable|warning|error>
--be, --brokenEncoding [enum]
Detect mojibake artifacts (e.g. 'é', '’') left by double UTF-8 encoding in translation values. (default: "disable")
Possible Values: <disable|warning|error>
--vekn, --valueEqualsKeyName [enum]
Detect translation values that are identical to their own key name. (default: "warning")
Possible Values: <disable|warning|error>
--lfnc, --localeFileNamingConvention [enum]
Enforce a naming convention for language file names. (default: "disable")
Possible Values: <disable|warning|error>
--lfnf, --localeFileNamingFormat [enum]
Naming format used by the localeFileNamingConvention rule. (default: "xx-XX")
Possible Values: <xx|xx-XX|XX-xx|xx_XX>
--dlf, --duplicateLocaleFile [enum]
Detect language files that are byte-for-byte identical to another language file. (default: "disable")
Possible Values: <disable|warning|error>
--elf, --emptyLocaleFile [enum]
Detect language files that parse successfully but contain no translation keys. (default: "disable")
Possible Values: <disable|warning|error>
--init [boolean]
Interactively generate a translate-lint config file
-v, --version Print current version of translate-lint
-h, --help display help for command
Examples:
$ translate-lint -p './src/app/**/*.{html,ts,resx}' -l './src/assets/i18n/*.json' -f angular-ngx-translate
$ translate-lint -p './src/app/**/*.{html,ts,resx}' -zk disable -kv error -f react-i18next
$ translate-lint -p './src/app/**/*.{html,ts,resx}' -i './src/assets/i18n/EN-us.json, ./src/app/app.*.{json}' -f react-intl
$ translate-lint -p './src/app/**/*.{html,ts,resx}' -l 'https://8.8.8.8/locales/EN-eu.json' -f lingui-jsNOTE: For
projectandlanguagesoptions need to include file types like on the example.
Option resolution order
Every option follows the same precedence, uniformly: explicit CLI flag >
config file (--config) > built-in default. None of the flags above carry
a hidden CLI-side default that would silently override a value you set in the
config file — if you omit a flag on the CLI, the config file (and then the
built-in default) decides.
Output format
Three formats are available via --format (--ft):
| Format | Description |
|--------|-------------|
| stylish | Human-readable colored output (default) |
| json | Machine-readable JSON — ideal for CI artifacts |
| junit | JUnit XML — consumed by GitHub Actions, Jenkins, GitLab CI test reporters |
JSON format:
translate-lint -p './src/**/*.ts' -l './locales/*.json' -f react-i18next --format json{
"errors": [
{
"key": "AUTH.LOGIN",
"errorType": "error",
"rule": "keysOnViews",
"file": "src/app/login.ts",
"message": ["Key: 'AUTH.LOGIN' doesn't exist in 'src/app/login.ts'"]
}
],
"summary": {
"total": 3,
"errors": 2,
"warnings": 1
},
"coverage": {
"totalKeys": 120,
"usedKeys": 105,
"unusedKeys": 15,
"percentage": 87.5
},
"stats": {
"viewFiles": 12,
"languageFiles": 3,
"elapsedMs": 342
}
}Default Configuration
Default JSON Config is:
{
"rule": {
"keysOnViews": {
"type": "error"
},
"zombieKeys": {
"type": "warning",
"fix": false
},
"emptyKeys": {
"type": "warning"
},
"misprintKeys": {
"type": "disable",
"coefficient": 0.9,
"ignored": []
},
"deepSearch": {
"type": "disable"
},
"maxWarning": 0,
"ignoredKeys": [ "IGNORED.KEY.(.*)" ],
"customRegExpToFindKeys": [ "(?<=marker\\(['\"])([A-Za-z0-9_\\-.]+)(?=['\"]\\))"],
"namespaceKeys": {
"type": "disable",
"delimiter": ".",
"namespaces": {},
"globalNamespaces": [],
"ignoreInFolders": []
},
"maxKeyDepth": {
"type": "disable",
"depth": 4
},
"duplicateKeys": {
"type": "disable"
},
"missingTranslations": {
"type": "disable",
"fix": false
},
"keyNamingConvention": {
"type": "disable",
"format": "SCREAMING_SNAKE"
},
"coverage": {
"type": "disable",
"min": 80
},
"duplicateValues": {
"type": "disable"
},
"untranslatedValues": {
"type": "disable",
"referenceLocale": ""
},
"caseSensitiveKeyCollision": {
"type": "disable"
},
"sortKeys": {
"type": "disable",
"sortBy": "asc",
"fix": false
},
"variableConsistency": {
"type": "disable"
},
"maxValueLength": {
"type": "disable",
"length": 200,
"fix": false
},
"noHtmlInValues": {
"type": "disable",
"fix": false
},
"htmlTagConsistency": {
"type": "disable"
},
"brokenEncoding": {
"type": "disable"
},
"valueEqualsKeyName": {
"type": "warning"
},
"localeFileNamingConvention": {
"type": "disable",
"format": "xx-XX"
},
"duplicateLocaleFile": {
"type": "disable"
},
"emptyLocaleFile": {
"type": "disable"
}
},
"fetch": {
"requestQuery": "",
"requestHeaders": {},
"responseQuery": ""
},
"project": "./src/app/**/*.{html,ts}",
"languages": "./src/assets/i18n/*.{json,yaml,yml}",
"frameworkPreset": "angular-ngx-translate"
}JS Config should have default export via object like config. See example:
Example JS config is:
const config = {
rule: {
keysOnViews: {
type: "error",
},
zombieKeys: {
type: "warning",
fix: false,
},
emptyKeys: {
type: "warning",
},
misprintKeys: {
type: "warning",
coefficient: 0.9,
ignored: [],
},
deepSearch: {
type: "disable",
},
maxWarning: 0,
ignoredKeys: [],
customRegExpToFindKeys: [],
namespaceKeys: {
type: 'error',
delimiter: '.', // use ':' for react-i18next (e.g. t('bonus:TITLE'))
namespaces: {
'bonus': ['apps/bonus', 'apps/bonus-detail'],
'registration': ['apps/registration', 'apps/auth'],
},
globalNamespaces: ['g', 'form', 'nav', 'popup'],
ignoreInFolders: ['sandbox-component-kit'],
},
maxKeyDepth: {
type: "disable",
depth: 4,
},
duplicateKeys: {
type: "disable",
},
missingTranslations: {
type: "disable",
fix: false,
},
keyNamingConvention: {
type: "disable",
format: "SCREAMING_SNAKE", // SCREAMING_SNAKE | camelCase | snake_case | kebab-case | PascalCase
},
coverage: {
type: "disable",
min: 80,
},
duplicateValues: {
type: "disable",
},
untranslatedValues: {
type: "disable",
referenceLocale: "", // e.g. "en" to match EN-us.json / en.json / en-GB.json
},
caseSensitiveKeyCollision: {
type: "disable",
},
sortKeys: {
type: "disable",
sortBy: "asc", // "asc" | "desc"
fix: false,
},
variableConsistency: {
type: "disable",
},
maxValueLength: {
type: "disable",
length: 200,
fix: false,
},
noHtmlInValues: {
type: "disable",
fix: false,
},
htmlTagConsistency: {
type: "disable",
},
brokenEncoding: {
type: "disable",
},
valueEqualsKeyName: {
type: "warning",
},
localeFileNamingConvention: {
type: "disable",
format: "xx-XX", // "xx" | "xx-XX" | "XX-xx" | "xx_XX"
},
duplicateLocaleFile: {
type: "disable",
},
emptyLocaleFile: {
type: "disable",
},
},
fetch: {
requestQuery: "",
requestHeaders: {},
responseQuery: "",
get: async () => {
const requestOne = fetch('https://8.8.8.8/locales/EN-eu.json');
const requestTwo = fetch('https://8.8.8.8/locales/EN-us.json');
const result = await Promise.all([requestOne, requestTwo]).then(async ([responseOne, responseTwo]) => {
return {
...(await responseOne.json()),
...(await responseTwo.json())
}
});
// NOTE: result should contains only translation keys. Example
// {
// "translation.key": "value"
// }
return result;
}
},
project: "./src/app/**/*.{html,ts}",
languages: "./src/assets/i18n/*.{json,yaml,yml}",
frameworkPreset: "angular-ngx-translate",
}
export default config;Init
Run the interactive config generator to create a translate-lint.config.json (or .js) file in the current directory:
translate-lint --initAfter generation, pass the file to the linter via --config:
translate-lint --config ./translate-lint.config.jsonTypeScript
translate-lint can also be used programmatically — the TranslateLint class, all rule config interfaces, and the framework preset registry are exported from the package. See docs/rules/README.md for a full, up-to-date example.
Rules
translate-lint ships with the following rules. See docs/rules/README.md for how each one works, its config options, CLI flags, and example error messages:
- keysOnViews
- zombieKeys
- emptyKeys
- misprintKeys
- deepSearch
- keyNamingConvention
- missingTranslations
- duplicateKeys
- namespaceKeys
- maxKeyDepth
- coverage
- duplicateValues
- untranslatedValues
- caseSensitiveKeyCollision
- sortKeys
- variableConsistency
- maxValueLength
- noHtmlInValues
- htmlTagConsistency
- brokenEncoding
- valueEqualsKeyName
- localeFileNamingConvention
- duplicateLocaleFile
- emptyLocaleFile
Contribute
You may contribute in several ways like requesting new features, adding tests, fixing bugs, improving documentation or examples. Please check our contributing guidelines.
Used By
Here can be your extensions:
- ngx-translate-editor - Simple GUI for CRUD translate keys of ngx-translate, which included ngx-translate-lint
- 121 Platform - 121 is an open source platform for Cash based Aid built with Digital Identity & Local/Global Financial service partners.
