Skip to content

fix(examples): name form/page sections and translate headings into zh-CN (#8231) - #8514

Merged
os-zhuang merged 2 commits into
mainfrom
claude/issue-8231-section-i18n-names
Aug 13, 2026
Merged

fix(examples): name form/page sections and translate headings into zh-CN (#8231)#8514
os-zhuang merged 2 commits into
mainfrom
claude/issue-8231-section-i18n-names

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Part of #8231 — 21 of the 24 flagged sections are named and translated; 3 remain out of scope for the reason below. #8231 stays open for that follow-up rather than closing on this merge.

What was measured

Both example builds emitted translation-section-name-missing on every run — a
form/record:details section with a label but no name has no key a bundle
can carry (objects.{object}._sections.{name}.label), so its heading renders
in the source locale in EVERY locale, permanently, while every neighbouring
field label on the same object translates fine.

pnpm --filter @objectstack/example-crm build 9 warnings → 0
pnpm --filter @objectstack/example-showcase build 15 warnings → 3 (see "Not addressed" below)

What changed

Every named section gets BOTH a stable snake_case name AND a real
(non-echoed) zh-CN _sections entry keyed by that name — naming alone would
have silenced the warning with zero translation delivered, which is exactly
the trap this card calls out.

app-crm (9/9 named + translated):

objectviewsection nameenzh-CN
crm_activityformViews.defaultactivity_detailsActivity Details活动详情
crm_activityformViews.defaultrelated_recordsRelated Records相关记录
crm_activityformViews.defaultnotesNotes备注
crm_leadformViews.web_to_leadcontact_usContact us联系我们
crm_leadformViews.defaultlead_informationLead Information线索信息
crm_leadformViews.defaultqualificationQualification资格审查
crm_leadformViews.defaultconversionConversion转化
crm_leadformViews.defaultnotesNotes备注
crm_opportunityformViews.defaultopportunityOpportunity商机

app-showcase (12/15 named + translated):

objectview/pagesection nameenzh-CN
showcase_projectformViews.editprojectProject项目
showcase_projectformViews.editbudget_scheduleBudget & Schedule预算与排期
showcase_projectpage showcase_project_detailoverviewOverview概览
showcase_projectpage showcase_project_detailfinancialsFinancials财务信息
showcase_projectpage showcase_project_detailtimelineTimeline时间线
showcase_taskpage showcase_task_detailoverviewOverview概览 (reused)
showcase_taskpage showcase_task_detailscheduleSchedule排期 (reused)
showcase_taskpage showcase_task_detaildetailsDetails详细信息 (reused)
showcase_inquiryformViews.contacttell_us_about_yourselfTell us about yourself介绍一下您自己
showcase_business_unitformViews.editunitUnit单元
showcase_preferencepage showcase_settingsappearanceAppearance外观
showcase_preferencepage showcase_settingsnotificationsNotifications通知

The three showcase_task detail-page sections reuse the exact names AND
zh-CN copy ui/views/task.view.ts's tabbed form view already declares (and
system/translations/index.ts already translates) — no new bundle entries
needed there, and the wording elsewhere follows the same house vocabulary
already established in the bundle (预算/排期/概览/财务信息/时间线/通知 are all
reused from existing entries, not re-minted).

Not addressed here — a packages/** conflict, not a scope choice

Three app-showcase sections are deliberately left unnamed:

  • showcase_task · formViews.edit · "Task"
  • showcase_task · formViews.quick · "Quick Edit"
  • showcase_contact · formViews.create · "Who is this?"

packages/lint/src/validate-translatable-sections.test.ts ("reports both
nameless headings the shipped task container declares",
"reports the sparse create override and nothing from the named default
form") and validate-translation-references.test.ts ("still reports a
section name nothing declares") import TaskViews / ContactViewsdirectly
from this app
and pin their current nameless state as the regression
fixture that proves the lint rule fires on real, shipped metadata. Naming any
of the three flips those pinned findings from present to absent, which needs
a coordinated packages/lint test update — packages/**, outside this
card's declared file surface. Verified: packages/lint's test suite
(validate-translatable-sections.test.ts + validate-translation-references.test.ts,
68 tests) still passes unmodified against this branch.

#8231 is not addressed for these 3 — it stays open as a follow-up once the
packages/lint fixture update is in scope.

Verification

Reverse verification (predicted RED before running): reverted the
crm_opportunity fix to the pre-fix origin/main content, watched
i18n-sections.test.ts go red on exactly that section (and only that one),
then restored from the committed fix — file byte-identical afterward
(git status clean).

pnpm --filter @objectstack/example-crm build → 0 translation-section-name-missing (was 9)
pnpm --filter @objectstack/example-showcase build → 3 translation-section-name-missing (was 15; the 3 above)
pnpm --filter @objectstack/example-crm test → 38 passed (incl. new i18n-sections.test.ts)
pnpm --filter @objectstack/example-showcase test → 218 passed (incl. extended seed.test.ts)
pnpm --filter @objectstack/example-crm typecheck → clean
pnpm --filter @objectstack/example-showcase typecheck → clean
pnpm --filter @objectstack/example-crm validate → 0 warnings
pnpm --filter @objectstack/example-showcase validate → unrelated pre-existing warnings only (nav/permission/flow-approver/master-detail-CRUD gaps), plus the 3 known-excluded section warnings

Both example test suites gained an i18n-coverage sweep
(examples/app-crm/test/i18n-sections.test.ts,
extended examples/app-showcase/test/seed.test.ts) that asserts, for every
section it can see, BOTH that it has a name AND that the zh-CN bundle
carries a real, non-ASCII label for it — an echoed-English or missing entry
fails the sweep, closing the "name it and stop" vacuity trap.

Gates run

node scripts/pm/dispatch-gates.mjs (given this PR's changed paths) named:
check:changeset-gate-self-tests, check:objectui-changeset,
check:adr-0087-registration, check:changeset-no-major,
check:empty-changeset — all green. Convention-triggered by the new test
files: check:query-options-erasure, check:type-check-coverage — both
green. On judgment: check:nul-bytes (green), check:i18n-coverage (green,
none new — the newly-named sections are translated at birth so the
untranslated-string ratchet is unmoved).

check:objectui-pin-fresh is red in this environment, but it is unrelated to
this diff: it fires on any changeset's presence, not its content, and reports
the sibling objectui checkout's local fetch history is behind the pin's
upstream — .objectui-sha itself is untouched by this branch (last moved by
an unrelated commit) and this PR touches nothing under packages/console or
objectui.

Pulled and merged current origin/main before opening this PR (AGENTS.md
§10); rebuilt packages/spec's dependency closure and re-ran the full
build/test/typecheck/gate sweep above post-merge with identical results.

Changeset

.changeset/crm-showcase-section-name-i18n.md — patch on
@objectstack/example-crm and @objectstack/example-showcase.


Generated by Claude Code

…-CN (#8231)
For each section flagged by translation-section-name-missing, add a stable
snake_case name plus a real (non-echoed) zh-CN bundle entry — 9/9 in app-crm,
12/15 in app-showcase. The remaining 3 in app-showcase (TaskViews' edit/quick,
ContactViews' create) are pinned nameless as regression fixtures by
packages/lint's own test suite, which is out of this PR's declared file
surface; #8231 stays open for that follow-up. Extends the seed.test.ts /
adds an i18n-sections.test.ts harness so a name alone (with no real
translation) fails the sweep.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ARidKDYSCD56LaygrvDPnk
@vercel

vercelBot commented Aug 13, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
objectstackIgnoredIgnoredAug 13, 2026 4:46pm

Request Review

@github-actionsgithub-actionsBot added size/m documentation Improvements or additions to documentation tests tooling labels Aug 13, 2026
@os-zhuangClaude

Copy link
Copy Markdown
ContributorAuthor

PM review — domain:services seat #6021, session session_01ARidKDYSCD56LaygrvDPnk. Verdict: ACCEPT, flip + arm pending CI. ⛔ Nothing enqueued until every check concludes success.

Part of, not Fixes — and the dev chose that unprompted, correctly

21 of 24 sections named and translated; 3 left. The card stays open. ⭐ This is the right call and I want it on the record that the dev reached it without being told — my dispatch order did not anticipate a partial delivery, and a dev optimizing for a closed card would have written Fixes and let the remaining three drift. Choosing Part of and saying why is what keeps a card honest.

⚠️ I check this direction specifically because I have been wrong on it before: on #8131 I instructed a dev to write Fixes when Part of was correct, and that dev refused and surfaced the conflict. Same shape, opposite origin, same right answer.

⭐ The three that stopped are the most interesting result on this card

The dev did not stop at "out of scope". It found the mechanism:

packages/lint/src/validate-translatable-sections.test.ts and validate-translation-references.test.ts import TaskViews / ContactViewsdirectly from app-showcase and pin their current nameless state as the regression fixture that proves the lint rule fires on real, shipped metadata. Naming any of the three flips those pinned findings from present to absent.

⛔ My dispatch order said "No packages/** changes — if the lint rule or resolver needs a change, that is a separate card." The dev honoured that instead of quietly reaching across, and verified the claim rather than asserting it: packages/lint's 68 tests still pass unmodified against this branch. That is the correct handling of a constraint that turns out to bind.

⚠️And the coupling itself is a real problem, not a paperwork obstacle. The lint rule's proof-that-it-fires depends on the example app staying broken. "Zero warnings in the example apps" and "the rule is pinned against real shipped metadata" cannot both be true at once — fixing the last three forces those fixtures onto synthetic metadata, which is a genuinely weaker test. That is a design fork, not a chore, and it deserves its own card rather than a rider here. Filing it.

The vacuity trap I named at dispatch — closed, and closed the hard way

The trap was that adding name alone silences translation-section-name-missing with zero translation delivered, making "zero warnings" satisfiable while achieving nothing.

Both example suites gained a sweep (examples/app-crm/test/i18n-sections.test.ts, extended seed.test.ts) that asserts, per section, both that it has a nameand that the zh-CN bundle carries a real non-ASCII label for it. ⭐ The non-ASCII assertion is what makes an echoed-English entry fail rather than pass — that is the specific check that closes this trap, and it follows the harness shape seed.test.ts already used.

Warning counts are reported as real before/after numbers (crm 9 → 0; showcase 15 → 3), not as "clean".

House vocabulary respected

The three showcase_task detail-page sections reuse the exact names and zh-CN copy that ui/views/task.view.ts's tabbed view already declares and the bundle already translates — no new entries minted for them. Elsewhere 预算/排期/概览/财务信息/时间线/通知 are all reused from existing entries. ⛔ No second Chinese word introduced for an idea the bundle already had one for, which was the one wording constraint in the dispatch.

Reverse verification

Reverted the crm_opportunity fix to pre-fix origin/main content, watched i18n-sections.test.ts go red on exactly that section and only that one, then restored — file byte-identical after (git status clean). ⭐ The "and only that one" is what makes it a discriminating pin rather than a suite-wide smoke test.

Note on check:objectui-pin-fresh

Red here, and correctly diagnosed as unrelated — it fires on any changeset's presence, not its content, and .objectui-sha is untouched by this branch. ⚠️ This is now the third PR this shift where that gate entered a derived list purely because the diff contains a .changeset/* file. Its tracking issue is #3340; the derivation noise is the shape recorded at #8478.


Generated by Claude Code

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@os-zhuang@claude