Check translations in GitLab CI
The job installs the kaeris package and runs kaeris check on merge requests that touch your locale files. A lost placeholder or a missing key fails the pipeline. It is offline and free: no API key, no account.
The recipe
The job
Add it to .gitlab-ci.yml. Change --source to your base-language file and --langs to the locales you ship. The rules block runs the job only on merge requests that change something under locales/; drop the changes lines to run it on every merge request.
i18n-check:
stage: test
image: python:3.12-slim
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
changes:
- locales/**/*
script:
- pip install kaeris
- kaeris check --source locales/en.json --langs de,fr --ci
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 the job prints
The two script lines, run in order. Left: files that are fine. Right: de.json with {nam} instead of {name} and the checkout key missing; the job stops with exit code 1.
$ pip install kaeris Collecting kaeris Using cached kaeris-0.2.35-py3-none-any.whl.metadata (26 kB) Using cached kaeris-0.2.35-py3-none-any.whl (131 kB) Installing collected packages: kaeris Successfully installed kaeris-0.2.35 $ kaeris check --source locales/en.json --langs de,fr --ci ✓ de: complete & placeholder-safe ✓ fr: complete & placeholder-safe ✓ all locales complete & placeholder-safe Job succeeded exit code: 0
$ pip install kaeris
Requirement already satisfied: kaeris in C:\Users\Inzagi\AppData\Local\Temp\claude\C--Users-Inzagi-projects-kaeris\bfeebe69-3cfd-4f28-bbe7-e5600be082b9\scratchpad\venv-gl2\Lib\site-packages (0.2.35)
$ kaeris check --source locales/en.json --langs de,fr --ci
✗ 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
ERROR: Job failed: exit code 1
exit code: 1
Set it up in three steps
Add the job
Paste the job into
.gitlab-ci.ymland set your own--sourceand--langs.Open a merge request
Change a locale file on a branch and open a merge request. The job runs and shows every finding in its log.
Require the pipeline
In the project settings under merge requests, turn on that pipelines must succeed. A red job then blocks the merge.
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
Both
scriptlines, taken from the parsed YAML and run one after another in a clean Python virtual environment, stopping at the first failing line as a GitLab job does.pip install kaerisdownloaded kaeris 0.2.35 from PyPI. Clean files: exit code 0. The brokende.json: exit code 1.What did not run
No GitLab runner and no Docker image were used (the log above comes from a Windows PC with Python 3.14, not from
python:3.12-slim). Therulesandchangesblock was only parsed as YAML; GitLab did not evaluate it. The package declares Python 3.8 or newer.
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
Can I save the result as a file?
Yes: --json prints the result as JSON and --sarif PATH writes a SARIF report. Add the file under artifacts if you want to keep it. Both flags are in the help of kaeris check; we did not run an artifact upload on GitLab.
Keep going
Other recipes: GitHub Actions · pre-commit · Make and npm scripts
CLI reference · Check reference · GitHub Action reference · Free check in the browser · Translate in the browser