feat(spec): resolve page metadata i18n — page:header title/subtitle (#3589)#3648
Merged
Conversation
…3589) Custom system pages authored as metadata (Installed Apps, Cloud Connection, Connect an Agent) hard-code their `page:header` copy in `properties.title` / `properties.subtitle`. Every other metadata type is localized at the REST boundary, but `page` was not, so those headers stayed English in every locale while the matching nav labels translated correctly. The `pages` namespace existed only on `AppTranslationBundleSchema` — a schema no runtime reads — with no resolver behind it. This wires the read side and the schema together rather than adding another namespace nothing consumes. - `TranslationDataSchema` (the shape the i18n service actually serves) gains `pages.<name>.{label,description,title,subtitle}`. - `translatePage` translates a page's own label/description and overlays title/subtitle onto every `page:header` in its regions; registered in `translateMetadataDocument` so it rides the existing read path. - `page` added to the REST boundary's TRANSLATABLE_META_TYPES. Locale extraction, the locale-keyed ETag and `Vary: Accept-Language` already covered every metadata type, so there is no new plumbing. - `objectstack i18n extract` emits page entries, so the new namespace is not invisible to the tooling. - zh-CN / ja-JP / es-ES copy for the three Setup pages, plus the missing `nav_cloud_connection` / `nav_connect_agent` labels (zh-CN-only until now). Header copy is keyed by page name, not component id: `page:header` instances carry no stable id. `title` falls back to `pages.<name>.label`, since a page's header title and its nav label are normally the same string. English literals stay in metadata as the fallback — a page with no `pages` entry renders exactly as before — and the console needs no change, since pages arrive already localized from the server. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TubWYdWquVkS9dj733sDmC
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 4 package(s): 110 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
`content/docs/references/system/translation.mdx` is generated from the spec schemas, so adding `pages` to `TranslationDataSchema` left it out of date (caught by `@objectstack/spec check:docs`). Regenerated via `gen:schema && gen:docs`. `content/docs/ui/translations.mdx` is hand-written and enumerated the translatable surfaces and the per-request metadata types — both stale as of this branch. Added the `pages` row and `page` to the resolved-types list, plus a note on why header copy is keyed by page name. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TubWYdWquVkS9dj733sDmC
`gen:api-surface` snapshot pins @objectstack/spec's public exports. Adding `translatePage` and its `PageLike` / `PageRegionLike` / `PageComponentLike` shapes is purely additive (0 breaking, 4 added) and mirrors the existing `translateDashboard` / `DashboardLike` / `WidgetLike` surface. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TubWYdWquVkS9dj733sDmC
os-zhuang
marked this pull request as ready for review
July 27, 2026 13:05
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.
Closes #3589. Implements 方案 A(framework 通用解析)from that issue.
The gap
Three plugin-carried Setup pages hard-code their
page:headercopy in metadata:packages/cloud-connection/src/marketplace-ui.tspackages/cloud-connection/src/cloud-connection-ui.tspackages/mcp/src/connect-ui.tsEvery other metadata type is localized at the REST boundary, but
pagewas not — so those headers stayed English in every locale while the matching nav labels translated correctly.Two things were missing, and shipping only one would have been another no-op:
pagesnamespace existed only onAppTranslationBundleSchema, whose accessors (getAppBundle/loadAppBundle) are optional onII18nServiceand have zero implementations. The schema the runtime actually serves —TranslationData, viagetTranslations(locale)— had nopageskey.translateMetadataDocumentdispatched five types;pagefell through unchanged.Approach
Option A over a console-side point fix, because the server already has every seam this needs and the console needs none:
TranslationDataSchemagainspages.<name>.{label,description,title,subtitle}, alongsidedashboards.translatePagein@objectstack/spec/systemtranslates a page's ownlabel/descriptionand overlaystitle/subtitleonto everypage:headerin the page's regions. Immutable overlay, mirroringtranslateDashboard. Registered intranslateMetadataDocument.pageadded toTRANSLATABLE_META_TYPESinrest-server.ts— a one-token change.Accept-Languageextraction, the locale-keyed ETag ([P2] i18n: zh-CN ↔ English switch occasionally garbled — requires page refresh #1319) andVary: Accept-Languagealready applied to every metadata type on both the list and single-item routes.objectstack i18n extractnow emits page entries so the new namespace is not invisible to the tooling.Two design choices worth review:
page:headerinstances carry no stableid, so the page name is the only addressable identifier on the document.titlefalls back topages.<name>.label. For all three pages the header title and the nav label are the same string; requiring both would just invite drift. An explicittitlestill wins when the two genuinely differ.Also fixed
nav_cloud_connectionandnav_connect_agentexisted only inzh-CN.ts— added toen/ja-JP/es-ES.No console change needed
Pages arrive already localized from the server, so
page:headerrenders translated copy with no@object-uichange. Two bonuses fall out for free:transformSpecTranslationsforwards unknown top-level keys verbatim, sopages.*reaches the client catalog, whereuseObjectLabel().pageLabelalready probespages.<name>.label— page breadcrumbs pick up the translations too.Compatibility
Authoring is unchanged and English literals stay in metadata as the fallback: a page with no
pagesentry renders exactly as before. The English bundle entries are byte-identical to the plugin literals, soenrequests are a no-op.Testing
translatePage(11 cases): header title/subtitle overlay, non-header components untouched,iconand other props preserved, explicit-title precedence, untranslated-page passthrough, no input mutation, no-bundle fallback, region-less pages, dispatch throughtranslateMetadataDocument, BCP-47 chain (zh→zh-CN). PlusTranslationDataSchema.pagesacceptance, two REST-boundary cases (envelope + list), and a CLI extraction case asserting the exact emitted key set.Suites run green:
spec1096,rest173,cli635,platform-objects215,lint359.Known consideration (pre-existing, not introduced here)
Translating on the read path means a Studio round-trip could persist localized strings back into metadata. That is already true for
object,app,dashboard,viewandaction;pagenow joins that set rather than diverging from it. Worth a separate fix if it bites — it needs a read-path bypass for the editor, which does not exist today for any type.Follow-ups (not in this PR)
CloudConnectionPanel.tsx(the Cloud Connection page body) has no i18n at all and nocloudConnection.*namespace in any of objectui's 10 locale packs. 自定义系统页 page:header 未国际化 (Installed Apps / Cloud Connection / Connect an Agent) #3589 assumed the widget bodies were mostly translated; that one is not, so that page stays mixed-language below the header until it is done. Belongs in objectui.enbundle would catch drift, but wiring it needs a package that depends on bothcloud-connectionandmcp—platform-objectsshould not.🤖 Generated with Claude Code
https://claude.ai/code/session_01TubWYdWquVkS9dj733sDmC
Generated by Claude Code