GitHub Action · @angular/localize

i18n coverage for angular-i18n

The Lokali GitHub Action detects Angular's src/locale/messages.{lang}.xlf XLIFF/JSON sibling layout (and any nested translation files under src/locale/messages.{lang}/), parses component $localize template tags inside the PR diff, and flags every missing entry — including ICU plural/select drift and stale @@id target-state rows left behind by an en rename.

src/locale/messages.{lang}.xlf, messages.{lang}.json
Scan your Angular repo → Install the GitHub App

What the Action checks for angular-i18n

Lokali reads angular.json for the configured i18n.locales[] list, walks the XLIFF/JSON sibling layout angular.json declares, and cross-references every $localize template tag in the diff to surface real coverage gaps — including ICU @@plural / @@select drift.

DETECT — XLIFF/JSON sibling
Default src/locale/messages.{lang}.xlf plus its messages.{lang}.json sibling — the layouts @angular/localize extraction emits out of the box, treated as one locale set.
DETECT — $localize template tag
Component files that call $localize`…` in templates or TypeScript — the Action diffs the tag IDs against messages.{lang}.xlf to find missing keys.
DETECT — i18n attributes
i18n="@@id" and i18n-… attribute declarations on element templates — each with its own extracted ID that must match a translation-file entry.
DETECT — nested translation files
src/locale/messages.{lang}/… nested folders for teams that split per-feature namespaces (cart/, auth/, billing/) under a single locale root.
DETECT — plurals & select ICU
@@plural and @@select markers in messages.xlf — the Action diffs the per-locale branch set against en so missing fr/de/ar ICU branches surface as ICU gaps, not flat missing keys.
DETECT — angular.json i18n config
Respects i18n.locales[] from angular.json as the source of truth for the locale set — so locales you haven't built yet are skipped, not flagged.

Common coverage gaps the Action flags

These three failure modes hit real @angular/localize apps — each one lands in the comment as a per-locale missing-keys row you can act on before the PR merges.


Sample Action report on an angular-i18n PR

What lokali[bot] posts in the PR thread after a push touches src/locale/messages.en.xlf and src/app/billing/invoice-summary.component.ts. The same table is dropped into the first-run Coverage delta PR.

lokali[bot] commented · i18n Coverage Report
## i18n Coverage Report · angular-i18n

Baseline: `src/locale/messages.en.xlf` · 218 keys · locales: 6
Package detected: `@angular/localize@17.x` · `angular.json` i18n.locales[6]

| Locale   | Coverage | Total | Translated | Missing |
|----------|----------|-------|------------|---------|
| 🟢 en    |   100%   |  218  |    218     |    0    |
| 🟢 pt-BR |    97%   |  218  |    212     |    6    |
| 🟡 fr    |    91%   |  218  |    199     |   19    |
| 🟡 de    |    88%   |  218  |    192     |   26    |
| 🔴 ja    |    69%   |  218  |    151     |    67    |
| 🔴 ar    |    44%   |  218  |     96     |   122   |

Top missing keys (across all locales):
  • `billing.invoiceSummary.exportCta`  (missing in fr, de, ja, ar, pt-BR — from src/app/billing/invoice-summary.component.ts)
  • `cart.checkout.discountBanner`  (missing in de, ja, ar, pt-BR — @@id rename, fr has stale target-state row)
  • `common.placeholder.welcomeName`  (placeholder drift in de)

ICU plural drift per locale:
  • `cart.itemsCount` → en has `one`/`other`, fr has only `other` → missing `one`
  • `cart.itemsCount` → en has `one`/`other`, de has only `other` → missing `one`

Coverage delta PR opened: #1234 — `lokali: coverage-delta-2026-08-17`

Scan an angular-i18n repo or install the Action

One URL works for both. Coverage comments arrive on your next PR, and the Coverage delta PR closes the gap automatically — across XLIFF/JSON siblings, $localize template tags, and ICU plural/select drift.

Run a free i18n audit → ← Back to integrations hub