Check translations before they ship
Runs on your machine, needs no key and sends nothing anywhere. It finds lost variables like {name}, changed numbers, missing keys and empty lines. Errors end the run with exit code 1, so CI can stop the merge; an empty line is a warning and fails the run only with --strict.
npx kaeris check --source en.json --langs de
No Python needed: npx runs the npm package on Node 18 or newer. With Python, pip install kaeris gives you the same kaeris check.
{
"greeting": "Hallo!",
"delete": "50 Dateien löschen",
"save": "",
"terms": "Lies unsere <b>AGB</b>"
}
✗ de: 1 placeholder mismatch(es): greeting: missing ['{name}'], added - ✗ de: 3 translation fault(s): greeting: lost placeholder {name} delete: number 50 appears in the translation but not the source delete: number 5 from the source is missing or changed in the translation ! de: 1 warning(s): save: empty translation — the source has text ✗ 1 placeholder mismatch, 3 translation faults
Source: {"greeting": "Hello, {name}!", "delete": "Delete 5 files", "save": "Save changes", …}
| Flag | Meaning |
|---|---|
--strict | Also fail on warnings, such as an empty translation, and on extra keys that are not in the source. |
--pattern '{lang}/common.json' | Where the translations live. Default: where translate writes them, next to the source (de.json, app_de.arb, values-de/strings.xml). |
--json | Machine-readable result instead of the report. |
--sarif kaeris.sarif | Also writes a SARIF 2.1.0 report for GitHub, see below. |
Source file: any of the 13 formats (JSON, YAML, .strings, .xcstrings, Markdown/MDX, .po, ARB, Android XML, CSV, XLIFF, .properties, .resx, Fluent). Exit codes: 0 clean, 1 problems found, 2 wrong usage or a file it cannot read.
Guides by format: JSON · YAML · PO · XLIFF · ARB · .strings · .xcstrings · Fluent · .properties · .resx · CSV · Markdown · Android XML
CI recipes (in English): GitHub Actions · GitLab CI · pre-commit · Make and npm scripts
Glossary: one word per term, per language
--keep leaves a word as it is. When a term must be translated, but always the same way, put it in kaeris-glossary.json next to kaeris.json. translate gives the agreed words to the model, and check fails a translation that uses another word.
{"terms": [
{"term": "workspace", "pos": "noun",
"note": "the shared project area, not a desk",
"translations": {"de": "Arbeitsbereich",
"fr": "espace de travail",
"ru": "рабочее пространство"}},
{"term": "KAERIS", "keep": true}
]}
→ Glossary: 1 term(s), 1 kept as is ✗ fr: 1 translation fault(s): open: glossary: "workspace" should be "espace de travail" ! ru: 1 warning(s): leave: glossary: "workspace" should be "рабочее пространство" — not found, not even as an inflected form; check this line ✗ 1 translation fault
Source: {"open": "Open the workspace", "leave": "Leave the workspace"}. French said «bureau», Russian said «рабочей области».
A term counts as a whole word: Pro is not found in “product”. An inflected form of the agreed word passes. Where words change shape or join into compounds (Russian, Polish, German and others) a miss is a warning, not an error, because it can still be correct grammar. A list of words gives accepted variants. It works on every plan, the free demo included.
Findings on the line, inside the pull request
With --sarif every finding becomes a note on the exact line of the language file, next to your reviewer's comments.
- uses: kaeris-io/kaeris-action@v1
with:
mode: check
source: locales/en.json
languages: de,fr,es
sarif-file: kaeris.sarif
- uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: kaeris.sarif
"greeting": "Hallo, {name}!",
- "delete": "5 Dateien löschen",+ "delete": "50 Dateien löschen",KAERIS i18n · delete: number 5 from the source is missing or changed in the translation "save": "Änderungen speichern"
The report is written even on a clean run, because upload-sarif fails when the file is missing. Notes on the line are free on public repositories; private ones need GitHub Advanced Security. Without it the check still works: the job fails and the log lists every finding.