Repository navigation
fix(collaboration,console): two more dates read the display locale, not the UI language (objectui#10375) - #10431
Conversation
…ot the UI language CommentThread's absolute date for comments seven or more days old, and the approvals inbox's date for timestamps 30 or more days old, formatted with the UI language. Both now take their tag from useDisplayLocale(), so a regional display locale (de-CH under an English UI) reaches them. The relative buckets stay on the translation function and the UI language. The machine-locale census keeps its CommentThread lit control: the bare fallback call in formatAbsoluteDate's catch is unchanged, and only its reason text now names the display locale its try passes. Refs objectui#10375. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BP8CMtACxTdLjqR6rhd33C
|
changeset-claim-re-read
|
✅ 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
|
Contract reviewServed-tier: Rendered by an isolated review subagent spawned by the ① Derived judgments
② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
Fixes #10375
Clause-②: no
Two more date faces take their locale from
useDisplayLocale()instead of the UI language. Implemented by a dispatched dev subagent under seatdomain:ui#4(sessionsession_01BP8CMtACxTdLjqR6rhd33C), on base74fcda829.Premise, re-derived on
74fcda829Both sites were on
origin/mainas the card and the triage comment (5823975048) quote them:ApprovalsInboxPage.tsx, informatRelative:try { return new Date(s).toLocaleDateString(language); } catch { return s; }, withlanguagefromuseObjectTranslation(). The page already readuseDisplayLocale()for every other date and number face.CommentThread.tsx:formatAbsoluteDate(date, language)randate.toLocaleDateString(language), withlanguagefromuseCollaborationTranslation(), reached fromformatTimestampfor comments seven or more days old.A one-off probe that walks the census population exactly as
machineLocaleCensus-9909.test.tsdoes (same manifests, same tooling filter, same comment masker, same matcher) finds exactly these two call sites passing alanguage-named first argument at74fcda829, and none at this branch's head.What changed
CommentThreadcallsuseDisplayLocale()in the component body, next touseCollaborationTranslation(), and passes the tag toformatTimestampand on toformatAbsoluteDate.languagehad no other use there, so it is no longer destructured. The parameter is renamedlocale, and the two doc comments now say the date follows the display locale and why objectui#3441 had chosen the session language. The innertry/catchfor a malformed tag is unchanged.ApprovalsInboxPage:formatRelative's date bucket passes the page's existingdisplayLocale, and the callback's dependency list names it instead oflanguage.languagehad no other use in the page, so it is no longer destructured.t/trand the UI language.machineLocaleCensus-9909.test.ts: theCommentThread.tsxDECLARED entry (the lit control) keeps its file, expression and count. The bare call informatAbsoluteDate's catch is still there, still counted once, and the lit-control case is green. Only itsreasontext changed, because its "whosetryalready passes the session language" would have gone false; it now names the display locale.patchfor@object-ui/collaborationand@object-ui/console. The dispatch named collaboration only; console is added because its source changes user-visibly too, and a changeset that describes the inbox without naming its package would leave the console changelog without the entry. The body states both behaviour changes.Mechanism readings (the dispatch's A1 to A4)
useLocalization()returns an empty object anduseDisplayLocale()falls through touseObjectTranslation().language, the same taguseCollaborationTranslation()returned before. Measured with a new case incomment-thread-no-provider-fallback.test.tsx: everytoLocaleDateStringcall receives exactly'en', never the machine locale. The file's existing week-old-date case stays green. The file still does not import@object-ui/i18n; its docblock now says the tag is the display locale's fallthrough. Under anI18nProviderwith noLocalizationProvider, the existingzh/de/en_USabsolute-date cases incomment-thread-i18n.test.tsxstay green unchanged.de-CHdisplay locale reads3d ago. It stayed green on both ablation legs, as an invariance pin should.@object-ui/collaborationalready depends on@object-ui/i18n(workspace:*), which exportsuseDisplayLocale. No dependency was added. The collaboration pins record the formatter argument withvi.spyOn(Date.prototype, 'toLocaleDateString')instead of@object-ui/test-support, which that package does not declare.Tests
New pins follow the objectui#10331 shape: the real
I18nProviderwith an English UI, and the display locale declared throughLocalizationProvider, so the display locale is the only variable.packages/collaboration/src/__tests__/comment-thread-display-locale-10375.test.tsx: thede-CHpin (4.3.2020, and equal to the runtime's ownde-CHrendering), theen-UScontrol (3/4/2020), a differential no runner locale can satisfy, an argument check (everytoLocaleDateStringcall receivedde-CH), the relative-bucket invariance case, and a malformed display locale (de_CH) that still falls back to the runtime date, never the raw ISO string.apps/console/src/pages/system/ApprovalsInboxPage.displayLocale-10375.test.tsx: the same pin, control, differential and relative-bucket cases, read from the queue cell whosetitletooltip is the (untouched)formatDateface, plus the@object-ui/test-supporttripwire.consoleFaces.displayLocale-9909.test.tsxcould not see this defect: it stubs the UI language to'en'and its tripwire tolerates any declared tag, so only the machine locale reads as red there.Runs from the repo root at
5a40f03e0, through the shared verify lock:pnpm exec vitest run --maxWorkers=2 packages/collaboration/ apps/console/src/pages/system/ apps/console/src/pages/consoleFaces.displayLocale-9909.test.tsx apps/console/src/__tests__/approvalsInboxComponentRef.test.tsx packages/app-shell/src/views/__tests__/DeclaredActionsBar.test.tsx packages/i18n/src/__tests__/dead-pack-keys-retired-8754.test.ts packages/i18n/src/__tests__/machineLocaleCensus-9909.test.tsgivesTest Files 30 passed (30)andTests 264 passed (264). That covers both new pins, every collaboration suite, every approvals-inbox suite, the other suites that nameApprovalsInboxPage, and the census.turbo run build --filter="@object-ui/collaboration^..."(3 of 3 tasks), thenpnpm --filter @object-ui/collaboration run type-checkandpnpm --filter @object-ui/i18n run type-check, both exit 0.turbo run build --filter="@object-ui/console^..."(34 of 34 tasks), then the console'stype-check(tsc --noEmit && tsc -b tsconfig.node.json --force) exits 0.--listFilesshows the programs contain the new files: 6 collaboration test files in itstsconfig.test.json, the census file in i18n's, and both the page and the new pin in the console program (itsincludeissrc).Ablation: fix committed first, every leg restored with on-disk proof
Each leg puts the UI language back at ONE face through
ablation-replace.mjs, then runs both pin files and the no-provider file (27 cases).const displayLocale = useDisplayLocale();becomesconst displayLocale = useCollaborationTranslation().language;const { t } = useObjectTranslation();becomesconst { t, language } = useObjectTranslation();(held), thentoLocaleDateString(displayLocale); } catch { return s; }becomestoLocaleDateString(language); } catch { return s; }The red readings:
got: 3/4/2020: expected '3/4/2020' to be '4.3.2020', and the argument checksaw: [["en"]].got: 3/4/2020: expected '3/4/2020' to be '4.3.2020', and the tripwire saw fourDate.prototype.toLocaleDateStringcalls, each with"locale":"en".The malformed-tag case stays green on the CommentThread leg. It pins the catch fallback, not the channel, so it is not offered as evidence for this change.
On-disk proof:
b3733ea1a9cftod73d4e95d0e6, restored tob3733ea1a9cf(HEAD's),git diff HEADempty.7869008b0099tod086337684fc, and the wrapped leg moved it on tof334af56992a. The wrapped leg's restore took the whole file back to HEAD, so both mutations were gone after it (on-disk counts 0 and 0), blob7869008b0099,git diff HEADempty.git status --porcelainwas empty at the end.No
dist/is on the path: the pins import the components by relative path, and vitest aliases@object-ui/i18n,@object-ui/collaborationand@object-ui/test-supporttosrc.Gates
dispatch-gates.mjsonly works in objectstack, so these were derived by hand from this repo'spackage.jsonand.github/workflows/. All at5a40f03e0, all exit 0:check-changeset-presence: 6 source files of 3 released packages changed, 1 changeset declared.check-changeset-no-major,check-changeset-overwrite(1 added, 0 modified),check-changeset-fixed.check-changeset-claims: no line address in this change's own prose; no self-contradiction. The went-false reading names two pending changesets that mentionmachineLocaleCensus-9909.test.ts:9786-detail-timestamps-display-locale.mdand9909-machine-locale-sweep.md. I read both paragraphs. They say collaboration's single hit is a deliberate catch fallback for a tagIntlrejected, and describe what the census refuses. Both still hold.check:new-line-citations:VERDICT new-cross-file-line-citations: 0 new citation(s).check:control-bytes: OK.check-vi-mock-specifiers,check-vi-mock-inherit,check-vi-mock-override-shape,check-test-path-roots,check-pending-changeset-literals. For the source:check-phantom-dependencies,check-unused-dependencies,check-package-self-import,check-unreferenced-sources,check-i18n-call-site-keys.pnpm lintis left to CI. ESLint with--no-inline-configran on the 6 changed TS files (each package'slintiseslint .over the root config).ApprovalsInboxPage.tsxhas 20 warnings andCommentThread.tsxhas 1, with the same per-rule counts as their base blobs.eslint --print-configresolves all 6 files, so none of them is ignored.eslint.config.jsconfigures no type-aware linting (noparserOptions.projectorprojectService). So this diff cannot change the lint result of a file it does not touch.The census extension to a bare
languageargument: not built hereThe card says objectui#10369's PR body costs this extension as cheap. That body says the opposite: "Not cheap: a bare
languageargument (the UI-language channel) cannot be told apart from a correctly named variable without data flow", and adds that such a rule would land red on three sites. The dispatch allowed the extension only if it is as cheap as that costing says, so it is not built. The conflict is reported to the seat.Measured here for whoever prices the follow-up: the "lands red" half no longer holds. The probe described above finds the two sites this PR changes at
74fcda829and none at head. Among the census population's identifier first arguments at head, the last segments arelocale,displayLocale,dateLocale,effectiveLocaleand one constant, and none islanguage. The data-flow half still holds: a name rule would judge the identifier, not where its value came from.Acceptance notes
formatRelative's catch (likeformatDate's) returns the raw value whenIntlrejects the tag. A malformed display locale would therefore show the raw ISO string in the inbox, whereCommentThreadfalls back to the runtime date. This PR does not make that more likely: the oldlanguageargument had the same exposure. No malformed tenant locale was reproduced, so nothing was filed.content/docschange: neither@object-ui/collaboration's README nor the docs describe how the timestamp is formatted. The changeset carries the user-facing sentence.Generated by Claude Code