Repository navigation
fix(app-shell,plugin-detail): titles and links that name a list of records read the plural label (objectui#11733) - #11746
Merged
objectstack-fleet[bot] merged 2 commits intoOct 6, 2026
Conversation
…cords read the plural label (objectui#11733) objectui#11696 gave useObjectLabel() an objectPluralLabel resolver and used it for the object list page title and its breadcrumb. The other places that name a list of an object's records still read the singular. Each now reads objectPluralLabel; record-scoped text keeps objectLabel. - RecordDetailView: related-list section titles. deriveRelatedLists takes a listLabel option for the multi-relationship title it composes, and the detail page passes the same resolver. - record:related_list and record:reference_rail (plugin-detail): the untitled fallback heading, the rail cards and the rail's empty line. - RecordFormPage: the shared label is split; the breadcrumb link to the list reads the plural, the title and the save toast keep the singular. - ObjectView: the favorite entry and the record page's way back when the open view has no label; AppContent: the way back handed to the page a create lands on. - useRecentItemLabel: a recent object entry reads the plural at the call site; useNavTargetLabel's nav-entry inheritance is unchanged. Claude-Session: https://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8 Co-authored-by: Claude <noreply@anthropic.com>
…nding pin Claude-Session: https://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8 Co-authored-by: Claude <noreply@anthropic.com>
Contributor
✅ 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
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #11733
Clause-②: no
What changed
PR objectui#11728 (objectui#11696) gave
useObjectLabel()anobjectPluralLabel(objectDef)resolver (the translated plural, else the declaredpluralLabel, else the singular) and used it for the object list page title, the/datatitle and the object breadcrumb. Every other place that names a LIST of an object's records now reads it too. Record-scoped text keepsobjectLabel.RecordDetailView, the single-relationship titleobjectPluralLabelover the child object ("Tasks")deriveRelatedLists, the title it composes when a child points at the parent through more than one fieldlistLabeloption;RecordDetailViewpassesobjectPluralLabel, so the title reads "Opportunities · Partner Project". It was built from the child's raw, untranslated label beforerecord:related_listwith notitle(record-related-list.tsx)objects.NAME.pluralLabel, else the translated label, else the humanized namerecord:reference_railentries with notitle: each card title and the "+ N empty (…)" line (record-reference-rail.tsx)RecordFormPagelabelis split: the link readslistLabel(plural); the "Create …" / "Edit …" title and the save toast keeplabel(singular)ObjectView's star buttonlabelis the pluraluseRecentItemLabel,objectkindobjectPluralLabelat the call site;useNavTargetLabelis not touchedObjectView, both builders (the list schema'sonNavigateand the record-link bridge'sopenRecord)viewOriginLabel(…) || objectPluralLabel(objectDef)AppContent, a create that lands on the new record's pageoriginLabelis the pluralWhere it landed beyond the claim's file surface, and why
The claim named
RecordDetailView,record-related-list.tsx,RecordFormPage,ObjectViewand the recent-item label. Three more files, each the producer of a surface the card's acceptance covers:packages/app-shell/src/utils/deriveRelatedLists.ts: it produces the multi-relationship related-list title (card location 1). Left alone, the record page would read "Tasks" next to "Opportunity · Partner Project". The new option is internal:deriveRelatedListsis not re-exported from app-shell's package entry (exportsis.only, andsrc/index.tsnames noderiveRelatedLists), so nothing published widens.packages/app-shell/src/console/AppContent.tsx: the second producer of the record page'slocation.state.from.label(card location 4), on the create path. The enumeration pin covers it.packages/plugin-detail/src/renderers/record-reference-rail.tsx: twoobjectLabel(hits under the enumeration pin'spackages/plugin-detail/src, and both name lists.Clause-②: nostill holds: no new export and no new locale key.plugin-detailreachesobjectPluralLabelthrough the hook it already calls (useSafeFieldLabel()returnsuseObjectLabel()).record-related-list.tsxnarrows with'objectPluralLabel' in i18ninstead of theas anycast its singular read used.The nav entry, unchanged by ruling
useNavTargetLabel'sobjectrung still callsobjectLabel, per step 3 of the spec's nav-label rule, as the seat ruled on objectui#11696. The recent-itemobjectkind no longer asks it, and callsobjectPluralLabelitself. The recent-item pin's last case asserts thatuseNavTargetLabelstill answers "Project" / "项目" for the same object.Enumeration pin
git grep -n 'objectLabel(' -- packages/app-shell/src packages/plugin-detail/src, tests excluded, at head50e8f98: 12 hits (20 at base17acfbb), 0 of them inplugin-detail.apps/console/srchas 0. Each remaining hit:AppContent.tsxhandleCrudSuccess'slabel(save toastsform.updateSuccess/form.createSuccess)AppContent.tsxhandleRecordFormSuccess(the same toasts)AppContent.tsxtitle(form.editTitle/form.createTitle), 2 hitsuseNavTargetLabel.tscase 'object'ObjectDataPage.tsxNavigationOverlaytitleObjectView.tsxuseObjectActions({ objectLabel })ObjectView.tsxImportWizardobjectLabelpropObjectView.tsxNavigationOverlaytitlesRecordDetailView.tsxrecordDelete.confirmCopy({ label })in the header's delete actionRecordFormPage.tsxconst labelTests
Seven pins, each named after this issue. In every
zh-CNfixturepluralLabelis a different word fromlabel(项目/项目清单), so an assertion can tell which key was read. Each has a no-plural control.ObjectView.listLinksPlural-11733.test.tsx: a realObjectViewin a real i18next instance (the 11696 harness). It reads the favorite the star button saves, and the way-back label each builder hands the record route (a list probe callsschema.onNavigateand the bridge'sopenRecord). The fixture's list view has no label, which is when the fallback applies.RecordFormPage.listLinkPlural-11733.test.tsx: the link reads the plural; the title and toast read the singular. Cells: en create, en edit, zh-CN, control.RecordDetailView.relatedListTitlePlural-11733.test.tsx: single- and multi-relationship titles, en and zh-CN, control.useRecentItemLabel.objectPlural-11733.test.tsx: en, zh-CN, control, the machine-name floor, and the unchanged nav-entry inheritance.AppContent.createLandingOriginPlural-11733.test.tsx: theAppContent.declaredVisibilityKeysharness with a real i18next instance. The modal'sonSuccessis called with a saved id for a page-surface object, and the record route's probe shows the state it received. The save runs outsideact: an awaited React 19actthere did not return before the test timeout.record-related-list.titlePlural-11733.test.tsxandrecord-reference-rail.titlePlural-11733.test.tsx: plurals from the bundle only (these renderers hold the object's name, not its definition), en and zh-CN, with controls.Reverse verification (on
a46d4a9, the implementation commit)Mutations went through objectstack's
scripts/ablation-replace.mjs. Each anchor's declared hit count was met and each file's blob changed. A script-held trap restored every path, proven by each blob equal toHEADand an emptygit diff HEAD. The direction was written down before each run.Tests 19 failed | 12 passed (31). The failures readexpected 'Project' to be 'Projects',expected '项目' to be '项目清单', and the form link'Project'/'项目'. Every control stayed green.deriveRelatedListsback to the raw label. Predicted 2 red and 1 green on the RecordDetailView pin. ObservedTests 2 failed | 1 passed (3): "Unable to find an element with the text: Opportunities · Primary Project" and the same for "商机列表 · Primary Project".50e8f98changes only the types of the AppContent pin's modal probe.Gates
pnpm exec vitest runover the 7 new pins50e8f98Test Files 7 passed (7)·Tests 31 passed (31)pnpm exec vitest run packages/plugin-detail/a46d4a9Test Files 243 passed | 1 skipped (244)·Tests 2401 passed | 8 skipped (2409)pnpm exec vitest runover 242 app-shell test files (declared narrowing, below)a46d4a9Test Files 242 passed (242)·Tests 2141 passed (2141)pnpm exec vitest runover 24apps/consoletest files that name AppContent, ObjectView, RecordDetailView, RecordFormPage, useRecentItemLabel, ConsoleLayout or the related-list blocksa46d4a9Test Files 24 passed (24)·Tests 603 passed (603)pnpm --filter @object-ui/app-shell type-check(dependency closure built first: turbo, 28 tasks)50e8f98tsc --noEmit && tsc -p tsconfig.test.json); the five app-shell pins are in its file listpnpm --filter @object-ui/plugin-detail type-checka46d4a9(no plugin-detail file changed since)pnpm exec eslinton the 15 touched.ts/.tsxfiles50e8f980 errors. No warning falls on a line this branch added except the existingreact-hooks/exhaustive-depswarning on thesynthPartsdeps line inRecordDetailView, which base17acfbbcarries too (it names other members)pnpm check:control-bytes50e8f98check-control-bytes: OKpnpm check:test-path-roots50e8f98check-test-path-roots: OKpnpm check:changeset-claims50e8f98No pending changeset names a file this change touches.pnpm check:pending-changeset-literals50e8f98No test source names a pending changeset.pnpm check:vi-mock-specifiers/vi-mock-inherit/vi-mock-override-shape50e8f98OKpnpm check:new-line-citations50e8f980 new citation(s)node scripts/check-changeset-presence.mjs50e8f9815 source file(s) of 2 released package(s) changed, and this change declares 1 changeset(s)node scripts/check-changeset-no-major.mjs50e8f98No changeset declares a major bump.Declared narrowing. The app-shell run covers the 242 of its 1043 tracked test files that name a touched module or one of its consumers. The selection is
git grep -lover app-shell's test files for RecordDetailView, RecordFormPage, ObjectView, AppContent, useRecentItemLabel, deriveRelatedLists, useNavTargetLabel, RecentApps, HomeRail, CommandPalette, UnifiedSidebar, StudioHomePage, useFavorites, FavoritesProvider and the related-list / reference-rail blocks. The other files are left to CI's fullpnpm test. Blind spot: a test that renders these modules through a component whose name matches none of those words. The narrowed runs are ona46d4a9;50e8f98changes one pin's types only, and that pin re-ran on50e8f98.Acceptance notes (observations, not filed; no carrier)
refreshLabelis called for record favorites only). Starring again saves the plural. Read from source, not measured.record:related_listandrecord:reference_railnever read the related object's declaredlabelorpluralLabel, because they hold its name only. Their fallback was bundle-then-humanized before and stays that way. With no bundle entry, an object with a declared plural is still headed by its humanized name there.label, untranslated, as before.ImportWizardkeeps the singular (see the enumeration table).Session:
https://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8Generated by Claude Code