Configurable CLI validation tool to look for common problems in your translated source files.
Have you ever shipped "buggy" translation files to your users in production? Probably! I know I have!
Let's face it, we all make mistakes (developers, translators, copywriters, marketers, humans, etc). Our translation files in our software should be protected against the most common mistakes.
Translated files in your software project are an often-overlooked source of problems that can affect the usability, reliability and reputation of your application(s). These translated files are often edited manually, built automatically (without any integrity checks) or outsourced to third parties to provide translations. These files typically do not pass through any (or most) of your automated tests, or they easily get skipped in your manual tests...which means the hidden problems get shipped to your real users in production.
Furthermore, there are best practices we want to adhere to with our translated files, and these best practices should be enforceable with an integrity check.
Keeli will help you automatically discover many problems with your translation files, including:
- ✅ Finds untranslated messages
- ✅ Finds empty messages
- ✅ Finds missing variables
- ✅ Finds accidentally translated variables
- ✅ Finds variable syntax errors
- ✅ Finds missing keys
- ✅ Finds unknown/un-balanced keys
- ✅ Finds keys violating your naming convention
- ✅ Finds extra whitespace
- ✅ Finds HTML in messages
- ✅ ...and more!
Most of these rules are configurable so you can customize keeli to your specific needs.
If you want the keeli
command to be globally available on your system, you can install it globally:
npm install --global keeli
And then you have the keeli
command in your terminal. Now you are ready to configure Keeli for your first validation check. 🎉
If you don't need the keeli
command to be available globally on your system, then you can install Keeli into your project directly into devDependencies
using the package manager of your choice:
npm
npm install --save-dev keeli
yarn
yarn add keeli -D
pnpm
pnpm add keeli --save-dev
Then you can use the keeli
command in your package.json scripts, for example: "validate-i18n": "keeli"
or "validate-i18n": "npx keeli"
For each project where you want to run the keeli, you will need to have a file named keeli.config.json
in the project root where your internationalization files are defined.
Tip
You can also pass in a custom configuration file path location with keeli's --config
option.
Each configuration file should be a valid JSON file and have a similar format to this (with comments removed):
{
/**
* The locale your translations uses as the default or source language.
* Example: 'en'
*
**/
defaultLocale: "en",
/**
* The filename of the file where your app's default/source language is defined.
* Example: "default.json" | "source.json" | "en.json"
*
**/
sourceFile: "en.json",
/**
* An object describing the locale and its associated translation file name.
* Example: {"de": "de.json", "fr": "fr.json" }
*
**/
translationFiles: {
de: "de.json",
fr: "fr.json",
},
/**
* The path, relative to the root directory where your i18n files can be located.
* Example: 'relative/path/to/i18n/files'.
*
* Note: This relative path will be combined with the filenames you supplied in
* the translationFiles object to create the path to each translation file.
*
**/
pathToTranslatedFiles: "i18n",
/**
* The rules configuration.
*
* You can set each rule to a severity level of: 'error' | 'warn' | 'off'
* Note: If you do not provide a severity setting for a rule, then the
* rule's default setting will apply.
*
* You can also pass in advanced configuration for some rules if you pass in
* an object like:
*
* "rule-name": {
* "severity": "error",
* "ignoreKeys": ["foo", "bar"]
* }
*
* Check the rule's documentation for advanced configuration options.
*
**/
rules: {
"no-empty-messages": "error",
"no-extra-whitespace": "error",
"no-html-messages": "error",
"no-invalid-variables": "error",
"no-untranslated-messages": "error",
"no-malformed-keys": "error",
"no-missing-keys": "error",
},
/**
* Set this dryRun setting to true to get all the same logging and reporting
* you would get in a real validation check, but no errors will be thrown if
* errors are found. Great for getting your i18n-validation setup in CI/CD
* pipelines without breaking your builds.
*
**/
dryRun: false,
/**
* Enable or disable this entire keeli.
*
**/
enabled: true,
/**
* Enable or disable verbose logging mode.
*
**/
verbose: false,
}
Tip
Your configuration file does need to be complete, if you do not specify a value for any parameter that is not required, then the default configuration value will apply. If you forget to provide a value for a required value, then keeli will throw a useful error.
Each rule (that allows you to configure the severity) can have a default setting of error
, warn
or off
, these defaults will apply if you do not provide a configuration for the rule in the configuration file.
Rule name | Default |
---|---|
no-empty-messages | error |
no-extra-whitespace | error |
no-html-messages | error |
no-untranslated-messages | error |
no-invalid-variables | error |
no-malformed-keys | error |
no-missing-keys | error |
no-invalid-configuration | error |
no-invalid-severity | error |
The following rules are not configurable, meaning you should not add these rules to your configuration files, but these rules run with their default severity shown below:
Rule name | Default |
---|---|
no-invalid-configuration | error |
no-invalid-severity | error |
Some rules can have advanced configuration passed in. Each rule might be different, so you want to read the documentation for each rule, but here are some of the more general overrides available:
For validation rules that read through each key of a translated file, you can pass in an ignoreKeys
array of strings that represent keys that should be ignored for the specific rule. Meaning, the rule will still run as expected on all keys, except for the keys specified. For example, if you have translation files that looks like this:
{
"en": { "ok": "OK" },
"fr": { "ok": "OK" }
}
Then you might want the rule no-untranslated-messages
to ignore this key 'ok'
because the translation of "ok" might also be "OK" and trigger a false positive in the rule.
To configure the ignoreKeys
array you can assign an object to the rule name in the configuration file.
{
"rules": {
"no-untranslated-messages": {
"severity": "error",
"ignoreKeys": ["ok"]
}
}
}
Important
Be careful when ignoring keys for specific rules, ignored keys can lead to ignored problems!
Tip
If you find that you are ignoring a lot of keys, consider moving some strings outside of your translated files (for example in a common messages library, etc)...
A feature of this CLI tool is that whenever you intentionally skip/ignore a known problem, these problems are still tracked and reported by default (as of now, there is also no way for you to opt-out of seeing ignored problems in the log output, pull requests welcome!), but any ignored errors won't fail your build, they are merely reported in the logs.
One of the main motivations with this CLI is to expose (not hide) the problems in your translation files. I believe that ignoring problems and not showing these ignored problems in the logs prevents new problems from creeping into the files and also helps encourage best practices, additional benefits include: tracking technical debt and showing the consequences/risks you might be taking by ignoring actual problems.
Contributions to this project are always welcome! ❤️ Feel free to open issues, pull requests, etc and let's make this project better together.
If you have the repo cloned locally, you can test run the CLI by running:
npm run build:dev && node dist/index.js
, which will run all of the validation rules on the translation files in the fixtures directory.
Tip
If you have the CLI linked with npm link
then you can just run npm run build:dev && keeli
Create a GitHub release with the version number you want to create. Make sure the tag you create matches the version number (e.g., v1.2.3
) and release! GitHub actions will take over and attempt to publish the package version you specified.
🌐 💻️