chki18n for Python
📘 chki18n.cdget.com
Every check, every option and every example. This README is just the quick start.
chki18n checks that your i18n translation files agree with each other. Point it at a folder of JSON, name the language everything is compared against, and it reports what is missing, what was never translated and what quietly broke.
- Twenty-five checks — missing keys, a language file nobody created, keys defined twice, empty values, untranslated strings, mismatched interpolation placeholders, dropped markup, changed numbers, invisible characters, drifting terminology, missing plural forms, and — pointed at your sources — keys nothing references and keys nothing defines.
- Every layout — one file per locale, one folder per locale, or one file holding them all.
- CLI and library share one set of checks and one set of options.
- A report you can act on — grouped by language, check, file or nothing; rendered for a terminal, for
grep, as JSON, as Markdown or as GitHub Actions annotations; saved to a file on request. - No dependencies, fully typed, and
chki18n.corereaches no file system at all.
Install
pip install chki18n
Requires Python 3.10 or newer.
From the command line
chki18n ./locales --target en
Path ./locales
Target en
Locales en, ko
Layout single, 1 group, 10 keys
ko ──────────────────────────────────────────────────────────────────────── 1 error · 1 warning
ERROR NO_KEY (1)
The key exists in the target language but is missing here.
attr.folder en: "Folder"
WARN NOT_TRANSLATED_VALUE (1)
The value is identical to the target language, so the translation may be incomplete.
desc.no-str en: "12345"
Summary ───────────────────────────────────────────────────────────────────────────────────────
Compared 10 keys across 2 locales in 1 group. (3ms)
1 error · 1 warning
Clean: en
FAIL 1 error must be fixed before this passes.
Exits with 1 when an error level issue was found, so a CI job fails on it. chki18n --help lists every flag. Without installing it, pipx run chki18n ./locales --target en does the same thing.
--group-by decides what a section is (locale, code, group, file or none), --reporter decides the shape (pretty, list, json, markdown or github), and --output keeps a copy:
chki18n ./locales --target en --reporter json > report.json
chki18n ./locales --target en --output translation-report.md
From Python
Four entry points, depending on who owns the translations:
from chki18n import (
Entry,
Input,
Options,
analyze_translations,
check_translation_files,
create_analyzer,
load_translations,
)
# Check a directory once — the same thing the CLI does.
result = check_translation_files("./locales", Options(target="en"))
result.success # False
result.issues # every issue, with its level, key, locale and file
# Check data you already have, with no file system work at all.
analyze_translations(Input(locales={"en": en, "ko": ko}), Options(target="en"))
# Read a directory once, then check it as often as you like.
session = load_translations("./locales", Options(target="en"))
session.set("ko", "desc.hello", "안녕하세요") # the issues for that key
# Or let your own application own the values and ask only for a verdict.
create_analyzer(Options(target="en")).check_entry(
Entry(key="desc.hello", values={"en": "Hello {name}", "ko": "안녕하세요"})
)
# [Issue(code='NO_INTERPOLATION_KEY', level='error', locale='ko', ...)]
The last two do no file system work and are also importable as chki18n.core, which reaches no file system at all.
Every option is one keyword-only object, so a flag and its Python counterpart are the same thing. The loose forms a command line writes are accepted here too:
Options(
target="en",
ignore_checks=["SUSPICIOUS_LENGTH"], # or "SUSPICIOUS_LENGTH"
levels={"EMPTY_VALUE": "error"}, # or "EMPTY_VALUE=error"
key_case="kebab",
reporter="markdown",
)
The documentation site covers every check, every option and how to render a result.
Contributing
Anyone can contribute by reporting an issue or opening a pull request. See CONTRIBUTING.md.
License
Released under the MIT License.
Metadata
Release files for chki18n 1.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| chki18n-1.1.0.tar.gz | 80.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| chki18n-1.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 158.1 kB
Release files / chki18n-1.1.0.tar.gz
| Download URL | chki18n-1.1.0.tar.gz |
|---|---|
| Size | 80.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
aad895bf2aa1ab1b0e423468435f28e14cad40d25565e70456dae6016c06a820
|
|
BLAKE2b-256 checksum How to use checksums |
121b3115e797389c19bfaffb6e5cddf6ede5dd5bbdc95a46d045d4e89f129be9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / chki18n-1.1.0-py3-none-any.whl
| Download URL | chki18n-1.1.0-py3-none-any.whl |
|---|---|
| Size | 77.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
85f26e4dfa3109d1a0093db231f8e657a8e28e284328e70f06f367e9b8efce18
|
|
BLAKE2b-256 checksum How to use checksums |
4552e66da84241a6e52386813f5587ad45c0d40e97e8de2edd8e9747f3e60644
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|