Skip to content

chore(i18n): drop 23 keys that no consumer can reach - #270

Merged
argszero merged 1 commit into
mainfrom
refactor/i18n-drop-23-dead-keys
Sep 21, 2026
Merged

argszero merged 1 commit into
mainfrom
refactor/i18n-drop-23-dead-keys

Conversation

@argszero

Copy link
Copy Markdown
Owner

Summary

The i18n reachability gate introduced by the preceding PR in this series finds 59 (key, class) pairs
that no consumer can reach. This PR deletes the 23 of them that are unambiguously dead — and, in the
same commit, removes their entries from the gate's sunset list, so the list stays exactly equal to
the computed unreachable set.

Nothing observable changes: these keys are not referenced by any T(...) call, any
data-i18n* attribute, or any other consumer in the UI corpus, and no value on screen is produced from
them.

Related Issue

Changes

  • ui/js/i18n.js: remove 23 keys from the Chinese pack and the same 23 from the English pack
    (46 lines = 23 × 2, i.e. 811 − 788).
  • src/i18n_pack.rs:
    • remove the matching 23 entries from UNREACHABLE_PACK_KEYS (59 → 36 pairs);
    • recalibrate ZH_KEY_COUNT / EN_KEY_COUNT (811 → 788).
  • ui/index.html: bump the cache-bust token for js/i18n.js (the pack file changed).

The deletion and the sunset-list edit are deliberately in the same commit: the gate asserts exact
equality, so splitting them would leave the tree red between the two commits.

Which 23 keys, and why they are safe

The classes below are mnemonics only. The sole criterion for deletion is zero consumers
(verified independently, see Tests), because the gate cannot catch the interesting mistake — see the
honesty note below.

class n keys
dup-sibling — a reachable key in the same pack already carries the same value 11 admin.emp.dept.unassignedcommon.unassigned; admin.emp.status.nearcommon.nearLimit; admin.emp.status.normalcommon.normal; common.callscnt.calls.unit; common.copysettings.ak.copy; common.monthops.stats.in.sub/out.sub; common.peoplecnt.people (substring, not equal); common.renamesettings.ak.rename; mk.countcnt.on; share.stats.listings.subcnt.hist; share.stats.used.subcnt.quota
zero-mock — the render was removed but the keys stayed 9 admin.emp.stats.members.sub.mock, …mock2, admin.org.demo, admin.raise.demo, ops.demo, ops.stats.in.sub.mock, ops.stats.keys.sub.mock, ops.stats.out.sub.mock, ops.stats.users.sub.mock
composite — the text is already embedded in a reachable composite value 2 login.forgot (⊂ login.foot), wallet.withdraw.disabled (⊂ wallet.note)
rename — the column header now uses another key 1 tx.col.key

⚠️ common.people is called out because its value is a bare / people, while the apparent twin
cnt.people is {n} 人 / {n} people — that is substring containment, not equality. So "has an
equal-valued twin" must not be treated as a precondition for deletion; it would wrongly flag this
safe key. The criterion is consumers, not twins.

Honesty note: the gate is not the safety net for this change

The gate proves the sunset list equals the computed unreachable set. That means:

  • deleting a key that is really unreachable → the gate goes red until you also remove its
    sunset entry (useful: it forces the pair to move together);
  • deleting a key that is actually reachable → the key simply leaves the pack; the unreachable set
    is unchanged and the gate stays green.

So the real safety net for this PR is the per-key zero-consumer verification (Tests), not the gate.
The gate keeps the bookkeeping honest.

What is deliberately left alone

The remaining 36 sunset entries are untouched, each for a stated reason:

  • old-design (30): superseded by the current design — retiring them is a larger product decision,
    not a cleanup, and belongs in its own change.
  • neutral-literal (4): login.email.ph, chat.close, settings.prefs.lang.zh, settings.prefs.lang.en
    — their values also appear as static text in index.html (placeholder=, , <option>), so
    removing the keys would turn the static-i18n gate red. Both packs carry the same literal by design.
  • weak-should-wire (1): admin.emp.dept.ok.unassigned should be wired up in code, not deleted.
  • host-decision (1): admin.usage.unit.points is a product-wording decision — out of scope here.

Also out of scope: the two dead placeholder divs (#dept-demo-note, #ops-demo-note) left behind by
the same commit that stopped rendering them. They are DOM, not pack keys; mixing them in would make this
PR two things at once.

Tests

  • cargo test passes — 302 passed / 0 failed, the same count as this branch's pre-change
    baseline (this change adds no new tests, by design: the work order says not to add a gate here).
  • cargo fmt --check passes
  • cargo clippy --all-targets -- -D warnings passes
  • The gate's own equality assertion is green after the change — i.e. the shortened sunset list
    (36 pairs) is still exactly equal to the computed unreachable set.
  • Constants are recalibrated from the gate's own failure message, never hand-computed, and the
    recalibration is applied as a loop (read the reported numbers → patch those → recompile →
    rerun) because packs() asserts the Chinese pack first and short-circuits, so on the first pass
    the English number does not exist yet. A one-shot parser crashes here; this is instrument
    shape, not a defect in the change.
  • Zero-consumer verification, independent of the work order: boundary-exact predicate
    ((?<![A-Za-z0-9_.-])key(?![A-Za-z0-9_.-])) over ui/index.html + ui/js/app.js +
    ui/js/api.js + ui/README.md (JS comments stripped; ui/js/i18n.js excluded — it is the
    pack) → 23/23 keys hit 0. Predicate shown to be tight, not loose: common.copy exact = 0
    while the live settings.ak.copy exact = 4.
  • Deletion shape verified: each of the 23 keys occurs exactly twice in ui/js/i18n.js
    (zh + en) → 46 lines removed; all 23 are present in the 59-pair sunset list.
  • Counts pinned after the edit: ZH_KEY_COUNT == EN_KEY_COUNT == 788; the pack files stay
    symmetric (zh key set == en key set).
  • Whole-queue landing simulation: this item is step 4 of the series and its prefix was measured
    green against a mirror carrying the whole queue plus this change — so the shortened list does
    not depend on a later item in the series.
  • (landing time) re-checked on the actual branch: cargo test 302/0 (baseline on the same tree
    before the edit: 302/0), cargo fmt --check clean, cargo clippy --all-targets -- -D warnings
    clean. The count above is the measured one, not the projection the draft carried (the draft
    projected ~299 because the series was shorter when it was written).

Checklist

  • Branch name follows the convention (refactor/…) — see the correction in §0 of the work order
  • Commit message follows the convention (chore(i18n): …)
  • PR targets the default branch (main)
  • Single responsibility, minimal change (only dead pack keys + their bookkeeping)
  • No unrelated changes (no DOM placeholders, no old-design keys, no consumer edits)

The reachability gate added by the preceding PR in this series finds 59
(key, class) pairs that no consumer can reach. Delete the 23 that are
unambiguously dead and, in the same commit, remove their sunset-list
entries, so the list stays exactly equal to the computed unreachable set
(59 -> 36 pairs).

Nothing observable changes: none of the 23 keys is referenced by any
T(...) call, data-i18n* attribute or other consumer in the UI corpus.

- ui/js/i18n.js: 23 keys removed from each pack (46 lines = 811 - 788)
- src/i18n_pack.rs: matching 23 sunset-list entries removed;
  ZH_KEY_COUNT / EN_KEY_COUNT recalibrated from the gate's own report
- ui/index.html: cache-bust token for js/i18n.js bumped
@argszero

Copy link
Copy Markdown
Owner Author

Self-review (R62, committer — allow_self_merge: true; gh pr review --approve on one's own PR always fails, pitfall #331).

Reviewed the diff and re-ran the verification on this exact branch.

What I checked

  • Shape: 3 files, +3 / −72. ui/js/i18n.js −46 (23 keys × zh+en), src/i18n_pack.rs −23 sunset-list entries plus the two recalibrated constants, ui/index.html one cache-bust token. No consumer file is touched — consistent with the claim that nothing observable changes.
  • Byte-exact edit sheet: the applier asserts old occurs exactly once for every one of the 69 edits before applying. It reported 69/69 clean, 0 mismatches. The two context-carrying deletions for tx.col.key (the only key whose zh and en lines are byte-identical, so a bare-line anchor matches twice) kept their surviving context line — verified after the edit: "tx.col.key": now 0, "tx.col.model": still 2.
  • Independent zero-consumer verification (this is the real safety net, not the gate — see the body's honesty note): boundary-exact predicate over ui/index.html + ui/js/app.js + ui/js/api.js + ui/README.md (JS comments stripped, ui/js/i18n.js excluded because it is the pack) → 23/23 keys hit 0. Predicate shown tight, not loose: common.copy exact = 0 while the live settings.ak.copy exact = 4.
  • Bookkeeping stayed exact: sunset list 59 → 36; keep-set categories untouched at 30 / 4 / 1 / 1; ("common.copy", in the Rust list 0.
  • Constants were read out of the gate, not hand-computed: the first run reported the Chinese count (788) and panicked before the English assertion could execute — exactly the short-circuit the edit sheet records — so this took a two-pass loop; both constants ended at 788, matching the independently measured 788 keys per pack.
  • Pack symmetry: 788 keys per pack, every key appears exactly twice, <script src="js/i18n.js?…"> tag count still 1.

Results on this branch

  • cargo test302 passed / 0 failed, identical to the pre-change baseline measured on the same tree (no new tests, by design).
  • cargo fmt --check clean; cargo clippy --all-targets -- -D warnings clean.
  • CI test / fmt / clippy (run 35592616253) passed.

Out of scope, deliberately: the 36 remaining sunset entries (30 old-design are a larger product decision, 4 neutral-literal have their values duplicated as static text in index.html, 1 weak-should-wire should be wired up rather than deleted, 1 host-decision), and the two dead placeholder divs left by the same commit that stopped rendering them — DOM, not pack keys.

Merging after this comment, since the change is a pure deletion of keys with zero consumers and the gate's exact-equality assertion is green.

@argszero
argszero merged commit 77b2a82 into main Sep 21, 2026
1 check passed
@argszero
argszero deleted the refactor/i18n-drop-23-dead-keys branch September 21, 2026 11:13
@argszero argszero mentioned this pull request Sep 21, 2026
12 tasks
argszero added a commit that referenced this pull request Sep 21, 2026
Ships the 10 PRs merged since v0.7.25 (#261-#270). No schema change, no config
change, so the deployment-side config.toml needs no edit.

One fact, one source / display must equal what it consumes (frontend, 6 places)
- #261 read the spendable half of the wallet payload when refreshing your own
  balance; #262 the transactions payload signature covers the time range, with
  one reload trigger shared by the four controls; #263 the settings controls are
  either wired or explicitly inert; #265 the re-list outcome comes from the same
  entry as its action; #268 the sharing form shows a plan's label, not its
  config id; #269 the ops card stops reading a key's status count as a health
  verdict.

i18n reachability
- #266 every pack key must reach a consumer (the gate), and #270 drops the 23
  keys that gate proved unreachable: ZH/EN key count 811 -> 788, sunset list
  59 -> 36.

Gateway
- #267 applies the body limit where axum actually reads it (per-route
  DefaultBodyLimit, 8 MiB on the three gateway routes; unauthenticated
  endpoints keep the 2 MiB default). This is the application half of rant
  2026-09-18T09:14:18. It also corrects the false v0.7.10 "raised to 70MB"
  CHANGELOG line, which described installing a layer rather than raising a limit.

- Cargo.toml / Cargo.lock: 0.7.25 -> 0.7.26.
- CHANGELOG.md: v0.7.26 entry plus the v0.7.10 correction.
- ui/index.html cache-bust left as-is: this release touches no UI file; the live
  values are app.js 20260921-2 / i18n.js 20260921-2.

cargo test 302 passed; cargo fmt --check clean; clippy -D warnings clean.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant