Skip to content

fix(cloud-connection): localize the Cloud Connection panel (objectstack#3589 follow-up) - #2865

Merged
os-zhuang merged 1 commit into
mainfrom
claude/custom-system-page-i18n-6ni3f8
Jul 27, 2026
Merged

fix(cloud-connection): localize the Cloud Connection panel (objectstack#3589 follow-up)#2865
os-zhuang merged 1 commit into
mainfrom
claude/custom-system-page-i18n-6ni3f8

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Follow-up to objectstack-ai/objectstack#3589 / objectstack-ai/objectstack#3648, and the remaining "复核其余 SDUI widget 正文 i18n 覆盖" item on #2851.

The gap

CloudConnectionPanel — the cloud-connection:panel SDUI widget that is the entire body of the Cloud Connection Setup page — had no i18n at all: no @object-ui/i18n import, and no cloudConnection namespace in any of the ten built-in locale packs.

This was missed because issue #3589 recorded that the three pages' body widgets were "大多已 i18n". Two of them are: marketplace:installed-list and mcp:connect-agent are fully localized across all ten packs. This one never was. With the framework-side page:header resolution now merged, the page rendered a translated header above an entirely English body — so the fix for that issue left this page visibly half-done.

Changes

New cloudConnection namespace in all ten packs (en, zh, ja, ko, de, fr, es, pt, ru, ar), matching the coverage its sibling namespaces already had. It covers every phase of the RFC 8628 device-code flow: checking, error + retry, waiting.* (approval prompt, user code, copy/copied), bound.* (the connection detail labels), and unbound.* (the call to action).

Failure messages are translated where they are raised, not where they are rendered. The three hard-coded literals (expired request, bind failure, device-code request failure) are stored in component state and rendered later via phase.message, so translating at the render site was not an option. t is added to the two useCallback dependency arrays accordingly.

One stitched sentence was restructured. The "code is pre-filled…" line was a single sentence assembled across JSX from a fragment, a conditional tail (— if the window did not appear,), an anchor, and a bare '.'. Translating that shape hands a translator a dangling clause whose word order JSX has already fixed — unworkable for languages that order clauses differently. It is now two self-contained strings: a complete sentence plus a standalone link phrase.

bound_at formats with the active UI language (toLocaleString(language)) rather than the browser default, so the timestamp matches the copy around it.

Testing

packages/i18n/src/__tests__/cloudConnection-locale-parity.test.ts asserts the cloudConnection key set is identical across all ten packs, every leaf is a non-empty string, and the prose keys in non-English packs actually differ from English (short labels like "Runtime" are excluded — they legitimately collide).

Partial coverage is the failure mode worth pinning, because it degrades quietly: i18next falls back to en and the result merely looks half-translated. That is exactly how both this bug and the zh-CN-only nav_cloud_connection label survived unnoticed.

I mutation-tested the guard rather than trusting a green run — deleting one key from zh.ts fails it, restoring passes.

Suites run locally: @object-ui/i18n 110 passed, @object-ui/app-shell 1859 passed, turbo type-check green for both packages, ESLint clean on all changed files (no new warnings).

Not covered here

The panel keeps its pre-existing catch (err: any) blocks and the set-state-in-effect lint warning in useEffect — both predate this change and are out of scope for a localization pass.

🤖 Generated with Claude Code

https://claude.ai/code/session_01TubWYdWquVkS9dj733sDmC


Generated by Claude Code

…ck#3589 follow-up)

`CloudConnectionPanel` — the `cloud-connection:panel` SDUI widget that is the
entire body of the Cloud Connection Setup page — had no i18n at all: no
`@object-ui/i18n` import, and no `cloudConnection` namespace in any of the ten
built-in locale packs. Its siblings on neighbouring pages
(`marketplace:installed-list`, `mcp:connect-agent`) were already fully
localized, so once the framework-side `page:header` resolution landed
(objectstack#3648) this page rendered a translated header above an English body.

- New `cloudConnection` namespace in all ten packs, matching the coverage its
  sibling namespaces already had. Covers every phase of the device-code flow:
  checking, error + retry, waiting (approval prompt, user code, copy), bound
  (connection detail labels), and unbound (call to action).
- The three hard-coded failure messages are translated where they are raised
  rather than where they are rendered, since they live in component state.
- The "code is pre-filled…" line was one sentence stitched across JSX with a
  conditional tail and a bare '.'. It is now two self-contained strings, so a
  translator never receives a dangling clause whose word order is fixed by JSX.
- `bound_at` now formats with the active UI language instead of the browser
  default, matching the surrounding copy.

Adds a locale-parity test over the `cloudConnection` key set. Partial coverage
is the failure mode worth pinning because it degrades quietly — i18next falls
back to `en` and the page merely looks half-translated, which is exactly how
this bug and the zh-CN-only `nav_cloud_connection` label both survived.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TubWYdWquVkS9dj733sDmC
@vercel

vercel Bot commented Jul 27, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectui Ignored Ignored Jul 27, 2026 1:43pm

Request Review

@github-actions github-actions Bot added the tests label Jul 27, 2026
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Main entry (gzip) 28.0 KB 350 KB
Entry file index-DxugL_Qn.js
Status PASS

📦 Bundle Size Report

Package Size Gzipped
app-shell (index.js) 8.20KB 2.97KB
app-shell (runtime-config.js) 7.42KB 2.32KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 7.57KB 2.97KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 1.17KB 0.53KB
auth (AuthProvider.js) 21.70KB 4.21KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.12KB 3.41KB
auth (LoginForm.js) 17.86KB 5.29KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.43KB 2.09KB
auth (SocialSignInButtons.js) 9.60KB 3.89KB
auth (UserMenu.js) 3.40KB 1.22KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 33.74KB 8.53KB
auth (createAuthenticatedFetch.js) 4.37KB 1.69KB
auth (index.js) 1.83KB 0.79KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 4.86KB 0.85KB
auth (useIsWorkspaceAdmin.js) 1.61KB 0.85KB
collaboration (CommentThread.js) 18.38KB 4.49KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 3.65KB 1.42KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.25KB 0.53KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 450.90KB 98.17KB
core (index.js) 2.12KB 0.77KB
create-plugin (index.js) 9.28KB 2.98KB
data-objectstack (index.js) 127.29KB 31.96KB
fields (index.js) 218.35KB 53.54KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (currency.js) 1.22KB 0.64KB
i18n (i18n.js) 4.32KB 1.77KB
i18n (index.js) 2.46KB 0.96KB
i18n (pickLocalized.js) 1.70KB 0.83KB
i18n (provider.js) 5.37KB 1.72KB
i18n (useObjectLabel.js) 25.17KB 5.80KB
i18n (useSafeTranslation.js) 2.87KB 1.28KB
layout (index.js) 38.45KB 10.67KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.74KB
mobile (index.js) 1.50KB 0.62KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 4.42KB 1.27KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.71KB 0.42KB
mobile (useResponsiveConfig.js) 1.36KB 0.63KB
mobile (useSpecGesture.js) 1.77KB 0.77KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 6.84KB 2.42KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 3.67KB 1.12KB
permissions (evaluator.js) 4.00KB 1.23KB
permissions (index.js) 0.91KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.52KB
permissions (usePermissions.js) 1.55KB 0.71KB
plugin-ai (index.js) 15.71KB 3.79KB
plugin-calendar (index.js) 45.37KB 12.48KB
plugin-charts (index.js) 47.20KB 13.35KB
plugin-chatbot (index.js) 179.53KB 42.79KB
plugin-dashboard (index.js) 109.60KB 28.33KB
plugin-designer (index.js) 210.92KB 42.69KB
plugin-detail (index.js) 215.28KB 52.50KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 103.53KB 25.12KB
plugin-gantt (index.js) 162.33KB 39.53KB
plugin-grid (index.js) 178.20KB 46.71KB
plugin-kanban (index.js) 47.82KB 13.18KB
plugin-list (index.js) 98.71KB 23.32KB
plugin-map (index.js) 16.80KB 5.24KB
plugin-markdown (index.js) 13.65KB 4.67KB
plugin-report (index.js) 37.07KB 9.81KB
plugin-timeline (index.js) 25.37KB 7.20KB
plugin-tree (index.js) 8.36KB 2.81KB
plugin-view (index.js) 85.70KB 20.87KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.55KB 0.67KB
providers (UploadProvider.js) 11.71KB 3.53KB
providers (index.js) 0.44KB 0.22KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 3.19KB 1.38KB
react (LazyPluginLoader.js) 3.77KB 1.33KB
react (SchemaRenderer.js) 18.70KB 6.09KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 1.00KB 0.55KB
sdui-parser (codegen.js) 4.09KB 1.74KB
sdui-parser (index.js) 2.16KB 0.94KB
sdui-parser (parse.js) 10.04KB 2.82KB
sdui-parser (types.js) 0.29KB 0.24KB
sdui-parser (validate.js) 4.69KB 1.48KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 0.99KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 0.20KB 0.18KB
types (crud.js) 0.20KB 0.18KB
types (data-display.js) 0.20KB 0.18KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 0.77KB 0.41KB
types (disclosure.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (index.js) 1.86KB 0.91KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 0.20KB 0.18KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (spec-report.js) 5.04KB 1.93KB
types (system-fields.js) 2.39KB 1.17KB
types (theme.js) 0.20KB 0.18KB
types (ui-action.js) 0.75KB 0.46KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@os-zhuang
os-zhuang marked this pull request as ready for review July 27, 2026 13:53
@os-zhuang
os-zhuang merged commit 20df08c into main Jul 27, 2026
14 checks passed
@os-zhuang
os-zhuang deleted the claude/custom-system-page-i18n-6ni3f8 branch July 27, 2026 13:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants