Skip to content

fix(hax-body): declare 2 undeclared i18n keys, drop 5,810 orphaned locale keys - #822

Merged
btopro merged 1 commit into
haxtheweb:masterfrom
SanikaA3:fix/3035-hax-locale-orphan-cleanup
Sep 8, 2026
Merged

fix(hax-body): declare 2 undeclared i18n keys, drop 5,810 orphaned locale keys#822
btopro merged 1 commit into
haxtheweb:masterfrom
SanikaA3:fix/3035-hax-locale-orphan-cleanup

Conversation

@SanikaA3

@SanikaA3 SanikaA3 commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Refs #3035.

While picking up #3035 I measured the drift against the source before starting the translation work, and the numbers came out differently than the issue describes. Along the way two keys turned up that are read off this.t but never declared anywhere — one of which is a live bug. This PR fixes both and aligns the locale files.

Happy to be corrected if I've misread the intent of the 182-key set.

What the issue reported vs. what the code shows

#3035 reads the 182-key set as the aggregate this.t across ~35 sub-components, with hax.en.json (122) as the stale side, and proposes translating ~60 keys × 102 locales (~6,120 strings).

Scanning it the other way around:

measurement result
files calling registerLocalization({ namespace: "hax" }) 12 (not ~35), all under elements/hax-body
union of this.t across those 12 files 122 keys
keys in hax.en.json 122 keys
in code but missing from hax.en.json 0
in hax.en.json but missing from code 0

hax.en.json and the code were already in sync. The 182-key set exists only in the translated hax.<lang>.json files, and those keys were already translated — canonical coverage was 98.0% (12,198 / 12,444). So there was no cohort of strings rendering English-only for this reason.

The extra keys are pre-rename names from the text editor toolbar. Current code declares boldButton, italicButton, underlineButton, strikethroughButton, unorderedListButton, emojiButton, symbolButton, indentButton, outdentButton; the locale files still carried bold, italic, underline, crossOut, bulledList, insertEmoji, insertSymbol, indent, outdent. The bulledList typo is a good tell — the rename happened and the locale files were never re-aligned.

Two keys read but never declared

1. copiedToClipboard — renders undefined for English users

// elements/hax-body/lib/hax-view-source.js:231
HAXStore.toast(this.t.copiedToClipboard);

No inline fallback. The key was absent from hax.en.json but present in all 102 translated locale files. Because i18n-manager copies every key it finds in a locale file onto the element's t (i18n-manager.js:519), the behaviour was inverted from what the issue describes:

  • non-English users → a correctly translated "Copied to clipboard" toast
  • English usersundefined

Declaring it makes the existing translations reachable from the canonical set. No new translation work is required — every locale already has this string.

2. pages — silently untranslatable

hax-tray renders two sibling <hax-stax-browser> labels in the same template:

label="${this.t.templates}"          // declared → translatable
label="${this.t.pages || "Pages"}"   // undeclared → always English

The inline fallback meant it always rendered, so it was never noticed, but the key reached no locale file and could not be translated in any language. Declared next to templates, and the now-redundant fallback removed.

What changed

  • Declare copiedToClipboard in hax-view-source.js
  • Declare pages in hax-tray.js, drop the || "Pages" fallback
  • Remove the remaining 59 dead keys from the locale files
  • Align all 103 locale files to one canonical 124-key set, in en.json order
key entries removed 5,810
key entries added 104
locale payload 741.9 KB → 497.7 KB (−244.2 KB, −32.9%)

No pre-existing translated value is altered — 12,668 values compared before and after, 0 changed.

New tooling

scripts/sync-shared-namespace-locales.js addresses the "why the skill tooling can't fix this automatically" note directly. Single-file extractors can't describe a namespace many files contribute to: pointed at hax-body.js they see only the 1 key declared there and would overwrite hax.en.json with that fragment. This scans every file registering the namespace.

It also reports keys read but never declared. Run against master it names both bugs above:

WARNING: 2 key(s) read but never declared:
  pages  (lib/hax-tray.js:1024)
  copiedToClipboard  (lib/hax-view-source.js:231)

Dry run by default; --write applies. It is idempotent — a second run reports 0 changes.

Verification

elements/hax-body/test/hax-i18n-namespace.test.js is added as a regression guard. Against master it fails, including expected undefined to be a string for hax-view-source's this.t.copiedToClipboard — the bug reproduced in a browser rather than inferred from a grep.

check master this branch
hax-body suite 12/12 17/17
keys read but never declared 2 0
locale files matching canonical set 1 / 103 103 / 103
runtime merge simulation (empty resolutions) 0 of 9,682
pre-existing values altered 0 of 12,668

scripts/generate-translation-manifest.js regenerates with no diff, so step 6 of the issue's plan is already satisfied.

On the issue's step 5

Steps 1–4 and 6 still apply. Step 5 — "~60 keys × 102 locales" of net-new translation — would have translated 59 dead keys plus one that is already translated everywhere. Removing them instead is what produces the −32.9% payload reduction.

Notes

  • 30 keys are declared in this.t but not read within hax-body. Left untouched: they're the declared contract and may be consumed in ways static analysis can't see. Removing them would be a separate, riskier change.
  • CI does not currently run tests. .github/workflows/build.yml does checkout → setup Node → yarn install → rebuild the elements README, with no yarn test or lerna run test step, so the guard test added here won't gate merges. Flagging it rather than expanding this PR's scope — happy to open a separate issue.

🤖 Generated with Claude Code

Two keys in the shared "hax" namespace were read off `this.t` but never
declared in any component's this.t block. Both are fixed here, and the
locale files are re-aligned to the resulting canonical key set.

1. copiedToClipboard - elements/hax-body/lib/hax-view-source.js:231

   `HAXStore.toast(this.t.copiedToClipboard)` has no inline fallback. The
   key was absent from hax.en.json but present in all 102 translated
   locale files, so behaviour was inverted: non-English users saw a
   correctly translated toast after "Copy HTML", English users saw
   `undefined`. Declaring it makes the existing translations reachable
   from the canonical set - no new translation work is needed.

2. pages - elements/hax-body/lib/hax-tray.js:1024

   hax-tray renders sibling <hax-stax-browser> labels: `this.t.templates`
   (declared, translatable) and `this.t.pages || "Pages"` (undeclared).
   The fallback meant it always rendered, so it was never noticed, but
   the key reached no locale file and could not be translated in any
   language. Declared next to templates and the now-redundant fallback
   removed.

The other 59 keys carried by the locale files are dead. They are legacy
names from the text editor toolbar rename (bold -> boldButton,
bulledList -> unorderedListButton, crossOut -> strikethroughButton, and
so on); the locale files were never re-aligned afterwards.

i18n-manager copies every key it finds in a locale file straight onto the
element's `t` (elements/i18n-manager/i18n-manager.js:519), so those keys
were fetched, parsed, assigned across all 12 sub-components sharing the
namespace, and swept into the reactive spread - then never read.

All 103 locale files are now aligned to one canonical 124-key set. No
pre-existing translated value is altered.

  key entries removed  5,810
  key entries added      104   (pages x 103, copiedToClipboard in en.json)
  locale payload       741.9 KB -> 497.7 KB  (-244.2 KB, -32.9%)

Adds scripts/sync-shared-namespace-locales.js. Single-file extractors
cannot describe a namespace that many files contribute to: pointed at
hax-body.js they would see only the 1 key declared there and overwrite
hax.en.json with that fragment. This script scans every file that
registers the namespace. It also reports keys that are read but never
declared - run against master it names both bugs above directly:

  WARNING: 2 key(s) read but never declared:
    pages  (lib/hax-tray.js:1024)
    copiedToClipboard  (lib/hax-view-source.js:231)

Adds elements/hax-body/test/hax-i18n-namespace.test.js as a regression
guard. Against master it fails, including `expected undefined to be a
string` for hax-view-source's this.t.copiedToClipboard - the bug
reproduced in a browser rather than inferred from a grep.

Verification:
  - hax-body suite: 12/12 on master, 17/17 on this branch
  - 12 files register namespace "hax", all under elements/hax-body
  - aggregate this.t is 124 keys, matching hax.en.json exactly
  - keys read but never declared: 2 on master, 0 here
  - runtime merge simulated per locale: 9,682 resolutions, 0 empty
  - 12,668 pre-existing values compared: 0 altered
  - script is idempotent; a second run reports 0 changes
  - generate-translation-manifest.js regenerates with no diff

Refs #3035

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@SanikaA3
SanikaA3 requested a review from btopro as a code owner September 7, 2026 20:27
@vercel

vercel Bot commented Sep 7, 2026

Copy link
Copy Markdown

@SanikaA3 is attempting to deploy a commit to the HAXTheWeb Team on Vercel.

A member of the Team first needs to authorize it.

@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown

All contributors have signed the CLA. Thank you!
Posted by the CLA Assistant Lite bot.

@SanikaA3

SanikaA3 commented Sep 7, 2026

Copy link
Copy Markdown
Contributor Author

I have read the CLA Document and I hereby sign the CLA.

@SanikaA3

SanikaA3 commented Sep 7, 2026

Copy link
Copy Markdown
Contributor Author

recheck

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The changes are narrowly scoped to i18n key declaration/synchronization and include a regression test to prevent reintroducing undeclared or drifting locale keys.

Pull request overview

This PR fixes two previously-undeclared i18n keys in the shared hax namespace (one causing an undefined toast for English users), and then normalizes the hax.*.json locale files to a single canonical key set. It also adds a script to keep shared-namespace locales in sync and a regression test to prevent future drift.

Changes:

  • Declare copiedToClipboard (used by hax-view-source) and pages (used by hax-tray) in component this.t defaults and in hax.en.json.
  • Remove thousands of orphaned/legacy keys from translated locale files and align all hax.<lang>.json files to the canonical key set/order.
  • Add a sync script for shared-namespace locales plus a regression test covering key presence and locale keyset parity.
File summaries
File Description
elements/hax-body/test/hax-i18n-namespace.test.js Adds regression coverage for declared/read keys and locale keyset parity.
elements/hax-body/lib/hax-view-source.js Declares copiedToClipboard in this.t defaults for the hax namespace.
elements/hax-body/lib/hax-tray.js Declares pages in this.t defaults and removes inline fallback usage.
scripts/sync-shared-namespace-locales.js Adds a utility to sync shared-namespace locale keysets across contributing files.
elements/hax-body/locales/hax.af.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.am.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.ar.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.az.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.be.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.bg.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.bn.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.bs.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.ca.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.co.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.cs.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.cy.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.da.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.de.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.el.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.en.json Adds canonical English strings for copiedToClipboard and pages.
elements/hax-body/locales/hax.eo.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.es.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.et.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.eu.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.fa.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.fi.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.fo.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.fr.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.fy.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.ga.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.gl.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.gn.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.gu.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.ha.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.haw.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.he.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.hi.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.hr.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.hu.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.hy.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.id.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.ig.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.is.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.it.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.ja.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.jv.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.ka.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.kk.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.km.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.kn.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.ko.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.ku.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.ky.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.lb.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.lo.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.lt.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.lv.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.mi.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.mk.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.ml.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.mn.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.mr.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.ms.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.mt.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.my.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.nb.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.ne.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.nl.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.no.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.ny.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.om.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.pa.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.pl.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.pnb.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.ps.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.pt.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.qu.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.ro.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.ru.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.sd.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.si.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.sk.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.sl.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.sn.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.so.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.sq.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.sr.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.sv.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.sw.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.ta.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.te.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.tg.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.th.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.tk.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.tl.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.tr.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.tt.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.uk.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.ur.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.uz.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.vi.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.wuu.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.xh.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.yi.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.yo.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.zh.json Locale alignment (canonical keyset/order + pages).
elements/hax-body/locales/hax.zu.json Locale alignment (canonical keyset/order + pages).
Review details
  • Files reviewed: 107/107 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +5 to +9
const LOCALES = "/elements/hax-body/locales";

async function loadLocale(lang) {
const res = await fetch(`${LOCALES}/hax.${lang}.json`);
expect(res.ok, `hax.${lang}.json should be fetchable`).to.be.true;
@btopro
btopro merged commit f2b7af7 into haxtheweb:master Sep 8, 2026
2 of 4 checks passed
@github-actions github-actions Bot locked and limited conversation to collaborators Sep 8, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants