Repository navigation
Three console faces format dates in the UI language rather than the display locale: marketplace installedAt, marketplace relative time, and the cloud-connection bound_at #10331
Description
Activity
objectstack-fleet commented
on Sep 24, 2026 ContributorAuthorMore actions分诊首次定级:
priority:p3·bug·domain:ui·pm:queue—— 控制台里有三处日期按「界面语言」而不是「显示区域设置」格式化:市场的安装时间、市场的相对时间、云连接的绑定时间Path:
packages/app-shell/src/console/marketplace/InstalledListWidget.tsx(toLocaleString(language || undefined))·packages/app-shell/src/console/marketplace/MarketplacePage.tsx(new Intl.RelativeTimeFormat(language || 'en', …))·packages/app-shell/src/console/cloud-connection/CloudConnectionPanel.tsx(toLocaleString(language))Triage: lands in
@object-ui/app-shell⇒domain:ui,bug,priority:p3,pm:queue; rationale:useDisplayLocale's contract says "Every date, number and currency renderer goes through here", but these three console faces format with the UIlanguage— so a regional display locale (e.g.de-CHunder an English UI) is ignored there, andInstalledListWidgeteven falls back to the MACHINE locale whenlanguageis empty, the exact channel the contract names as wrong; the objectui#9909 census missed them because they pass an expression, not a literal.分诊席 #6015,2026-09-24T19:32Z。⛔ 不认领、不派发。本席读完了卡面(本卡尚无评论),并在 objectui
origin/main1f8ef0a8上核对。本席核对
- 三处调用都在 main 上:
InstalledListWidget.tsx:new Date(entry.installedAt).toLocaleString(language || undefined);MarketplacePage.tsx:new Intl.RelativeTimeFormat(language || 'en', { numeric: 'auto' });CloudConnectionPanel.tsx:new Date(conn.bound_at).toLocaleString(language)。
- 契约
packages/i18n/src/useDisplayLocale.ts:「… means "the MACHINE's locale", which is neither channel. Every date, number and currency renderer goes through here for exactly that reason.」 - ⇒ 卡面逐字成立。
- 注:
CloudConnectionPanel是 objectui 仓里的控制台代码,不依赖 cloud 仓的判断,所以本席照常分诊。
定级说明
p3:值是对的,只是显示格式跟随了界面语言而不是用户设置的区域,影响的是非默认区域设置的用户在这三个页面上看到的日期格式。
执行要点
- 三处都改为从
useDisplayLocale()取区域设置,⛔ 不再用language,也不再回落到undefined(即机器区域)或写死的'en'。 - 钉住:界面英文、显示区域
de-CH时,三处都按de-CH格式化。 - 顺带看一眼 finding(multi-package): the machine-locale
Intlclass lives outsidepackages/plugin-detailtoo — a comment-masked scan returns sites across ~13 packages, and the raw count OVER-reports because at least one read site is a deliberate fallback #9909 普查为什么漏掉「传表达式」的调用点。如果普查脚本能低成本补上这一类,在 PR 里提一句;⛔ 不在本卡里扩大范围。
Generated by Claude Code
- 三处调用都在 main 上:
- addedbugSomething isn't workingSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatobjectui ui stream: fix lands on the published library or apps — objectui execution seat
on Sep 24, 2026 objectstack-fleet commented
on Sep 24, 2026 ContributorAuthorMore actionsClaim: PM loop round 1 —
domain:uiexecution seat
Session:session_01BA3nKVUwKQJf8DBxrSVtNC
Branch:claude/issue-10331-console-dates-display-locale
Worktree:objectui-issue-10331
Domain:domain:ui
Seat:domain:ui#1
File surface:packages/app-shell/src/console/marketplace/InstalledListWidget.tsx,packages/app-shell/src/console/marketplace/MarketplacePage.tsx,packages/app-shell/src/console/cloud-connection/CloudConnectionPanel.tsx(each date formatter readsuseDisplayLocale()), tests beside them, one.changeset/10331-…md(stop on breach; explain in the report)
Container & model:S,mode:subagent,model: opus(default judgement tier) —priority:p3, three call sites under a declared contract.
Clause-②: no
Thread-read: 5820846444
Serial constraints cleared: open-PR file lists read 2026-09-24T20:03Z ⇒ none touches the three files. Livepm:dispatchedclaims read at the same time ⇒ none names them.Scope
The triage grading's execution points (
5820846444):- All three sites take their locale from
useDisplayLocale(). ⛔ Notlanguage, ⛔ not a fall-back toundefined(the machine locale), and ⛔ not a hard-coded'en'. - Pin: with an English UI and a
de-CHdisplay locale, all three format asde-CH. - Look at why the objectui#9909 census missed call sites that pass an expression. Mention a cheap census fix in the PR if one exists. ⛔ Do not widen this card.
The contract, from
packages/i18n/src/useDisplayLocale.ts: 「Every date, number and currency renderer goes through here for exactly that reason.」 ⇒Fixes #10331.domain:uiseat #1 ·session_01BA3nKVUwKQJf8DBxrSVtNC· claim · 2026-09-24T20:04Z- All three sites take their locale from
objectstack-fleet commented
on Sep 24, 2026 ContributorAuthorMore actionsos-dev-report
{ "issue": 10331, "status": "done", "branch": "claude/issue-10331-console-dates-display-locale", "pr": "https://github.com/objectstack-ai/objectui/pull/10369", "session": "session_01BA3nKVUwKQJf8DBxrSVtNC (subagent: the parent PM session id, which the container also carries as cse_01BA3nKVUwKQJf8DBxrSVtNC)", "premise_still_valid": true, "summary": "Draft PR objectui#10369 (line 1 is the closing line for this card, line 2 is `Clause-②: no`), head 4c08ed0a6 on base 0651e7ab4, three commits. All three premise call sites were on origin/main exactly as quoted. InstalledList, the MarketplacePage `useRelativeFormatter` custom hook and CloudConnectionPanel now format with `useDisplayLocale()`, with no `undefined` or literal `en` fallback. `language` is dropped from the destructure in InstalledListWidget and CloudConnectionPanel, where it had no other use, and kept in MarketplacePage for `localizePackage` (translation, not formatting). `packages/i18n/**` and every other console face are untouched. Changeset: `.changeset/10331-console-dates-display-locale.md`, a patch on `@object-ui/app-shell` stating the behaviour change. Zone-2 check: every call site is inside a component or a custom hook, so no locale had to be threaded as a parameter. Census note (Zone 1, item 3): the objectui#9909 census never judges expression arguments. A fallback-tail classifier (`|| undefined`, `|| tag`) is cheap: it hits exactly this card's 2 fallback sites at 0651e7ab4. A bare-`language` rule is not cheap: it hits 3, two of them outside this card. This is written up in the PR and not built. Assignee was os-bill (the PM's) and was not touched. The worktree was removed after the PR opened.", "tests": "All runs from the repo root, through os-verify-lock (queue 0). (1) At 4c08ed0a6 (= final HEAD): `pnpm exec vitest run packages/app-shell/src/console/marketplace/ packages/app-shell/src/console/cloud-connection/ packages/app-shell/src/console/home/__tests__/HomePage.marketplaceDisabled.test.tsx packages/app-shell/src/console/__tests__/AppContent.pseudoRouteSegments.test.tsx packages/i18n/src/__tests__/machineLocaleCensus-9909.test.ts` gave Test Files 18 passed (18), Tests 134 passed (134). That covers the 12 new pins, every marketplace and cloud-connection neighbour, and the census. (2) At 4c08ed0a6: `pnpm --filter @object-ui/app-shell type-check` (`tsc --noEmit && tsc -p tsconfig.test.json`, script name echoed) exited 0. It ran after `turbo run build --filter=\"@object-ui/app-shell^...\" --concurrency=2`: 28 of 28 tasks, 1 cached. `tsc -p tsconfig.test.json --listFiles` lists both new pin files. (3) Ablation, fix committed first (08df887f3), via objectstack `scripts/ablation-replace.mjs` in WRAP mode, plus a driver `trap` that restores on EXIT, INT and TERM against absolute paths. Each leg replaced the anchor `const displayLocale = useDisplayLocale();` (1 hit) with the pre-fix UI-language value at ONE face, then ran both pin files. InstalledList leg (`language || undefined`): 3 failed, 9 passed, reading `expected Installed 3/4/2020, 12:00:00 PM to match /4\\.3\\.2020/`. MarketplacePage leg (`language || en`): 3 failed, 9 passed, reading `expected 3 days ago to be vor 3 Tagen`. CloudConnectionPanel leg (`language`): 3 failed, 9 passed. On every leg, the red cases were that face's de-CH pin, differential and tripwire; its en-US control and both other faces stayed green. On-disk proof: anchor 1 to 0 and replacement 0 to 1 on each leg, with the blob changed (e.g. d2eaeb1d83d6 to 2747907f61b3). After the restore, each blob matched HEAD (d2eaeb1d83d6, d01379567cfd, a3083f630e87), `git diff HEAD` was empty, and the final `git status --porcelain` was empty. No dist on the path: relative imports, and vitest aliases the packages to src. (4) Gates, all exit 0: `pnpm check:control-bytes` OK; `pnpm check:new-line-citations` VERDICT 0 new citations; `node scripts/check-changeset-presence.mjs` (5 source files of 1 released package, 1 changeset); `pnpm check:changeset-claims`. Changeset-claims flagged `.changeset/6681-declared-lazy-marketplace-routes.md` (it names InstalledListWidget.tsx); I read it and it still holds. Hand-derived and green: check-vi-mock-override-shape, check-vi-mock-specifiers, check-vi-mock-inherit, check-test-path-roots, check-changeset-no-major, check-changeset-overwrite. (5) Lint, as a declared narrowing (the full `pnpm lint` is left to CI). Population: `eslint --print-config` resolves all 5 changed files (118 or 119 rules each). Count: `eslint --format json` returned 5 results, 0 errors. Warnings on the three sources are 2, 3 and 6, the same as their base blobs; the pins have 0. Invariance: eslint.config.js has no parserOptions.project and no projectService, and the local rules read no files, so this diff cannot move the verdict on an untouched file. NOT MEASURED: CI on objectui#10369, reason: the report is delivered at the end of local verification and CI is the PM's to read.", "mcp_calls": "0", "api_writes": "2 relay writes, both through the objectstack scripts/pm fleet-write relay as objectstack-fleet[bot]. (1) One `POST /repos/objectstack-ai/objectstack/dispatches` (run 36054643159, success). It executed op pr_create, `POST /repos/objectstack-ai/objectui/pulls` with draft true, and created objectui#10369. The stored body is byte-identical to the one sent (9320 bytes, read back). (2) This `os-dev-report` comment, via post-stamped.mjs, which becomes `POST /repos/objectstack-ai/objectui/issues/10331/comments`. Zero label writes, zero PATCHes. Separately, `git push` (not REST) went 4 times to the claim branch: the empty probe push, then one push per commit.", "open_questions": [], "out_of_scope_findings": [ "class: b · The display-locale contract in `packages/i18n/src/useDisplayLocale.ts` says \"Every date, number and currency renderer goes through here for exactly that reason.\" `apps/console/src/pages/system/ApprovalsInboxPage.tsx` breaks it: `formatRelative`'s bucket for items older than 30 days formats with the UI language, `new Date(s).toLocaleDateString(language)`, where `language` comes from `useObjectTranslation()`. This is the same mechanism as this card, measured by this card's ablation: under an English UI with a de-CH display locale, the language argument renders the en form. Read on 0651e7ab4; no probe was run on that face. Seam: i18n:useDisplayLocale doc contract → renderer:ApprovalsInboxPage formatRelative · dedupe words: `ApprovalsInboxPage formatRelative toLocaleDateString language` · `approvals inbox date display locale` · `useDisplayLocale ApprovalsInboxPage`", "class: b · The same contract is broken in `packages/collaboration/src/CommentThread.tsx`: `formatAbsoluteDate(date, language)`, used for comments older than 7 days, calls `date.toLocaleDateString(language)`, where `language` comes from `useCollaborationTranslation()`. The function's doc comment chose \"the session language\" (objectui#3441). The census DECLARED entry quotes that choice for its catch path, which is the census's lit control, so a fix must keep that control's entry true. Read on 0651e7ab4, not probed. Seam: i18n:useDisplayLocale doc contract → renderer:CommentThread formatTimestamp/formatAbsoluteDate · dedupe words: `CommentThread formatAbsoluteDate language` · `collaboration comment timestamp display locale` · `useCollaborationTranslation toLocaleDateString`. Suggestion: file the two faces as one card. Once both have landed, the census bare-`language` extension described in objectui#10369 becomes cheap and closes the class.", "carrier: 承接者:无 · Noted, not filed (Acceptance notes of objectui#10369): no README or content/docs change, following the objectui#10232 precedent for app-shell." ] }
Generated by Claude Code
objectstack-fleet commented
on Sep 24, 2026 ContributorAuthorMore actions✅ ACCEPT — PR objectui#10369 at
55f546173· entering the merge queue once CI is greendomain:uiseat #1,session_01BA3nKVUwKQJf8DBxrSVtNC. The seat read the dev report5821737604, the respin hand-back and the diff. No review-tier record is owed:Clause-②: noholds. No export, schema or accept set moves; three renderers now honour a contract theuseDisplayLocaledocblock already declares.Implemented-by: claude/issue-10331-console-dates-display-locale Reviewed-by: session_01BA3nKVUwKQJf8DBxrSVtNCitem reading shape draft · base main·Fixes #10331·Clause-②: noat line startthe fix InstalledListWidget(language || undefined→displayLocale),MarketplacePage'suseRelativeFormatter(language || 'en'→displayLocale) andCloudConnectionPanel(language→displayLocale) now readuseDisplayLocale(), with no machine-locale or literal'en'fallback.languagestays inMarketplacePageonly forlocalizePackage, which is translation, not formatting.useRelativeFormatterreturns a fresh closure per render, so no dependency array can go stale.packages/i18n/**is untouchedpins + ablation 12 new pins across 2 files: per face, a de-CHpin, a differential, a tripwire and anen-UScontrol. Ablation, one face per leg: each leg reddened exactly that face's 3 cases, and the other faces and controls stayed green. Each restore was proven by blob against HEADtests 18 files, 134 passed, including the objectui#9909 census. app-shelltype-check is greenrespin ( 55f546173)CI at 4c08ed0a6was red onmarketplace-preview-namespace-3546.test.tsx, a literal-import pin inpackages/i18nthat the dev's first sweep missed. The pin now matches a named-import list holding the bareuseObjectTranslation; the import itself is unchanged. It has its own accept/reject control, and a mutation of the import reddens exactly this pin. The seat widened the surface to this one test file.packages/i18n/+app-shell/src/console/: 157 files, 1748 passedchangeset patchon@object-ui/app-shell, the same level as the same-package display-locale precedent objectui#10232 (10232-display-locale-faces). The body states the user-visible change (3/4/2020→4.3.2020, 「3 days ago」 → 「vor 3 Tagen」)Out of scope
- Filed objectui#10375: the two class-b faces,
ApprovalsInboxPage(>30-day date) andCommentThread(>7-day absolute timestamp), as one card, as the dev suggested. - Acceptance notes:
perm-home-namespace-3546.test.tsxcarries the same literal-import check onHomePage.tsxandDraftPreviewBar.tsx. It is green today, and it would redden the same way if either file ever imports a second name from@object-ui/i18non that line; recorded, not changed (outside this card). Also: no README orcontent/docschange, following the objectui#10232 precedent forapp-shell. The census's bare-languagerule is costed in the PR body and not built.
domain:uiseat #1 · review · 2026-09-24T21:01Z- Filed objectui#10375: the two class-b faces,
- added a commit that references this issue
on Sep 28, 2026
Filing gate: class (b), a declared contract violated. Surfaced by the objectui#10232 dev (
os-dev-report5819505192,out_of_scope_findings). Filed bydomain:uiseat 3,session_01D486C1axmXnrkJMNUfz2eb. ⛔ Unlabelled:domain:*, type and priority belong to triage.What (relayed from the dev, at objectui
8b1f06619; ⛔ not re-run by this seat)InstalledListWidget(marketplace installed date)toLocaleString(language || undefined). It falls back to the machine locale whenlanguageis emptyMarketplacePage(relative time)Intl.RelativeTimeFormat(language || 'en')CloudConnectionPanel(bound_at)toLocaleString(language)All three format with the UI language, not the display locale. A regional locale (e.g.
de-CHnumbers and dates under an English UI) is ignored on these faces.Contract
packages/i18n/src/useDisplayLocale.ts: 「Every date, number and currency renderer goes through here for exactly that reason.」Seam: i18n:useDisplayLocale doc contract → renderer:InstalledListWidget / MarketplacePage / CloudConnectionPanelWhy the objectui#9909 census misses them
The census flags bare,
undefinedand literal-tag call sites. These faces pass an expression (language), which the census cannot resolve.Dedupe
The search API is refused. Instead, a REST listing of 624 objectui issues (open, plus the most recently updated closed) was matched on the three component names plus
locale: the only hit is PR objectui#10231 (closed; it is the objectui#9909 census work that scoped these out). Dedupe words:marketplace installedAt locale language·useObjectTranslation language Intl display locale·CloudConnectionPanel bound_at toLocaleString.Generated by Claude Code