The Lokali GitHub Action scans your React binding's default public/locales/{lng}/{namespace}.json layout on every PR, posts a per-language coverage table, and opens a Coverage delta PR the moment a new key lands in en.
What the Action checks for react-i18next
Lokali watches the same globs the React binding's i18n.ts provider already knows. If you ship a non-default layout, the action falls back to scanning from the repo root for any {lng}/{namespace}.json pair.
public/locales/{lng}/{namespace}.json layout used by useTranslation() providers out of the box.src/i18n.ts|js and respects any explicit backend.loadPath overrides the developer set.common and translation as independent namespaces and reports each one separately in the PR comment.public/locales/{lng}/{namespace}.yml files are picked up when present alongside JSON.public/locales/{lng}/auth/login.json are scanned recursively and counted under their parent namespace.{name} and {{count}} placeholders are diffed per locale so untranslated placeholders show up as missing keys, not silent fallbacks.Common coverage gaps the Action flags
These are the three failure modes we see most often in real react-i18next repos. Each one ships the same way — as a per-locale missing-keys row in the PR comment.
pt-BR missing in common.json after an en renamecommon.signIn to common.signInCta in en/common.json leaves pt-BR/common.json with the old key — the comment flags 1 missing under the common namespace and opens a one-key translation PR.en: "Welcome, {{name}}!" vs de: "Willkommen!" — the German file is missing the placeholder. The Action diffs placeholder tokens per key and reports placeholder drift on the affected row.fr, dear and he first, with English-only baseline, leaves fr and de showing 100% on the new RTL keys but the existing fr/de keys drifting. The Action reports the asymmetry per-locale rather than as a global drop.Sample Action report on a react-i18next PR
What lokali[bot] posts in the PR thread after a push touches public/locales/en/common.json. The same table is also dropped into the first-run Coverage delta PR.
## i18n Coverage Report · react-i18next Baseline namespace: `common` · 184 keys · locales: 6 | Locale | Coverage | Total | Translated | Missing | |----------|----------|-------|------------|---------| | 🟢 en | 100% | 184 | 184 | 0 | | 🟢 pt-BR | 98% | 184 | 181 | 3 | | 🟡 fr | 92% | 184 | 170 | 14 | | 🟡 de | 88% | 184 | 162 | 22 | | 🔴 ja | 71% | 184 | 131 | 53 | | 🔴 ar | 42% | 184 | 78 | 106 | Top missing keys (across all locales): • `common.signInCta` (missing in fr, de, ja, ar, pt-BR) • `common.billing.invoiceSuccess` (missing in de, ja, ar, pt-BR) • `common.placeholder.welcomeName` (placeholder drift in de) Coverage delta PR opened: #1234 — `lokali: coverage-delta-2026-08-11`
One URL works for both. Coverage comments arrive on your next PR, and the Coverage delta PR closes the gap automatically.