From 7179ae5a3ede90096e7292f4b0da43f3c04ab03b Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 14 Aug 2026 17:37:05 +0000 Subject: [PATCH 1/2] fix(examples): name the last 3 translation-section-name-missing sections in app-showcase (#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 #8514 because packages/lint pinned their nameless state against live examples/app-showcase metadata; #8515 (PR #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 Claude-Session: https://claude.ai/code/session_01NaS1PAHJcPfAA2acnV53Tn --- .../src/system/translations/index.ts | 34 ++++++---- .../app-showcase/src/ui/views/contact.view.ts | 4 ++ .../app-showcase/src/ui/views/task.view.ts | 8 ++- examples/app-showcase/test/seed.test.ts | 65 +++++++++---------- 4 files changed, 65 insertions(+), 46 deletions(-) diff --git a/examples/app-showcase/src/system/translations/index.ts b/examples/app-showcase/src/system/translations/index.ts index 4f7db872b1..d80360657b 100644 --- a/examples/app-showcase/src/system/translations/index.ts +++ b/examples/app-showcase/src/system/translations/index.ts @@ -335,15 +335,17 @@ export const ShowcaseTranslationBundle = { successMessage: '已为整个选中集重算工时。', }, }, - // Section headings of the four form-view projections in - // `ui/views/task.view.ts` (tabbed / wizard / split). Each section - // there declares a stable `name`, which is the only thing that makes - // the heading translatable — `ObjectForm` looks it up as - // `objects.showcase_task._sections..label` and otherwise renders - // the English `label` verbatim. Wording follows the vocabulary the - // rest of this bundle already uses (任务 / 负责人 / 进度), rather than - // introducing a second word per idea. + // Section headings of the six form-view projections in + // `ui/views/task.view.ts` (edit / tabbed / wizard / split / quick). + // Each section there declares a stable `name`, which is the only + // thing that makes the heading translatable — `ObjectForm` looks it + // up as `objects.showcase_task._sections..label` and otherwise + // renders the English `label` verbatim. Wording follows the + // vocabulary the rest of this bundle already uses (任务 / 负责人 / + // 进度), rather than introducing a second word per idea. _sections: { + // edit (#8231 remainder) + task: { label: '任务信息' }, // tabbed overview: { label: '概览' }, schedule: { label: '排期' }, @@ -355,6 +357,8 @@ export const ShowcaseTranslationBundle = { // split panes split_task: { label: '任务' }, split_schedule: { label: '排期' }, + // quick (drawer, #8231 remainder) + quick_edit: { label: '快速编辑' }, }, // The filter-preset tab bar of `ui/pages/task-triage.page.ts` — four // tabs carrying a `filter` and no `view`, which is the shape that had @@ -418,21 +422,27 @@ export const ShowcaseTranslationBundle = { default: { label: '联系人' }, }, // `_sections` — the four groups of the default edit form in - // `ui/views/contact.view.ts`. Each declares a stable `name`, which is - // the only reason the heading is translatable (`ObjectForm` looks it up - // as `objects.showcase_contact._sections..label` and otherwise + // `ui/views/contact.view.ts`, plus the sparse `formViews.create` + // override's single section (#8231 remainder). Each declares a + // stable `name`, which is the only reason the heading is + // translatable (`ObjectForm` looks it up as + // `objects.showcase_contact._sections..label` and otherwise // renders the English `label` verbatim). Wording reuses the vocabulary // this bundle already established — 状态 and 备注 are exactly the words // its `stage`-family and `notes` entries use — rather than minting a // second term per idea. `contact` reads 联系方式 (contact DETAILS, its // fields being 姓名/邮箱/电话) instead of a circular 联系人 heading // inside a contact record; `work` follows the `…信息` shape the - // showcase_semantic_zoo headings set. + // showcase_semantic_zoo headings set. `who_is_this` is the create + // form's own heading — a plain rendering of the English question, + // matching the no-punctuation style every other heading in this file + // uses. _sections: { contact: { label: '联系方式' }, work: { label: '工作信息' }, status: { label: '状态' }, notes: { label: '备注' }, + who_is_this: { label: '这是谁' }, }, }, showcase_invoice: { diff --git a/examples/app-showcase/src/ui/views/contact.view.ts b/examples/app-showcase/src/ui/views/contact.view.ts index 04ba8b5983..6bc1b5fb35 100644 --- a/examples/app-showcase/src/ui/views/contact.view.ts +++ b/examples/app-showcase/src/ui/views/contact.view.ts @@ -85,6 +85,10 @@ export const ContactViews = defineView({ title: 'New contact', sections: [ { + // #8231 remainder — a stable `name` is the only thing that makes + // this heading translatable; see `_sections.who_is_this` in + // `system/translations/index.ts`. + name: 'who_is_this', label: 'Who is this?', columns: 1, fields: ['name', 'email', 'phone', 'company'], diff --git a/examples/app-showcase/src/ui/views/task.view.ts b/examples/app-showcase/src/ui/views/task.view.ts index 69290c1cd4..dc411931ac 100644 --- a/examples/app-showcase/src/ui/views/task.view.ts +++ b/examples/app-showcase/src/ui/views/task.view.ts @@ -338,6 +338,10 @@ export const TaskViews = defineView({ data, sections: [ { + // #8231 remainder — a stable `name` is the only thing that makes + // this heading translatable; see `_sections.task` in + // `system/translations/index.ts`. + name: 'task', label: 'Task', columns: 2, fields: [ @@ -395,7 +399,9 @@ export const TaskViews = defineView({ quick: { type: 'drawer', data, - sections: [{ label: 'Quick Edit', columns: 1, fields: ['status', 'priority', 'progress'] }], + // #8231 remainder — `name` is what makes the heading translatable; see + // `_sections.quick_edit` in `system/translations/index.ts`. + sections: [{ name: 'quick_edit', label: 'Quick Edit', columns: 1, fields: ['status', 'priority', 'progress'] }], }, }, }); diff --git a/examples/app-showcase/test/seed.test.ts b/examples/app-showcase/test/seed.test.ts index 8a8b1df90d..b3f306becc 100644 --- a/examples/app-showcase/test/seed.test.ts +++ b/examples/app-showcase/test/seed.test.ts @@ -84,11 +84,12 @@ describe('showcase stack', () => { }); /** - * #5420 — the section headings the registration makes translatable, pinned - * from BOTH sides on the real stack. + * #5420 (extended by #8231's remainder) — the section headings the + * registration makes translatable, pinned from BOTH sides on the real + * stack. * - * Registering `ContactViews` is what puts its four named `form.sections` in - * front of the i18n gates, and the two gates read the same set in opposite + * Registering `ContactViews` is what puts its named sections in front of + * the i18n gates, and the two gates read the same set in opposite * directions: `i18n/missing-section` (#5405) fails on a declared section * with no bundle entry, `translation-target-unknown` (#5415/#5422) fails on * a bundle entry no section declares. A test that checked one direction @@ -96,19 +97,23 @@ describe('showcase stack', () => { * * Read on the composed stack, not on the imported container: what the gates * see is `stack.views`, and that is exactly the reachability this issue was - * about. A section with no `name` (the sparse `formViews.create` section) is - * untranslatable by construction — every renderer guards the lookup on - * `name` — so it is deliberately outside the set. + * about. The default `form`'s four sections AND the sparse + * `formViews.create` override's one section are both named as of #8231's + * remainder, so both contribute to the set below — `formViews.create` is no + * longer excluded. */ it('keys the contact form sections to exactly what the container declares', () => { const contact = (stack.views ?? []).find((v) => targetObject(v) === 'showcase_contact'); + const namesOf = (view: unknown): string[] => { + const sections = (view as { sections?: unknown } | undefined)?.sections; + return (Array.isArray(sections) ? sections : []) + .map((s) => (s as { name?: unknown } | undefined)?.name) + .filter((n): n is string => typeof n === 'string'); + }; const form = (contact as { form?: unknown } | undefined)?.form; - const sections = (form as { sections?: unknown } | undefined)?.sections; - const declared = (Array.isArray(sections) ? sections : []) - .map((s) => (s as { name?: unknown } | undefined)?.name) - .filter((n): n is string => typeof n === 'string') - .sort(); - expect(declared).toEqual(['contact', 'notes', 'status', 'work']); + const formViews = (contact as { formViews?: Record } | undefined)?.formViews ?? {}; + const declared = [...namesOf(form), ...Object.values(formViews).flatMap(namesOf)].sort(); + expect(declared).toEqual(['contact', 'notes', 'status', 'who_is_this', 'work']); const zh = ShowcaseTranslationBundle['zh-CN']?.objects?.showcase_contact as | { _sections?: Record } @@ -218,25 +223,21 @@ describe('showcase form + page section i18n coverage (#8231)', () => { } /** - * KNOWN EXCLUSIONS — deliberately left unnamed by #8231's own scope. + * Formerly KNOWN EXCLUSIONS, now named (#8231 remainder). * - * Naming any of these three would flip a currently-green, currently-PINNED - * finding in `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") — all three import `TaskViews` / - * `ContactViews` directly from this app and pin their CURRENT nameless - * state as the regression fixture. Fixing them requires a coordinated - * `packages/lint` test update, which is `packages/**` — out of #8231's - * declared file surface. Tracked as a follow-up rather than silently - * widening scope; see this test's introducing PR for the issue link. + * Three sections used to be excluded here — `showcase_task`'s + * `formViews.edit` ("Task") and `formViews.quick` ("Quick Edit"), and + * `showcase_contact`'s `formViews.create` ("Who is this?"). Naming them + * would have flipped a currently-PINNED finding in + * `packages/lint/src/validate-translatable-sections.test.ts` and + * `validate-translation-references.test.ts`, which imported `TaskViews` / + * `ContactViews` directly from this app and pinned their nameless state as + * the regression fixture. #8515 (PR #8610) moved those pins onto a frozen + * snapshot (`packages/lint/src/showcase-shape.fixtures.ts`) that no longer + * reads live `examples/**` for the nameless cases, which is what freed this + * app to name all three. No exclusion set is needed any more — every + * section the sweep below finds is asserted individually. */ - const KNOWN_UNNAMED = new Set([ - 'showcase_task::formViews.edit::Task', - 'showcase_task::formViews.quick::Quick Edit', - 'showcase_contact::formViews.create::Who is this?', - ]); function collectFormSections(): Found[] { const out: Found[] = []; @@ -291,9 +292,7 @@ describe('showcase form + page section i18n coverage (#8231)', () => { return out; } - const found = [...collectFormSections(), ...collectPageDetailSections()].filter( - (f) => !KNOWN_UNNAMED.has(`${f.object}::${f.where}::${f.label}`), - ); + const found = [...collectFormSections(), ...collectPageDetailSections()]; it('found more than a token number of sections (guards against a vacuous sweep)', () => { expect(found.length).toBeGreaterThanOrEqual(12); From 04bb17585b419eee0be81f3405b7ee773b16547e Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 14 Aug 2026 18:45:20 +0000 Subject: [PATCH 2/2] test(cli): assert who_is_this in the showcase-contact section coverage list (#8231) Patch round on the merge queue's full-suite run: naming showcase_contact's formViews.create section (PR #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/objectstack#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 Claude-Session: https://claude.ai/code/session_01NaS1PAHJcPfAA2acnV53Tn --- packages/cli/test/i18n-section-coverage.test.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/cli/test/i18n-section-coverage.test.ts b/packages/cli/test/i18n-section-coverage.test.ts index 53e7f9105d..5197166a59 100644 --- a/packages/cli/test/i18n-section-coverage.test.ts +++ b/packages/cli/test/i18n-section-coverage.test.ts @@ -535,11 +535,17 @@ describe('the showcase app, walked for real', () => { 'objects.showcase_semantic_zoo._sections.basics.label', 'objects.showcase_semantic_zoo._sections.money.label', // `ContactViews.form.sections[].name` — the container's DEFAULT form, - // which `ObjectForm` renders and translates like any other. + // which `ObjectForm` renders and translates like any other. NOT the + // only contributor: `formViews.create`'s sparse override names its own + // section too (objectstack#8231), which is the entry right below. 'objects.showcase_contact._sections.contact.label', 'objects.showcase_contact._sections.notes.label', 'objects.showcase_contact._sections.status.label', 'objects.showcase_contact._sections.work.label', + // `ContactViews.formViews.create.sections[0].name` — the sparse create + // override's own section ("Who is this?"), named by objectstack#8231's + // remainder. Distinct surface, same object, same walker. + 'objects.showcase_contact._sections.who_is_this.label', ].sort()); }, 60_000); });