Uh oh!
There was an error while loading. Please reload this page.
fix(plugin-detail,i18n): ActivityTimeline's remaining 18 literals resolve from the packs - #7162
Conversation
…olve from the packs
objectui#7142 gave this component its first `t()` call (the empty-state title)
and filed the sweep that found the other 18. A zh activity tab read
"Activity(0)暂无活动记录" — one translated string in an otherwise English
component, beside a sibling (`RecordActivityTimeline`) that renders the same
surface fully translated.
Three groups, split by reachability rather than by defect:
- Relative timestamps + the card title render on EVERY activity tab. All five
were a pure lookup swap — the `en` pack value was already byte-identical to
the literal, and the sibling already used the same keys.
- The `formatFieldChange` sentences are assembled in code, so they needed new
keys WITH interpolation holes. Same reachability as the timestamps: they
render for any entry whose optional `description` is absent.
- The six filter chips + the chip group's accessible name need `filterable`,
which no host in this repo passes — reachable through the published export.
Ten new `detail.*` keys in all ten packs, mirrored byte-for-byte into
DETAIL_DEFAULT_TRANSLATIONS; no inline `defaultValue` anywhere (objectui#3517).
The quoted spans are inside each pack's value so every locale punctuates its
own way, which moves the de quote census 55 -> 58.
The chip group's aria-label is a deliberate English copy change: "Activity type
filter" now resolves `detail.filterActivity` ("Filter activity"), the key
`RecordActivityTimeline` already uses for the same control.
Also drops the unused `Filter` import (pre-existing eslint warning).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012wwHa4aaFybxXrfmfHioDMA `type -> key` map read as `t(KEYS[type])` gives a key no call site the i18n scanners can resolve: `detail.allFilter`, `createsFilter`, `deletesFilter` and `statusChangesFilter` each rendered correctly but read as unreferenced — `check-i18n-dead-keys` listed all four as needs-review in the `detail` namespace, and `check:i18n-keys` counted the site among its dynamic-head bucket. Static `t()` calls behind a `filterLabel(type, t)` switch instead — the shape the sibling `RecordActivityTimeline`'s `getFilterOptions` already uses. Chip order moves to an explicit `FILTER_ORDER` array, which is what `Object.keys(FILTER_LABELS)` was supplying before. Measured: `detail` needs-review 21 -> 17, dynamic call sites with no static head 36 -> 35, both gates still exit 0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012wwHa4aaFybxXrfmfHioDM
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
os-warren
commented
Sep 1, 2026
✅ Reviewed — will arm on greenCI still settling; auto-merge SQUASH goes on once all 30 read green. Posting now because the scope correction here is mine to record.
|
Uh oh!
There was an error while loading. Please reload this page.
Fixes#7149
ActivityTimelinehad 18 hardcoded English literals left after #7142 fixed oneof them. All 18 now resolve from the ten packs.
What this finishes, honestly
#7142 gave this component its first
t()call — the empty-state title — andfiled the sweep that found the rest. The state it left behind is what this PR
repairs: a zh activity tab read
"Activity(0)暂无活动记录", the empty statecorrect and the card title beside it still English. One translated string in a
component that was otherwise entirely untranslated, next to a sibling
(
RecordActivityTimeline) that renders the same surface fully in Chinese.Re-derived on
origin/mainhead85b5077d7— the card's line numbers stillland exactly, and the re-sweep confirms the count.
The re-sweep: 18 literals, 20 occurrences
The card's 18 is right;
(empty)is one literal at three sites (L66, L67, L74),which is where the other two occurrences go. No nineteenth: the only other
locale-sensitive thing in the file is
date.toLocaleDateString()on thepast-a-week branch, which already localizes and is byte-identical to the
sibling's own tail — not a literal, deliberately untouched.
Three groups, split by reachability rather than by defect
formatFieldChangesentencesdescription)All three shipped. The measurement that decided it: the card grouped the
formatFieldChangesentences apart because they need interpolation holes, butthat is a key-shape difference, not a reachability one.
descriptionisoptional on
ActivityEntry, andformatFieldChangeruns for every renderedentry — so those six sentences render on exactly the same surface as the
timestamps. Shipping the timestamps without them would have reproduced #7142's
half-done shape one level down:
"Ada Changed Status from "open" to "closed" 5分钟前".The chips are genuinely less reachable —
DetailViewmounts this component attwo sites (
DetailView.tsx:1586,:1713) and neither passesfilterable,so no host in this repo renders them. They are included rather than deferred
because
filterableis already published API on an already-shipped export, and3 of those 7 need no new key at all; the marginal cost was 4 keys on a PR
already touching all ten packs. Value accrues to outside consumers only, and
that is stated rather than implied.
Pack verification — read out of the pack objects, never grepped
The packs are nested, so a dotted-key grep returns a false zero for keys
that exist. Every mapping was re-verified by importing the ten pack objects and
resolving dotted paths, with both controls live:
detail.back,detail.noActivity,detail.edit:10/10eachdetail.zzzAbsentControl7149,detail.noSuchKeyAtAll:0/10All 7 inherited "existing key" mappings are exact (
envalue byte-identicalto the literal), each present in 10/10 packs:
detail.justNow'just now'·detail.minutesAgo'{{count}}m ago'·detail.hoursAgo·detail.daysAgo·detail.activity'Activity'·detail.fieldChangesFilter'Field Changes'·detail.comments'Comments'A second probe over all 2845
enleaf keys confirmed the new copy has noexisting home —
Creates,Deletes,Status Changes,(empty),Created this record,Deleted this record,Updated record,Activity type filterall return 0 exact-value hits, withAll,Createand
Deletereturning non-zero on the same instrument as the live control.One deliberate English copy change
The chip group's
aria-labelwas"Activity type filter". It now resolvesdetail.filterActivity('Filter activity') — not an exact match, and thecard flagged it as such. Adopted anyway, and reported rather than quietly
swapped:
RecordActivityTimeline.tsx:345already uses that exact key for theaccessible name of its activity filter. Minting a near-synonym would fork
one control's name across two components — the same argument
useDetailTranslation.tsalready records forcommon.resizeDrawer. The Englishaccessible name changes; no host in this repo renders the control today.
Ten new keys, all ten packs
detail.allFilter·createsFilter·deletesFilter·statusChangesFilter·activityEmptyValue·activityFieldChanged·activityCreated·activityDeleted·activityStatusChanged·activityUpdatedNo inline
defaultValueanywhere (#3517) — every key resolves from the packs orfrom
DETAIL_DEFAULT_TRANSLATIONS, which mirrors the ten newenrowsbyte-for-byte (verified against the pack object;
defaults-maps-mirror-en-packgreen).
Quotes live inside each pack's value so every locale punctuates its own way,
following each pack's measured convention rather than a guess: de
„…“, zh“…”, ja「…」, fr/ru«…», en/ko/es/pt/ar ASCII. This moves the de quotecensus, and
de-quote-pairing-3876.test.tsis updated with it:okSpansand{open, close, rdq}go 55 → 58 (three interpolated spans;rdqstays 0, soeach addition is a matched pair), with the comment naming why, in the file's
established style.
Evidence
Ablation — prediction stated before running: reverting only
ActivityTimeline.tsxtoorigin/main(packs left in place) turns the newlocale assertions red. Mutation proved on disk by blob hash
(
50c29a43…→6bb6ce32…, equal toorigin/main:that path) and markercount (
'just now'literal 0 → 1). Restore proved by state —git diff HEAD,git diff --cachedandgit status --shortall empty, blob back to50c29a43…. No rebuild leg needed and it is stated why: the test imports./ActivityTimelinerelatively, so vitest loads the source, notdist.Result
10 failed | 10 passed (20). The ten reds are every locale-dependentassertion. The ten greens are the discriminating half and are green by
construction on the unfixed component — the six that came with #7142, the two
no-provider English cases (each
enpack value is byte-identical to the literalit replaces, so English cannot tell a lookup from a literal), the
author-
descriptionshort-circuit, and the no-raw-key sweep.Non-
enrender — the actual defect is "stays English in a zh session", sozh/ja/ar/de carry the load:
Plus the exact repair, asserted whole: the zh card's
textContentis now'活动(0)暂无活动记录'— the string the card recorded as half-done, minus theEnglish half.
Gates — all run at
ea5e7c052, exit codes captured before any pipe:vitest run packages/plugin-detail packages/i18n defaults-maps-mirror-en-pack→
Test Files 181 passed (181)·Tests 2066 passed (2066)type-check(both packages, script name echoed, not a zero-match) →Doneeslintboth packages →0 errors;ActivityTimeline.tsxcontributes nofindings, so the unused
Filterimport warning the card noted is gonecheck:i18n-keys→ "Every in-scope call-site key resolves against the en pack(2855 keys) … every call site passes exactly the arguments that value has
holes for"
check:i18n-drift→ "0 en value(s) changed (10 key(s) added, 0 removed)" —additive, no translation follow-ups owed
all-locales-key-parity,untranslated-identity-4376,fallback-placeholder-spelling-3512,de-quote-pairing-3876— all inside the959-test
packages/i18nrunchangeset:check→ "✅ All workspace packages are in the changeset fixedgroup. ✅ No changeset declares a
majorbump."Also in this PR
The second commit resolves the chip labels with static
t()calls behind afilterLabel(type, t)switch instead oft(KEYS[type]). A key that onlyappears as a map value has no call site the scanners can resolve: all four new
chip keys rendered correctly while reading as unreferenced. Measured
detailneeds-review 21 → 17 and dynamic call sites with no static head36 → 35. Same shape as the sibling's
getFilterOptions.RecordActivityTimeline.tsxwas read-only reference throughout and isunchanged.
Generated by Claude Code