Check translations with pre-commit
With pre-commit, a commit that touches a locale file runs kaeris check first and is refused when a translation lost a placeholder or a key. It is offline and free: no API key, no account.
The recipe
The hook
Save it as .pre-commit-config.yaml in the project root. It is a local hook: pre-commit builds its own environment and installs kaeris from PyPI into it, so nothing else has to be installed. files limits it to commits that stage something under locales/.
repos:
- repo: local
hooks:
- id: kaeris-check
name: kaeris check (translations)
entry: kaeris check --source locales/en.json --langs de,fr --ci
language: python
additional_dependencies: ["kaeris==0.2.35"]
files: ^locales/
pass_filenames: false
What it prints, on real runs
Output of kaeris 0.2.35, captured 27 October 2026. The last line of each box is the exit code the CI job sees.
What a commit prints
Left: a commit with files that are fine. Right: a commit with de.json that has {nam} instead of {name} and the checkout key missing; the commit is refused.
$ git commit -m "add locales" kaeris check (translations)..............................................Passed [main (root-commit) 96c8a87] add locales 4 files changed, 25 insertions(+) create mode 100644 .pre-commit-config.yaml create mode 100644 locales/de.json create mode 100644 locales/en.json create mode 100644 locales/fr.json exit code: 0
$ git commit -m "break de"
kaeris check (translations)..............................................Failed
- hook id: kaeris-check
- exit code: 1
✗ de: 1 missing key(s): checkout
✗ de: 1 placeholder mismatch(es):
greeting: missing ['{name}'], added ['{nam}']
✗ de: 2 translation fault(s):
greeting: lost placeholder {name}
greeting: invented placeholder {nam} (not in source)
✓ fr: complete & placeholder-safe
✗ 1 missing key, 1 placeholder mismatch, 2 translation faults
exit code: 1
A commit that touches no locale file
The hook is skipped, so unrelated commits stay fast.
$ git commit -m "readme" # nothing under locales/ is staged kaeris check (translations)..........................(no files to check)Skipped [main e82b41d] readme 1 file changed, 1 insertion(+) create mode 100644 README.md exit code: 0
Set it up in three steps
Add the hook
Save the config above as
.pre-commit-config.yamland set your own--sourceand--langs. Keep the version pin and raise it when you upgrade.Install the git hook
Run
pip install pre-commitand thenpre-commit installonce per clone.Commit as usual
A commit that stages a file under
locales/runs the check and is refused when it fails.pre-commit run --all-filesruns the same check on demand, for example in CI.
How we checked this
Every command on this page was run before it was written here. What was not run is listed too.
What ran
pre-commit 4.7.0 with exactly the config above in a fresh git repository:
pre-commit install, then realgit commitruns. The hook environment was built from PyPI (kaeris 0.2.35). Good files: the commit went through. Brokende.json: the hook failed with exit code 1 and git refused the commit. A commit with no locale file: skipped.What did not run
A CI job that calls
pre-commit run --all-fileswas not run on a runner. There is no published hook repository for kaeris, so the recipe uses a local hook; there is norepo: https://github.com/…line to add.
Options worth knowing
Fail on stale keys too
--strictalso fails when a translation has keys that the source no longer has.Keep brand names as written
--glossary 'KAERIS,GitHub'fails a target that dropped one of the terms. The same list can live underkeepinkaeris.json.Different file layout
--outis the folder with the translations and--pattern '{lang}/common.json'their file names. Default: the layoutkaeris translatewrites.No flags at all
With a
kaeris.jsonin the project (kaeris initwrites one),kaeris checktakes the source, languages and folder from it.
Questions
Does it need an API key or an account?
No. kaeris check is described in its own help as static, local and no-API, so the job needs no secret. Translating is a separate command that does need a key.
What does the check catch?
A lost or invented placeholder ({name}, %s, {{x}}, ICU plurals), a missing key and a locale file that is not there. The comparison is local and static. What it checks, with every flag
Does it slow every commit down?
Only commits that stage a file matching files. The first run builds the hook environment, which needs network; later runs reuse it. We did not time it, so no figure here.
Keep going
Other recipes: GitHub Actions · GitLab CI · Make and npm scripts
CLI reference · Check reference · GitHub Action reference · Free check in the browser · Translate in the browser