Repository navigation
fix(console): the standalone /forms route shows select option labels in the language the user chose - #26584
Conversation
… (red) Claude-Session: https://claude.ai/code/session_01SMps7wq7rBTk53e8KTSEup Co-authored-by: Claude <noreply@anthropic.com>
… authenticated fetch, so option labels follow the chosen language /forms/:name read the object schema with a bare fetch, so the server localized option labels by the browser's Accept-Language. The internal route now takes SchemaRendererContext.apiFetch, the channel the in-app record form reads the schema over, which sends the session language. Claude-Session: https://claude.ai/code/session_01SMps7wq7rBTk53e8KTSEup Co-authored-by: Claude <noreply@anthropic.com>
…rm-options-locale
维护者速读改了什么:独立表单路由 席位核对seat 2 · 2026-10-27T09:13Z · head
|
Fixes #26452
Clause-②: no
Metadata-protocol: none
维护者速读
/forms/:name(及/apps/:appName/forms/:name)改走控制台已认证的 fetch 读写 API,下拉选项标签随用户所选语言显示。fetch读对象 schema,服务端按浏览器的Accept-Language本地化选项;应用内弹窗表单走的通道带会话语言,所以同一会话两处语言不一致(p1,发版扫描 FAIL)。X-Tenant-ID、Accept-Language,与弹窗表单一致;公开表单/f/:slug不变。回滚即 revert 本 PR。Summary
On the standalone internal form route, a user whose chosen language differs from the browser's saw field labels in their language and every select option in the browser's. The in-app overlay form (
?form=ID) in the same session showed the options translated.Root cause
The overlay (
ModalForm→ObjectForm) does not translate option labels on the client. Its field builder (fromObjectSchemain@object-ui/plugin-form) handsfield.optionsto the widget as served. The translation happens on the server: the object schema is read over the console's authenticated fetch (createAuthenticatedFetch, published to the renderers asSchemaRendererContext.apiFetch). That fetch stampsAccept-Languagewith the session's UI language (thelangattribute of the html element), and the server localizes/meta/object, option labels included, in that language.FormPageread/meta/view,/meta/objectand the record with a barefetch, so the request carried the browser's language. Field labels still came out translated because they resolve on the client through the bundle (fieldLabel); options have no client lookup on either form surface, so they followed the browser. This matches the card's Control 2: a zh-CN browser already got Chinese options on this route.Fix
ui/apps/console/src/components/FormPage.tsx: in internal mode, every API request (the form view, object schema and record reads, and the create/update submit) goes throughSchemaRendererContext.apiFetch, the host's authenticated fetch the console shell publishes. With no provider above the route it falls back to the barefetch, as before. The public anonymous route/f/:slugkeeps the barefetch.No option-label translator is added. The route now takes the channel the overlay takes, so the server's one resolver answers both surfaces. An option the bundle does not translate keeps its authored label, because that is the server's fallback.
InternalFormRoute.tsxis unchanged: both internal mounts (InternalFormRouteandInAppFormRoute) renderFormPage mode="internal"inside the console shell'sSchemaRendererProvider.Tests
New pin
ui/apps/console/src/components/FormPage.optionLabelLocale-26452.test.tsx. It mounts/forms/:namein edit mode inside aSchemaRendererProviderwhoseapiFetchis the realcreateAuthenticatedFetch({ sameOriginOnly: true }), with the html element'slangset tozh-CNand a stub server that localizes/meta/objectby the request'sAccept-Language.评审中,高), and the schema read carriedzh-CN.urgent) keeps its authored labelUrgent.Red first, at
1768fd79(pin only, no fix): 2 of 2 failed,AssertionError: expected 'In Review' to contain '评审中'andexpected 'Done' to contain '已完成'. Green atff64ab6a(fix): 2 of 2 passed.At head
f0294fa8(after mergingorigin/main), under the shared verify lock (VERDICT command-exit 0):pnpm --filter "@object-ui/console^..." build: ok;FormPageorInternalFormRoute(FormPage.*,formPageRequiredMarker,internalFormShell,formViewFeaturesRootRefused,App.homePermissions,App.docsPortalLazy,App.uploadAltitude,createdRecordPath,submitRedirect,OAuthConsentPage.agent-scopes): Test Files 42 passed (42), Tests 415 passed (415);pnpm --filter @object-ui/console type-check: exit 0. The console tsconfig includessrc, so the new test is type-checked.Live showcase reproduction: NOT MEASURED. Booting the showcase plus a console build and a browser on the shared box was not cheap under the verify-lock queue. The pin models the server's measured behaviour (the card's Control 2) with the real host fetch.
Gates
Local, at
f0294fa8:node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ranreports 33 derived, 33 run, 0 NOT-MEASURED, and every one exited 0. That includesnode scripts/check-sdui-manifest.mjs(intact, 106 components; no registry contract changed, so it was not regenerated) andpnpm --dir ui exec vitest run scripts/(178 passed, 2 skipped). CI runs the rest.Acceptance notes
/f/:slugstill reads over the barefetch. For an anonymous visitor the UI language is the detected browser language, so nothing diverges. A signed-in visitor who chose another language would still see options in the browser's language there. Moving that route onto a session-carrying channel changes what the anonymous resolver and submit see, so it is left out of this change.?form=UNKNOWN_IDopening a blank edit form) are not addressed here.Generated by Claude Code