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

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):

object view section name en zh-CN
crm_activity formViews.default activity_details Activity Details 活动详情
crm_activity formViews.default related_records Related Records 相关记录
crm_activity formViews.default notes Notes 备注
crm_lead formViews.web_to_lead contact_us Contact us 联系我们
crm_lead formViews.default lead_information Lead Information 线索信息
crm_lead formViews.default qualification Qualification 资格审查
crm_lead formViews.default conversion Conversion 转化
crm_lead formViews.default notes Notes 备注
crm_opportunity formViews.default opportunity Opportunity 商机

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

object view/page section name en zh-CN
showcase_project formViews.edit project Project 项目
showcase_project formViews.edit budget_schedule Budget & Schedule 预算与排期
showcase_project page showcase_project_detail overview Overview 概览
showcase_project page showcase_project_detail financials Financials 财务信息
showcase_project page showcase_project_detail timeline Timeline 时间线
showcase_task page showcase_task_detail overview Overview 概览 (reused)
showcase_task page showcase_task_detail schedule Schedule 排期 (reused)
showcase_task page showcase_task_detail details Details 详细信息 (reused)
showcase_inquiry formViews.contact tell_us_about_yourself Tell us about yourself 介绍一下您自己
showcase_business_unit formViews.edit unit Unit 单元
showcase_preference page showcase_settings appearance Appearance 外观
showcase_preference page showcase_settings notifications Notifications 通知

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 / ContactViews directly
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

claude added 2 commits August 13, 2026 16:29
…-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

vercel Bot commented Aug 13, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 13, 2026 4:46pm

Request Review

@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tests tooling labels Aug 13, 2026

Copy link
Copy Markdown
Contributor Author

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 / ContactViews directly 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 name and 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

@os-zhuang
os-zhuang marked this pull request as ready for review August 13, 2026 17:06
@os-zhuang
os-zhuang added this pull request to the merge queue Aug 13, 2026
Merged via the queue into main with commit 450f3e5 Aug 13, 2026
25 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-8231-section-i18n-names branch August 13, 2026 17:20
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Aug 17, 2026
…ons in app-showcase (objectstack-ai#8231) (objectstack-ai#8742)

* fix(examples): name the last 3 translation-section-name-missing sections in app-showcase (objectstack-ai#8231)

Names showcase_task's formViews.edit ("Task") and formViews.quick ("Quick
Edit"), and showcase_contact's formViews.create ("Who is this?"), and adds
real zh-CN _sections translations for all three. These were the 3 of 24
sections withheld from PR objectstack-ai#8514 because packages/lint pinned their nameless
state against live examples/app-showcase metadata; objectstack-ai#8515 (PR objectstack-ai#8610) moved
those pins onto a frozen snapshot, freeing this remainder. Extends the
existing seed.test.ts sweep (removes the now-obsolete KNOWN_UNNAMED
exclusion set) so all three are asserted individually: named AND resolving
to a real, non-echoed zh-CN label.

app-showcase build: translation-section-name-missing 3 -> 0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NaS1PAHJcPfAA2acnV53Tn

* test(cli): assert who_is_this in the showcase-contact section coverage list (objectstack-ai#8231)

Patch round on the merge queue's full-suite run: naming showcase_contact's
formViews.create section (PR objectstack-ai#8742, examples/app-showcase) added a real,
new translatable section that packages/cli/test/i18n-section-coverage.test.ts
had no expectation for. Its 'the showcase app, walked for real' test walks
ContactViews live and asserts an exact toEqual set -- correctly, since it
caught a genuine growth in the walked surface. Add the new key rather than
weaken the assertion; the toEqual stays exact.

Searched packages/** for other live importers of examples/app-showcase or
examples/app-crm that assert fixed section-name expectations (regex:
imports matching examples/app- across packages/). Found 4 total, 3 already
safe (packages/lint's two files read a frozen snapshot for the sections this
card touches; packages/cli/test/i18n-tab-coverage.test.ts filters to _tabs
only and never reads _sections). This is the one that needed the update.

Gap recorded separately: objectstack-ai#8754 (no inventory of
packages/** tests that live-import examples/**, so an example-app edit can
break one with no affected-subset CI run surfacing it).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NaS1PAHJcPfAA2acnV53Tn

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants