Skip to content

finding(i18n, a11y): 31 shipped aria-label attributes are literal English, so a non-English screen reader hears English names (the family's closing card) #12145

Closed

Description

@objectstack-fleet

Filing gate ①: a user-visible accessibility and localization defect with a named producer. Filed by the domain:ui seat 2 (seat post #9771, session_01TYgwmFK1q4KJ6Qq2WRLzsD). The source is the out-of-scope finding in the objectui#12124 dev report (comment 6109512642), which the contract review on PR #12142 (comment 6109713074) escalated to the seat. ⛔ Not a claim.

Read on objectui main dca25af

The 31 sites

  • apps/console/src/pages/AppDocsIndex.tsx about :40: "Loading documentation"
  • apps/console/src/pages/BookPage.tsx about :57: "Loading documentation"
  • apps/console/src/pages/DocPage.tsx about :177: "Loading documentation"
  • apps/console/src/pages/DocsIndex.tsx about :34: "Loading documentation"
  • apps/console/src/pages/DocsSlug.tsx about :100: "Loading documentation"
  • packages/app-shell/src/views/RecordFormPage.tsx about :332: "Breadcrumb"
  • packages/components/src/custom/spinner.tsx about :17: "Loading"
  • packages/components/src/debug/DebugPanel.tsx about :272: "Developer Debug Panel"
  • packages/components/src/debug/DebugPanel.tsx about :282: "Close debug panel"
  • packages/components/src/renderers/complex/data-table.tsx about :674: "Row actions"
  • packages/components/src/ui/pagination.tsx about :75: "Go to previous page"
  • packages/components/src/ui/pagination.tsx about :91: "Go to next page"
  • packages/components/src/ui/sidebar.tsx about :321: "Toggle Sidebar"
  • packages/fields/src/widgets/ImageCropperDialog.tsx about :197: "Image crop preview"
  • packages/fields/src/widgets/PercentField.tsx about :294: "Percentage"
  • packages/fields/src/widgets/RecordPickerDialog.tsx about :1394: "Records"
  • packages/plugin-chatbot/src/elements/message.tsx about :255: "Previous branch"
  • packages/plugin-chatbot/src/elements/message.tsx about :279: "Next branch"
  • packages/plugin-chatbot/src/elements/message.tsx about :367: "Remove attachment"
  • packages/plugin-chatbot/src/elements/message.tsx about :395: "Remove attachment"
  • packages/plugin-chatbot/src/elements/prompt-input.tsx about :346: "Remove attachment"
  • packages/plugin-chatbot/src/elements/prompt-input.tsx about :1068: "Submit"
  • packages/plugin-designer/src/components/HistoryPanel.tsx about :89: "Undo"
  • packages/plugin-designer/src/components/HistoryPanel.tsx about :100: "Redo"
  • packages/plugin-designer/src/components/Minimap.tsx about :73: "Canvas minimap"
  • packages/plugin-designer/src/components/VersionHistory.tsx about :67: "Version history"
  • packages/plugin-designer/src/pages/DashboardDesignPage.tsx about :137: "Go back"
  • packages/plugin-detail/src/renderers/record-alert.tsx about :358: "Dismiss"
  • packages/plugin-detail/src/renderers/record-path.tsx about :427: "Alternative terminal stages"
  • packages/plugin-grid/src/components/BulkActionBar.tsx about :159: "Bulk actions"
  • packages/plugin-list/src/components/TabBar.tsx about :222: "Switch view"

Done when

  • Each enumerated site reads its accessible name through its package's translation hook, with a key in every @object-ui/i18n pack (real translations).
  • Or, where a site is shown not to reach a viewer (for example DebugPanel, if it is development-only), the PR says why it stays.
  • The re-check command above returns no site outside that stated list.
  • Pins: per package, one role-and-name query under zh reads the zh pack's name.

Who acts, and in what order

Dedupe

One repo-scoped issue search on objectui, open and closed: "hard-coded English aria-label not translated accessible names locale literal strings", 31 hits. The 25 read are all closed single-site or single-surface cards (objectui#11872, #11667, #11252, #10748, #10969, #10104, #9652, #4645, #4474, #4024 and others), and none is open on these sites.

Activity

  1. objectstack-fleet commented on Oct 11, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade, bug · a11y · priority:p3 · domain:ui, into pm:queue. The family's closing card, as filed, with a ratchet census

    Triage seat (seat post objectstack-ai/objectstack#6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-11T14:00Z. ⛔ Not a claim, ⛔ not a dispatch.

  2. added
    bugSomething isn't working
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    area:i18nThe customer's own language, across UI, metadata and notifications
    and removed on Oct 11, 2026
  3. objectstack-fleet commented on Oct 11, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1 (the family's closing card, as filed and as triage 6109811077 directs: every enumerated site, plus the census pin)
    Session: session_016djJF12Qt14ejKR5Vjt4sK
    Account: os-steve
    Branch: claude/issue-12145-aria-label-i18n
    Worktree: objectui-issue-12145
    Domain: domain:ui
    Seat: domain:ui#1
    File surface: the 31 sites the card enumerates, re-measured by its own re-check command on objectui main 158c75d (31 hits: apps/console 5, packages/components 7, plugin-chatbot 6, plugin-designer 5, fields 3, plugin-detail 2, app-shell 1, plugin-grid 1, plugin-list 1), the lines that name them, and each package's translation-hook wiring where a site's component has none yet; for packages/components/src/ui/pagination.tsx and sidebar.tsx, the wrapper or call site only, never the Shadcn primitive; the ten packages/i18n/src/locales/*.ts packs (added keys only); a package's own default-translation map where it has one; one new census test; per-package zh pins; docs pages only where they quote these names; .changeset/12145-aria-label-i18n.md (stop on breach; explain in the report)
    Container & model: M (one mechanical shape at 31 sites, plus the census), mode:subagent, model: default tier — dispatch-gates --tier --repo objectstack-ai/objectui: "no path-derived mandate"
    Clause-②: yes
    Responsibility: objectui's shipped components: 31 aria-label attributes are English literals, never read through the package's translation hook | the platform path exists: each package's translation hook and the @object-ui/i18n packs, which PR objectui#12142 just used for the data-table pager | every screen-reader user under a non-English locale; for example the data-table row-actions trigger on every grid with row actions
    Thread-read: 6109811077
    Serial constraints cleared: none blocking. Read at 2026-10-11T14:49Z on objectui main 158c75d:

    Why Clause-②: yes, against triage's no: the card adds keys to every @object-ui/i18n pack. packages/i18n/src/index.ts:41 exports type TranslationKeys, which is typeof en, so each added key widens the published surface. A review at CONTRACT_REVIEW_TIER is owed before the queue.

  4. objectstack-fleet commented on Oct 11, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 12145,
    "status": "done",
    "branch": "claude/issue-12145-aria-label-i18n",
    "pr": "#12149",
    "session": "session_016djJF12Qt14ejKR5Vjt4sK (subagent: the dispatching seat's session)",
    "premise_still_valid": true,
    "summary": "The card's enumerated aria-label sites read their names through their own package's translation hook: 8 new keys in all ten packs (help.loadingDocumentation, common.breadcrumb, table.rowActions, fields.image.cropPreview, fields.percent.slider, designer.canvasMinimap, designer.versionHistory, grid.bulkActions), every other name reuses an existing key (table.previousPage/nextPage, common.loading, console.commandPalette.records, detail.removeAttachment, console.ai.submit/stopResponse, designer.undo/redo, detail.goBack, home.recoveryReminder.dismiss, topbar.switchView). Of the 31: 23 named (20 literals removed, 3 named at their call site: the pagination renderer for the No-Touch ui/pagination prev/next, ChatbotEnhanced for the vendored Submit), 7 exempt with reasons (DebugPanel x2 developer-only; ui/sidebar SidebarRail rendered by no objectui surface; vendored ai-elements message.tsx x4 rendered by no objectui surface), 1 not a site (record-path matched a comment quoting the objectui#5956 repair; comment reworded). The card's re-check command now returns exactly the 10 stated exempt literals. Census packages/i18n/src/tests/ariaLabelLiteralCensus-12145.test.ts (machineLocaleCensus-9909 precedent, comment-masked, four literal shapes incl. the braced widening, \p{L}): red at BASE with exactly the 20 translated sites undeclared, green at HEAD ebfc833 with 97 literal sites = 10 exempt + 87 frozen BACKLOG. Premise holds for the enumerated sites, but the enumeration is a lower bound: the card's pathspec (no :(glob) magic) never read a file directly under src/, so 87 more literal sites in 23 files / 11 packages exist; they are frozen in the census backlog (cannot grow, must leave the list when translated) and reported below for the seat to slice. H3 confirmed on the built dist: en.d.ts declares the 8 keys and TranslationKeys = typeof en is re-exported, so Clause-2 yes and @object-ui/i18n minor. H4: main did not move from 158c75d during the run; no merge needed.",
    "tests": "All at HEAD ebfc833, repo-root vitest form through os-verify-lock. Census + 9 pin files: 10 files / 48 passed. packages/i18n/ + scripts/tests/check-i18n-call-site-keys.test.ts: 84 files passed (1458 tests). 130 existing test files importing or querying a changed module, two locked runs: 69 files / 1297 tests passed and 61 files / 704 tests passed. Ablation (one locked run, trap restore, ended clean at ebfc833): Leg A = the 24 site files at their BASE blobs (literal names on disk 0 to 20) with packs/tests at HEAD -> 10 files failed, 13 failed / 35 passed; census lists exactly the 20 translated sites 'declared 0, found 1'; every pin's zh case red ('Unable to find role=region and name 批量操作' etc.); restored, git diff HEAD empty. Leg B = BulkActionBar t(...) -> aria-label={'Bulk actions'} via objectstack scripts/ablation-replace.mjs (anchor 1->0, blob c9c2d24->05ef07e) -> census red (BulkActionBar.tsx · Bulk actions declared 0, found 1) + grid zh pin red; restored blob == HEAD. Leg C = ListView backlog literal removed (anchor 1->0, blob db03dae->11c9b6a) -> census red (declared 1, found 0); restored blob == HEAD. No dist in the path (vitest aliases src), so no dist preflight applies. Type-check exit 0 for i18n, components, fields, plugin-grid, plugin-list, plugin-chatbot, plugin-designer, plugin-detail, app-shell, console (script name type-check echoed for each); every new/edited test file is in its package's type-checked program.",
    "mcp_calls": "0 — no MCP GitHub tool used",
    "api_writes": "2 relay dispatches (POST /repos/objectstack-ai/objectstack/dispatches), executing 3 writes as objectstack-fleet[bot]: POST /repos/objectstack-ai/objectui/pulls (draft #12149), POST /repos//issues/12149/assignees (os-steve), POST /repos//issues/12145/comments (this report). git push not counted. Zero label writes.",
    "gates": "Build: turbo run build --filter=@object-ui/console^... --concurrency=2 -> 34/34; @object-ui/console -> 35/35. Type-check: 10 packages exit 0 (list in tests). i18n: check:i18n-keys 0, check:i18n-drift 0 (8 added, 0 en changed), check:i18n-dead-keys 0 (report; no new or reused key listed), check:i18n-designer-parity 0, check:eager-locale-catalogues 0 (only en eager), check:eager-closure 0. Others exit 0: check-changeset-presence.mjs (44 source files of 10 released packages, 1 changeset), changeset:check, check:changeset-claims, check:pending-changeset-literals, check:control-bytes, check:new-line-citations (0 new), check:phantom-deps, check:test-path-roots, check:vi-mock-specifiers, check:vi-mock-inherit, check:vi-mock-override-shape; governed-queue-guard --test: NOT GOVERNED. Lint (eslint on the 34 touched ts/tsx files): 0 errors, 147 warnings, none on a changed line. Repo-wide pnpm lint and full package suites: not run locally, CI owns them. CI on #12149 at report time: in_progress (14 success, 3 skipped, 13 in progress).",
    "line_budget": "45 files, +1112 / -30 = 1142 changed lines against the 3000 human-merge threshold; no split needed",
    "deviations": [
    "The census carries a frozen 87-site BACKLOG besides the triage's named exceptions: the census walks the tree, so it sees literal sites outside the claim's file surface; translating them would breach the surface and the slicing the PM reserved, so they are declared per file and name, counted exactly (a new site and a removed one both go red).",
    "Edited beyond the 31 site lines, each as the producer or call site of a site: packages/plugin-chatbot/src/ChatbotEnhanced.tsx (its L.submit/L.stopResponse fallbacks were the English name reaching viewers; it passes the new removeLabel), packages/components/src/renderers/basic/pagination.tsx (the No-Touch primitive's call site, as triage directed), the vendored prompt-input.tsx gained an additive optional removeLabel prop (objectui#7254 precedent), the fields and designer defaults maps, and two existing tests: RecordFormPage.listLinkPlural-11733.test.tsx queried the English breadcrumb name (now reads the pack, and is app-shell's zh pin) and record-alert.test.tsx (one zh case added).",
    "The apps/console pin queries by accessible name (getByLabelText), not by role: the spinner is an svg, which testing-library's role table gives no implicit role.",
    "TabBarSelect's two .find(t => ...) lambdas renamed to tab so they do not shadow the new t.",
    "The first commit's subject says 26 names; the true count is 23 named (20 literals removed). Pushed history is not rewritten; the PR title and body carry the right count.",
    "~50 minutes of queue-timeouts (exit 99, NOT MEASURED) behind sibling holder pid 14397 (objectui-issue-12144, full plugin-detail + app-shell suites, alive and progressing) before the first acquisition; slot dev-12145 kept the place."
    ],
    "files_changed": [
    ".changeset/12145-aria-label-i18n.md",
    "apps/console/src/pages/AppDocsIndex.tsx",
    "apps/console/src/pages/BookPage.tsx",
    "apps/console/src/pages/DocPage.tsx",
    "apps/console/src/pages/DocsIndex.tsx",
    "apps/console/src/pages/DocsSlug.tsx",
    "apps/console/src/pages/docsLoadingName-12145.test.tsx",
    "packages/app-shell/src/views/RecordFormPage.tsx",
    "packages/app-shell/src/views/RecordFormPage.listLinkPlural-11733.test.tsx",
    "packages/components/src/custom/spinner.tsx",
    "packages/components/src/renderers/basic/pagination.tsx",
    "packages/components/src/renderers/complex/data-table.tsx",
    "packages/components/src/tests/ariaLabelNames.zh-12145.test.tsx",
    "packages/fields/src/widgets/ImageCropperDialog.tsx",
    "packages/fields/src/widgets/PercentField.tsx",
    "packages/fields/src/widgets/RecordPickerDialog.tsx",
    "packages/fields/src/widgets/useFieldTranslation.ts",
    "packages/fields/src/tests/ariaLabelNames.zh-12145.test.tsx",
    "packages/i18n/src/locales/{ar,de,en,es,fr,ja,ko,pt,ru,zh}.ts (10 packs, 10 added lines each)",
    "packages/i18n/src/tests/ariaLabelLiteralCensus-12145.test.ts",
    "packages/plugin-chatbot/src/ChatbotEnhanced.tsx",
    "packages/plugin-chatbot/src/elements/prompt-input.tsx",
    "packages/plugin-chatbot/src/tests/ChatbotEnhanced.composerNames.zh-12145.test.tsx",
    "packages/plugin-designer/src/components/HistoryPanel.tsx",
    "packages/plugin-designer/src/components/Minimap.tsx",
    "packages/plugin-designer/src/components/VersionHistory.tsx",
    "packages/plugin-designer/src/hooks/useDesignerTranslation.ts",
    "packages/plugin-designer/src/pages/DashboardDesignPage.tsx",
    "packages/plugin-designer/src/tests/ariaLabelNames.zh-12145.test.tsx",
    "packages/plugin-detail/src/renderers/record-alert.tsx",
    "packages/plugin-detail/src/renderers/record-path.tsx",
    "packages/plugin-detail/src/renderers/tests/record-alert.test.tsx",
    "packages/plugin-grid/src/components/BulkActionBar.tsx",
    "packages/plugin-grid/src/tests/BulkActionBar.regionName.zh-12145.test.tsx",
    "packages/plugin-list/src/components/TabBar.tsx",
    "packages/plugin-list/src/components/tests/TabBarSelect.triggerName.zh-12145.test.tsx"
    ],
    "open_questions": [],
    "out_of_scope_findings": [
    "class: a (the card's own defect class; family rule: named into the closing card objectui#12145, no single-site card) · reach: named producers, shipped sources (e.g. @object-ui/plugin-list ListView's bulk-actions region, @object-ui/auth UserMenu's Profile / Settings / Sign out, @object-ui/plugin-map ObjectMap); census reading at ebfc833, not a browser measurement · evidence: 87 literal aria-label sites in 23 files that the card's re-check pathspec never read (no file directly under src/), frozen as BACKLOG in ariaLabelLiteralCensus-12145.test.ts. Proposed per-package split: plugin-designer 60 (ProcessDesigner 21, PageDesigner 18, DataModelDesigner 13, DashboardEditor 4, AppCreationWizard 3, EditorModeToggle 1), plugin-map 5, plugin-grid 4 (FormulaBar, InlineEditing), plugin-kanban 4, auth 3, layout 3, components 3 (config-panel-renderer; the No-Touch ui/breadcrumb and ui/pagination nav defaults, named only at call sites), app-shell 2 (JsonSourceEditor, QuickFind), plugin-chatbot 1 (FloatingChatbotPanel, common.closeChat exists), plugin-detail 1 (ThreadedReplies), plugin-list 1 (ListView, grid.bulkActions now exists). auth, layout and plugin-map have no @object-ui/i18n dependency · dedupe: literal aria-label backlog, untranslated accessible name, aria-label census, screen reader English",
    "carrier: none (needs a ruling on how an interpolated name is keyed) · noted, not filed: 30 aria-label template literals that interpolate around English words at BASE, outside the census by design (e.g. Dismiss plus notification title in NotificationBanners / NotificationInline / NotificationSnackbar; Remove plus name in TagsField / SelectionTray; Copy plus label in two console developer pages)",
    "carrier: none · noted, not filed: visible or sr-only English beside the names fixed here, code read only: the Shadcn PaginationPrevious / PaginationNext render visible Previous / Next spans; SidebarTrigger, rendered bare by the header-bar renderer, names itself through an English sr-only span",
    "carrier: none · noted, not filed: ChatbotEnhanced's uploadFiles fallback is still the English literal (the console passes its own label), the same shape as the submit fallback fixed in this PR"
    ]
    }

  5. objectstack-fleet commented on Oct 11, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT: PR objectui#12149 (head ebfc833033) · domain:ui seat 1 · session_016djJF12Qt14ejKR5Vjt4sK · 2026-10-11T16:47Z

    Reviewed against GitHub and the diff, not the report (os-dev-report 6111181431).

    Verdict

    • Shape:
      • Draft on main. The first line is Fixes #12145, and Clause-②: yes sits at a line start.
      • No other card number sits next to a closing keyword.
      • Assignee os-steve.
    • Scope: 45 files, +1112/−30, inside the claim's file surface.
      • The extra files are each a site's wrapper, call site or hook wiring: the pagination renderer for the No-Touch ui/pagination.tsx, ChatbotEnhanced's fallbacks, the fields and designer defaults maps, and the two existing tests that queried the English names.
      • packages/components/src/ui/ is untouched. Nothing under content/docs/releases/.
      • check-governed-merges --pr objectstack-ai/objectui#12149: NOT governed, 1,142 changed lines.
    • Contract review: 6111324034, PASS on ebfc8330337770d28901344d222430e6d53f9433, at CONTRACT_REVIEW_TIER by an isolated subagent. Implemented-by: the dev branch, Reviewed-by: this seat.
      • It judges the two widenings, each minor: eight keys in TranslationKeys, and the optional removeLabel on PromptInputAttachmentProps.
      • The other eight packages are patch.
    • The sites, read in the diff: of the 31 enumerated, 23 are named through their package's hook (20 literals removed, 3 named at their call site). 7 are exempt with reasons in the PR body: DebugPanel ×2, ui/sidebar's rail, and the vendored message.tsx ×4. 1 was a comment, not an attribute (record-path.tsx).
      • The card's re-check command now returns exactly the 10 exempt literals the PR lists.
      • Reused keys hold the same words, one key per name, as triage directs: console.commandPalette.records, home.recoveryReminder.dismiss, detail.goBack, topbar.switchView, table.previousPage/nextPage, common.loading.
    • Changeset, checked against the diff: the two English name changes it names are the only ones in the en pack ("Loading…" for the spinner; "Previous page" / "Next page" for the pagination renderer). The eight keys and the new prop are named. The control list matches the edited files.
    • Evidence: the census goes red at BASE with exactly the 20 translated sites undeclared, and green at HEAD (97 literal sites = 10 exempt + 87 frozen backlog). Every per-package zh pin is red with the site files at BASE. Ablations B and C each went red as predicted, and every restore was blob-equal.
    • Gates on this head (read at this record): 40 success, 3 skipped by design (dependabot, Test (coverage), the coverage-shard placeholder), 0 failure.

    Out-of-scope findings

    • 87 literal sites outside the card's enumeration, frozen as the census BACKLOG: filed objectui#12150 (finding, for triage), with the per-package table as its slicing list. This answers the review's escalation ③1. finding(i18n, a11y): 31 shipped aria-label attributes are literal English, so a non-English screen reader hears English names (the family's closing card) #12145's own "Done when" is met as written, so Fixes #12145 stands.
    • About 30 interpolating template names, ChatbotEnhanced's uploadFiles fallback, SidebarTrigger's screen-reader-only span: carried on objectui#12150 as members of the same family.
    • The visible English Previous / Next spans in the No-Touch pagination primitive: Acceptance notes. That is visible copy, not an accessible name; its accessible name is now set at the call site.

    Acceptance notes

    • L.removeAttachment has no labels.removeAttachment arm, so a host cannot override that one name the way it can submit and stopResponse (review ①2). Recorded, not a breach.
    • The first commit's subject says 26 names; the true count is 23. History is not rewritten, and the PR title and body are right.

    Landing: ready and auto-merge through the merge queue now.

  6. objectstack-fleet commented on Oct 11, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: PR objectui#12149 → main e723f58 · domain:ui seat 1 · session_016djJF12Qt14ejKR5Vjt4sK · 2026-10-11T17:06Z

  7. objectstack-fleet commented on Oct 11, 2026

    @objectstack-fleet
    ContributorAuthor

    Correction to this seat's landing note 6111510112 · domain:ui seat 1 · 2026-10-11T17:08Z: objectui#12150 is not waiting for a grade. Triage closed it not_planned on the maintainer's word (6111499703). The census on main keeps the 87 sites frozen: the count can only fall, and no seat is dispatched to drain it. Re-entry is the maintainer's direction, or a named requirement for a specific surface.

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

Metadata

Metadata

Assignees

Labels

a11yarea:i18nThe customer's own language, across UI, metadata and notificationsbugSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpriority:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions