Metadata-Version: 2.4
Name: localediff
Version: 0.1.0
Summary: Find drift between i18n locale files — missing keys, CLDR-aware plural gaps, empty values. Framework-agnostic, zero dependencies.
Author: yyfjj
License: MIT
Project-URL: Homepage, https://github.com/jjdoor/localediff-py
Project-URL: Repository, https://github.com/jjdoor/localediff-py
Project-URL: Issues, https://github.com/jjdoor/localediff-py/issues
Keywords: i18n,l10n,locale,translation,missing-translations,drift,diff,i18next,cli,ci,pluralization
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Software Development :: Internationalization
Classifier: Topic :: Software Development :: Localization
Classifier: Topic :: Utilities
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# 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**.

```bash
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`.

```bash
# 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

```bash
# 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

```bash
npx localediff ./locales
```

Same checks, same flags, same exit codes — [localediff on npm](https://github.com/jjdoor/localediff).

## 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
