Found while sweeping the consumption radius for objectstack#5506 (objectui PR objectstack-ai/objectui#3423). Outside that task's file fence (packages/plugin-detail was not in it), so filed rather than fixed there.
(Note for whoever edits this issue: the body below deliberately avoids writing a JSX open-tag literally. GitHub's body sanitizer strips a left-angle-bracket followed by a letter as an HTML tag at rest and silently truncates everything after it — the first version of this issue was cut off mid-sentence at a div tag. Describe tags, don't paste them.)
The finding
packages/plugin-detail/src/RecordDetailDrawer.tsx:277 carries a byte-identical twin of the literal #5506 just removed from packages/components/src/custom/navigation-overlay.tsx:
- guarded by
{resizable && ( … )}
- a plain
div element carrying role="separator", aria-orientation="vertical", aria-label="Resize drawer", and onPointerDown={handleResizePointerDown}
- className
"hidden sm:block absolute left-0 top-0 h-full w-1.5 cursor-col-resize …"
Same control (a drag-resize handle on a record drawer's left edge), same shape (a separator with no visible label, so the literal is the control to a screen reader), same defect: a zh/ja/de session gets one English string in an otherwise localized drawer.
This is not dormant code — RecordDetailDrawer is the detail drawer the console renders, and the handle is only gated on resizable.
Why this should be a one-liner
Two halves of the fix already exist once objectui PR objectstack-ai/objectui#3423 merges:
- The key
common.resizeDrawer lands in all ten locale packs in that PR (en "Resize drawer", zh "调整面板宽度", de "Panelbreite anpassen", …).
RecordDetailDrawer.tsx already has the hook wired — const { t } = useDetailTranslation(); at line 136, used elsewhere in the same file.
So the change is: add 'common.resizeDrawer': 'Resize drawer' to DETAIL_DEFAULT_TRANSLATIONS in packages/plugin-detail/src/useDetailTranslation.ts (that map is what keeps the no-provider path English), then swap the literal for t('common.resizeDrawer') at line 277.
Acceptance
-
RecordDetailDrawer's separator announces the session locale's value; a zh session sees no "Resize drawer" anywhere.
-
With no I18nProvider mounted it still resolves to English — plugin-detail's existing suites and the live e2e specs address this chrome by English accessible name with no provider.
-
A test under packages/plugin-detail/src/__tests__/, in the shape of objectui's packages/components/src/__tests__/navigation-overlay-chrome-i18n.test.tsx.
Note the trap that PR hit and paid for: createI18n calls instance.use(initReactI18next), and initReactI18next registers that instance as react-i18next's module-global default. The registration survives unmount and cleanup(), so once any test in a file mounts a provider with defaultLanguage: 'de', every later "no provider" render in that same file silently resolves against the German instance. Any no-provider assertion must therefore live in its own test file that never imports the provider — never in a describe block after a provider mount.
Blocked-by: the common.resizeDrawer key, which lands in objectstack-ai/objectui#3423 (objectstack#5506). Trivially unblockable by adding the key in the same PR if this is picked up first.
Found while sweeping the consumption radius for objectstack#5506 (objectui PR objectstack-ai/objectui#3423). Outside that task's file fence (
packages/plugin-detailwas not in it), so filed rather than fixed there.(Note for whoever edits this issue: the body below deliberately avoids writing a JSX open-tag literally. GitHub's body sanitizer strips a left-angle-bracket followed by a letter as an HTML tag at rest and silently truncates everything after it — the first version of this issue was cut off mid-sentence at a
divtag. Describe tags, don't paste them.)The finding
packages/plugin-detail/src/RecordDetailDrawer.tsx:277carries a byte-identical twin of the literal #5506 just removed frompackages/components/src/custom/navigation-overlay.tsx:{resizable && (…)}divelement carryingrole="separator",aria-orientation="vertical",aria-label="Resize drawer", andonPointerDown={handleResizePointerDown}"hidden sm:block absolute left-0 top-0 h-full w-1.5 cursor-col-resize …"Same control (a drag-resize handle on a record drawer's left edge), same shape (a separator with no visible label, so the literal is the control to a screen reader), same defect: a zh/ja/de session gets one English string in an otherwise localized drawer.
This is not dormant code —
RecordDetailDraweris the detail drawer the console renders, and the handle is only gated onresizable.Why this should be a one-liner
Two halves of the fix already exist once objectui PR objectstack-ai/objectui#3423 merges:
common.resizeDrawerlands in all ten locale packs in that PR (en "Resize drawer", zh "调整面板宽度", de "Panelbreite anpassen", …).RecordDetailDrawer.tsxalready has the hook wired —const { t } = useDetailTranslation();at line 136, used elsewhere in the same file.So the change is: add
'common.resizeDrawer': 'Resize drawer'toDETAIL_DEFAULT_TRANSLATIONSinpackages/plugin-detail/src/useDetailTranslation.ts(that map is what keeps the no-provider path English), then swap the literal fort('common.resizeDrawer')at line 277.Acceptance
RecordDetailDrawer's separator announces the session locale's value; a zh session sees no "Resize drawer" anywhere.With no
I18nProvidermounted it still resolves to English —plugin-detail's existing suites and the live e2e specs address this chrome by English accessible name with no provider.A test under
packages/plugin-detail/src/__tests__/, in the shape of objectui'spackages/components/src/__tests__/navigation-overlay-chrome-i18n.test.tsx.Note the trap that PR hit and paid for:
createI18ncallsinstance.use(initReactI18next), andinitReactI18nextregisters that instance as react-i18next's module-global default. The registration survives unmount andcleanup(), so once any test in a file mounts a provider withdefaultLanguage: 'de', every later "no provider" render in that same file silently resolves against the German instance. Any no-provider assertion must therefore live in its own test file that never imports the provider — never in a describe block after a provider mount.Blocked-by: the
common.resizeDrawerkey, which lands in objectstack-ai/objectui#3423 (objectstack#5506). Trivially unblockable by adding the key in the same PR if this is picked up first.