doxa-maps/library/i18n
doxa-maps-88655c70 a177f98aff
Some checks are pending
build-gate / build (push) Waiting to run
legend: the controls no longer read their own i18n key aloud
The legend's toggle button announced the literal string "aria.toggleLegend" to
screen readers. Not in translated locales only — in English too. vue-i18n echoes
the key when a lookup misses, and `toggleLegend` was absent from every catalog
while its two siblings in the same file, backToLegend and closeDetailView, were
present. Six call sites across LegendDesktop.vue and LegendMobile.vue, desktop
and mobile alike.

aria-label is the one string a sighted tester never sees. Every other
untranslated string in this tree got caught because somebody looked at the
screen; this one is only ever spoken aloud, so it survived every visual QA pass.

A second instance turned up in the same sweep with the opposite shape:
SemanticTreeLegend hardcoded 'Collapse legend' / 'Expand legend' into both title
and aria-label with no t() at all — correct English, and untranslatable. One key
missing FROM the catalog, one string never routed TO it.

Adds toggleLegend, collapseLegend and expandLegend to all 11 aria.json catalogs
and routes the hardcoded pair through t(). check:i18n-parity already guards them.

New gate smoke:aria-labels asserts BOTH shapes, because either assertion alone
is insufficient: "no raw keys" passes a hardcoded English label forever, and
"labels differ across locales" passes a raw key, since a key is identical in
every locale. It detects a raw key by SHAPE — dotted, camelCase, no spaces —
which is locale-independent and works on the Arabic and Chinese pages where no
English word list could.

Mutation-tested twice. Removing the catalog key fails with the raw key named, on
all four viewport/locale combinations. Hardcoding one label back to English
initially PASSED: the differ count dropped 6 to 5 and nothing asserted on it —
the same "a number printed but not asserted on is decoration" shape this tree
keeps rediscovering — so the gate gained a LOCALIZED_FLOOR ratchet and now fails
that too.

Verified the fix generalizes beyond the profile it was found on: country-posters
reports 182 of 190 accessible names localized, 0 raw keys. Rebuilds the six
bundles that embed a legend; check:sri confirms every leaf matches. Wave 34/34.
2026-09-24 13:16:18 +00:00
..
locales legend: the controls no longer read their own i18n key aloud 2026-09-24 13:16:18 +00:00
index.js Localize tab labels for it/ro/zh, and prove the catalog is actually read 2026-09-24 06:23:30 +00:00
README.md consolidate docs into documentation/ and deployment shells into deployment/ 2026-09-21 23:23:23 +00:00

library/i18n/ — the map's translations

One of the three customization seams (colors/, api/, i18n/). This one owns every user-facing string. Translations are one folder per locale, one JSON file per area, inlined at build time into a Vue I18n instance (one per <doxa-map>, so embeds don't bleed state).

  i18n/
  ┌───────────────────────────────┐
  │ index.js   ← engine, don't edit │
  │ locales/                        │
  │   en/  buttons.json legend.json …│  ← en is the source of truth + fallback
  │   es/ ar/ zh/ …  (same keys)     │  ← 11 shipped locales, drop in your own
  └───────────────────────────────┘
     filename → namespace:  buttons.json → $t('buttons.prayForThem')
  • 11 shipped locales; ar renders right-to-left (RTL_LOCALES).
  • Add a language by copying locales/en/ to locales/<code>/ — never by editing index.js (except adding the code to SUPPORTED_LOCALES).
  • Missing keys fall back to English silently.

Adding or overriding a locale? The full contract (locale matrix, checklist, integration) is in ../../documentation/architecture/i18n.md.