Find drift between i18n locale files — missing keys, CLDR-aware plural gaps, empty values. Framework-agnostic, zero dependencies.
Project description
localediff
Find drift between your i18n locale files — before your users do. You add a
string to en.json, ship it, and three weeks later notice fr.json and
zh.json were never updated. localediff catches that in CI: missing keys,
plural forms a language actually needs, and keys that exist but were left blank.
Framework-agnostic, zero dependencies.
pip install localediff
localediff ./locales
✗ fr.json
missing (1): auth.errors.locked
plural cart.items — has {other}, missing {one}
empty (1): footer.copyright
extra (1): legacy.banner
✓ zh.json — in sync
✗ 1 of 2 file(s) drifted — 1 missing, 1 plural gap(s), 1 empty, 1 extra
Why another i18n tool
Most existing tools are tied to one framework, need an account, or are heavy
AST-based linters. localediff just reads JSON. It compares structure, so it
works for next-intl, react-intl, i18next, vue-i18n, Django/Flask JSON catalogs,
or any plain message file — and it ships for both PyPI and npm with identical
behavior.
It understands plurals per language
This is the part naive "diff two JSON files" scripts get wrong. English has two
plural forms (one, other); Chinese has one (other); Russian has four
(one, few, many, other). A file with only items_other is correct for
Chinese but broken for French. localediff resolves the required CLDR
categories from each target's language, so it flags the real bug without crying
wolf on zh.json.
# en base: items_one + items_other
zh.json → items_other only → ✓ in sync (Chinese needs only `other`)
fr.json → items_other only → ✗ missing {one}
ru.json → items_one + _other → ✗ missing {few, many}
Usage
# Scan a folder. The base defaults to en.json; everything else is checked.
localediff ./locales
# Pick a different base language in the folder.
localediff ./locales --base de
# Compare specific files explicitly.
localediff --base en.json --check fr.json zh.json
# Shorthand: first file is the base.
localediff en.json fr.json zh.json
# Machine-readable output for CI gates.
localediff ./locales --format json
You can also run it as a module: python -m localediff ./locales.
What it checks
| Check | Meaning |
|---|---|
| missing | a key in the base that the target never translated |
| plural | a pluralized key (key_one, key_other, …) missing a CLDR form the target language requires |
| empty | a key present in the target whose value is a blank string |
| extra | a key in the target the base no longer has |
Nested objects are flattened to dot-paths (auth.errors.locked); arrays are
indexed (steps.0). Plural keys use the i18next suffix convention
(_zero, _one, _two, _few, _many, _other). Unknown languages fall back
to parity with the base, so you never get a confidently-wrong result.
Options
--base <file|lang> base/source locale (a file, or a lang stem in dir mode)
--check <files...> one or more target locales to compare against the base
--dir <dir> scan a directory of *.json locales
--lang <code> force the target language for plural rules
--format text|json output format (default: text)
--ignore-missing don't report missing keys
--ignore-extra don't report extra keys
--ignore-plural don't report plural gaps
--ignore-empty don't report empty values
-v, --version
-h, --help
In CI
localediff exits non-zero when anything has drifted:
| Exit code | Meaning |
|---|---|
0 |
every checked file is in sync |
1 |
one or more files have drift |
2 |
error (file not found, invalid JSON, bad arguments) |
Also available for Node
npx localediff ./locales
Same checks, same flags, same exit codes — localediff on npm.
Scope
JSON locale files only (the common case). YAML/.properties/gettext are not
supported — parsing them would mean pulling in a dependency, and zero-dep is the
point. Convert to JSON, or open an issue to discuss.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file localediff-0.1.0.tar.gz.
File metadata
- Download URL: localediff-0.1.0.tar.gz
- Upload date:
- Size: 13.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
efdf76c19377de05c91dbb0b92c0a3cd7a926481de897dc854b5fae8ffb67af5
|
|
| MD5 |
eda20ec5be1f7f8770073f0c272862fd
|
|
| BLAKE2b-256 |
c696fb8124cf2c7c85f92554352cd35d1b022fa2252fb411d0cc8f3cf0347261
|
File details
Details for the file localediff-0.1.0-py3-none-any.whl.
File metadata
- Download URL: localediff-0.1.0-py3-none-any.whl
- Upload date:
- Size: 11.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
980d099c5687076092e675802dfa3c421b14cfacb80965c20339df139b8f2e0e
|
|
| MD5 |
ec46c1da645fc3f8d3696d7e0c4f438f
|
|
| BLAKE2b-256 |
b057c8707b216953cf59bc0d9a7003687ff21a1dc9f5d1d72295cdc69985de21
|