Conversation
Every hub-ui string now goes through a small in-house t() with ten
locales (en, zh-CN, zh-TW, ja, ko, es, fr, de, pt-BR, ru) and English as
the fallback. A Language select in Settings > Appearance persists the
visitor's pick per browser; createUi({ locale }) seeds the default via
ConnectionMeta.configs.ui.locale, and the running UI writes the effective
language back into the connection it publishes so same-origin frames read
it at boot.
refs #398
◈ PR LensNote This drawing shows
Architecture 5 components touched across 2 lanes. Play the interactive walkthrough Inside the changed components — 2 viewsComponent view — Hub UI Localization Engine Internal localization engine, message catalogs, and UI translation integration Component view — Locale State & Connection Publishing Reactive locale watcher and cross-frame connection storage synchronization Data flow
Follow each request, response and payload View
Tip Untick Architecture lens or Data flow lens under View to hide a diagram, or tick Expand every detail to open every section. The comment redraws in a few seconds 🪧 More tips
Thanks for using PR Lens! It's built by Coldtea, free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. |
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
|
👁️🗨️ Review this pull request with grouped, summarized diffs at: raw result💭 analyzed by {
"headSha": "0fadd97ad8d2937b797442bb9a158a4df8bf7505",
"result": {
"overallSummary": "Introduces a lightweight `t()` i18n system for `@devframes/hub-ui`: every user-facing string now routes through typed keys with 10 shipped locales, a new Settings → Appearance language picker persists the visitor's choice, and `createUi({ locale })` plus a republished connection let a same-origin frame inherit the resolved language at boot. Supporting changes cover Storybook/playground locale toggling, docs, and generated API snapshots.",
"groups": [
{
"key": "i18n-infra",
"label": "i18n infrastructure",
"category": "core",
"filePaths": [],
"children": [
{
"key": "locale-resolution",
"label": "Locale resolution & t()",
"summary": "Defines the supported locale list, BCP-47 matching, the `t()`/`tAround()` helpers and the `locale` ref that resolves visitor pick → host default → browser language → English.",
"category": "core",
"filePaths": [
"packages/hub-ui/src/locales.ts",
"packages/hub-ui/src/client/i18n/index.ts",
"packages/hub-ui/src/client/i18n/i18n.test.ts",
"packages/hub-ui/src/client/state/locale.ts",
"packages/hub-ui/src/client/state/locale.test.ts"
]
},
{
"key": "connection-publishing",
"label": "Publish locale on connection",
"summary": "Exposes `storeConnection` from `devframe/client` and wires `setupLocale` into the embedded and standalone entry points so the resolved language is written back into the published connection for same-origin frames to inherit at boot.",
"category": "api",
"filePaths": [
"packages/devframe/src/client/connection-storage.ts",
"packages/devframe/src/client/index.ts",
"packages/hub-ui/src/client/embedded/index.ts",
"packages/hub-ui/src/client/standalone/main.ts"
]
},
{
"key": "create-ui-option",
"label": "createUi locale option",
"summary": "Adds `createUi({ locale })` to seed the host's default UI language as `ConnectionMeta.configs.ui.locale`.",
"category": "api",
"filePaths": [
"packages/hub-ui/src/index.ts",
"packages/hub-ui/src/index.test.ts"
]
}
]
},
{
"key": "component-translations",
"label": "Translate hub-ui components",
"category": "ui",
"filePaths": [],
"children": [
{
"key": "command-palette",
"label": "Command palette",
"summary": "Replaces hardcoded command-palette strings (placeholder, empty/no-results states, footer hints, server badge) with `t()`/`tAround()`.",
"category": "ui",
"filePaths": [
"packages/hub-ui/src/client/components/command-palette/CommandPalette.vue",
"packages/hub-ui/src/client/components/command-palette/CommandPaletteItem.vue"
]
},
{
"key": "display",
"label": "Display components",
"summary": "Translates the confirm dialog's default buttons and the OTP input's ARIA labels.",
"category": "ui",
"filePaths": [
"packages/hub-ui/src/client/components/display/Confirm.vue",
"packages/hub-ui/src/client/components/display/OtpInput.vue"
]
},
{
"key": "dock",
"label": "Dock chrome",
"summary": "Translates dock labels, tooltips and context-menu entries, and sets `lang` on the color-scheme root so hyphenation, fonts and screen readers match the resolved locale.",
"category": "ui",
"filePaths": [
"packages/hub-ui/src/client/components/dock/ColorSchemeRoot.vue",
"packages/hub-ui/src/client/components/dock/Dock.vue",
"packages/hub-ui/src/client/components/dock/DockContextMenu.ts",
"packages/hub-ui/src/client/components/dock/DockEdge.vue",
"packages/hub-ui/src/client/components/dock/DockGroupPopover.vue",
"packages/hub-ui/src/client/components/dock/DockGroupSidebar.vue",
"packages/hub-ui/src/client/components/dock/DockOverflowButton.vue"
]
},
{
"key": "views-builtin",
"label": "Settings & built-in views",
"summary": "Translates the Settings tabs, Advanced/Docks/Shortcuts panes and the auth notice, and adds the new Appearance language `<select>` that persists the visitor's pick under `devframes-locale`.",
"category": "ui",
"filePaths": [
"packages/hub-ui/src/client/components/views-builtin/SettingsAdvanced.vue",
"packages/hub-ui/src/client/components/views-builtin/SettingsAppearance.vue",
"packages/hub-ui/src/client/components/views-builtin/SettingsDocks.vue",
"packages/hub-ui/src/client/components/views-builtin/SettingsShortcuts.vue",
"packages/hub-ui/src/client/components/views-builtin/ViewBuiltinClientAuthNotice.vue",
"packages/hub-ui/src/client/components/views-builtin/ViewBuiltinSettings.vue"
]
},
{
"key": "views",
"label": "Iframe & launcher views",
"summary": "Translates loading/error/retry copy, iframe toolbar tooltips and launcher button states.",
"category": "ui",
"filePaths": [
"packages/hub-ui/src/client/components/views/ViewAssetsError.vue",
"packages/hub-ui/src/client/components/views/ViewDockRenderer.vue",
"packages/hub-ui/src/client/components/views/ViewEntry.vue",
"packages/hub-ui/src/client/components/views/ViewIframe.vue",
"packages/hub-ui/src/client/components/views/ViewIframeLoading.vue",
"packages/hub-ui/src/client/components/views/ViewLauncher.vue"
]
},
{
"key": "state",
"label": "Command & category labels",
"summary": "Re-registers built-in commands reactively so their titles follow locale changes, and maps dock category keys through `t()` instead of a static English label table.",
"category": "core",
"filePaths": [
"packages/hub-ui/src/client/state/context.ts",
"packages/hub-ui/src/client/state/dock-settings.ts"
]
}
]
},
{
"key": "locale-files",
"label": "Locale message files",
"summary": "Adds the 151-key message file per shipped locale, `en.json` as the authoritative source and fallback; the non-English files are machine translations flagged for native review, kept in lockstep with `en.json` by the i18n test.",
"category": "i18n",
"filePaths": [
"packages/hub-ui/src/client/i18n/locales/en.json",
"packages/hub-ui/src/client/i18n/locales/zh-CN.json",
"packages/hub-ui/src/client/i18n/locales/zh-TW.json",
"packages/hub-ui/src/client/i18n/locales/ja.json",
"packages/hub-ui/src/client/i18n/locales/ko.json",
"packages/hub-ui/src/client/i18n/locales/es.json",
"packages/hub-ui/src/client/i18n/locales/fr.json",
"packages/hub-ui/src/client/i18n/locales/de.json",
"packages/hub-ui/src/client/i18n/locales/pt-BR.json",
"packages/hub-ui/src/client/i18n/locales/ru.json"
]
},
{
"key": "storybook-playground",
"label": "Storybook & playground locale",
"summary": "Adds a Storybook toolbar language toggle and wires locale setup into the playground entry so both dev surfaces exercise the new i18n.",
"category": "config",
"filePaths": [
"packages/hub-ui/.storybook/preview.ts",
"packages/hub-ui/playground/main.ts"
]
},
{
"key": "docs",
"label": "Docs",
"summary": "Documents the `locale` createUi option, how the resolved language propagates to same-origin frames, how to add a new language, and codifies the `t()`-only rule for hub-ui strings.",
"category": "docs",
"filePaths": [
".agents/06-design-system.md",
"docs/content/1.guide/18.hub-initiate.md"
]
},
{
"key": "snapshots",
"label": "Generated API snapshots",
"summary": "Regenerated public API snapshots reflecting `HUB_UI_LOCALES`, `CreateUiOptions.locale` and the newly exported `storeConnection`.",
"category": "tests",
"filePaths": [
"tests/__snapshots__/tsnapi/@devframes/hub-ui/index.snapshot.d.ts",
"tests/__snapshots__/tsnapi/@devframes/hub-ui/index.snapshot.js",
"tests/__snapshots__/tsnapi/devframe/client.snapshot.d.ts",
"tests/__snapshots__/tsnapi/devframe/client.snapshot.js"
]
}
],
"schemaVersion": 1,
"source": "llm",
"generatedAt": "2026-10-01T00:28:37.229Z",
"model": "vercel-ai-gateway/anthropic/claude-sonnet-5",
"locale": "en"
}
} |
Refs #398
What
@devframes/hub-uirenders through a small in-houset()(src/client/i18n) with typed keys fromen.json. Ten locales ship:en,zh-CN,zh-TW,ja,ko,es,fr,de,pt-BR,ru. English is the fallback.<select>, so it works inside the shadow root). The pick persists per browser underdevframes-locale, like the color scheme.Autofollows the host default, thennavigator.languages.createUi({ locale })publishes a host default asConnectionMeta.configs.ui.locale. The running UI writes the language it resolved back into the connection it publishes on its window (__DEVFRAME_CONNECTION__/__DEVFRAME_CONNECTION_META__), so a same-origin frame reads the effective language fromconnectionMeta.configs.ui.localeat boot.devframe/clientexportsstoreConnectionso the hub UI can refresh the published connection after mutating its meta.localeoption and how to add a language in the hub-initiate guide; at()rule in.agents/06-design-system.md.Translations
The non-English files are machine quality and marked for native review. The i18n test keeps every file in lockstep with
en.json(same keys, same{slot}placeholders), so a missing or renamed key fails CI.Deferred
@antfu/designFormSelectteleports its list todocument.body, which escapes the shadow root. Filed upstream as FormSelect: dropdown teleports to document.body and loses styles inside a shadow root antfu/design#30; the picker moves to it once fixed.Created with the help of an agent.