Skip to content

fix(i18n): console chrome reaches the bundle — list switcher, aggregate prefixes, dialog a11y fallbacks, SettingsView (#4024) - #4602

Merged
yinlianghui merged 1 commit into
mainfrom
claude/issue-4024-console-chrome-i18n
Aug 13, 2026
Merged

fix(i18n): console chrome reaches the bundle — list switcher, aggregate prefixes, dialog a11y fallbacks, SettingsView (#4024)#4602
yinlianghui merged 1 commit into
mainfrom
claude/issue-4024-console-chrome-i18n

Conversation

@yinlianghui

Copy link
Copy Markdown
Collaborator

Fixes #4024

Six strings on the two screens a user looks at most were hardcoded English literals rather than bundle lookups, so they stayed English on every non-English console with nothing an app could author to change them. This converts all of them, plus the SettingsView unit recorded in the card's comment, through the existing console bundle — adding keys to all ten packs.

Precondition check — objectstack#5084

The card was migrated from objectstack#5084 under the file-at-destination ruling, and its body requires that thread's triage rulings and hold/restart conditions be read before acting. Read in full: no hold or restart condition is in force. The thread has exactly two comments — a triage comment (分诊:入队 pm:queue) and a migration comment closing it as not planned with the words "moved, not rejected". The triage comment carries three leads, all checked against origin/main, all three load-bearing:

  • ViewSwitcher.tsx Grid/Gallery hardcoded, with the bundle already carrying viewTypeGrid/viewTypeGallery — "疑似未接线". Confirmed exactly.
  • plugin-form's a11y fallback. Confirmed, at ModalForm.tsx:842 / DrawerForm.tsx:642 (line numbers had moved).
  • "Avg:/Sum: 前缀在 origin/main 上未直接 grep 命中,可能已修或以模板串形式存在 —— 开发时请先与已落地的 i18n 修复对账,只补余量." The most valuable lead. The prefixes are alive, in plugin-grid, not plugin-list — and reconciling with landed fixes found a fourth remainder the card could not have known about; see the close button below.

String census (file:line, on origin/main)

# string location disposition
1 Grid / Gallery (+7 more) packages/plugin-list/src/ViewSwitcher.tsx:57-67 (VIEW_LABELS, read at :122/:123/:134, :148/:156, :173, :216/:217/:229) reuse existing keys
2 Sum Avg Min Max Count Empty Filled Unique packages/plugin-grid/src/useColumnSummary.ts:70-83 (TYPE_LABELS), joined at :231/:234/:269 new grid.summary.*
3 Complete the form fields, then submit or cancel. packages/plugin-form/src/ModalForm.tsx:842, DrawerForm.tsx:642 new form.dialogDescriptionFallback
4 Close (sr-only) packages/components/src/custom/mobile-dialog-content.tsx:147 reuse common.close
5 SettingsView, 18 strings apps/console/src/pages/settings/SettingsView.tsx :143, :167, :185, :212, :218, :234, :250, :261, :262, :264, :273, :284, :303, :309, :312, :362, :377, :381 new console.settingsView.*

Row 5 is wider than the nine strings the comment listed: converting the screen turned up the bare Locked by environment (no-key variant), both Cannot encrypt secrets toasts, and both halves of the crypto panel's body sentence. Those are the same refusal unit as the heading the comment names, so they convert with it rather than leaving a file split-conventioned in a new place.

Two of the six needed no new keys — the more interesting half

The switcher was a wiring gap, not a missing capability. console.objectView.viewType{Grid,Kanban,Gallery,…} already exists in all ten packs and already carries exactly these words: the create-view picker (packages/app-shell/src/views/CreateViewDialog.tsx:88-96) has resolved them through the bundle for months. Minting a parallel list.viewType.* would have duplicated nine already-translated words into ten packs and left two tables free to drift, so the picker's 「画廊」 and the switcher's 「画廊」 are now the same key. A plugin package reading a console.* key has in-repo precedent in this very namespace: packages/plugin-view/src/ObjectView.tsx:83 resolves console.objectView.new.

The close button is a remainder of a fix that already landed, and only the triage comment's "只补余量" instruction surfaced it. objectstack#5505 routed the sr-only close label through CloseSrLabelcommon.close, and sheet-dialog-close-i18n.test.tsx pins it — but it patched the two Shadcn-synced primitives under src/ui/** through the declared patch in scripts/shadcn-local-patches.mjs. custom/mobile-dialog-content.tsx is a hand-written wrapper outside that regeneration zone, with its own close button, and it is what plugin-form's ModalForm renders — so the create/edit dialog the card measured was precisely where an English "Close" survived. One-line fix, no new key.

Key taxonomy and the convention matched

  • console.settingsView.*, placed immediately after console.settingsHub in every pack. The convention matched is the sibling's, exactly: SettingsHub.tsx uses useObjectTranslation from @object-ui/i18n + t('console.settingsHub.*'), and SettingsView.tsx now does the same. The comment's own framing is the rationale — the screen converts as one unit or the file stays split-conventioned.
  • grid.summary.*, a nested block inside the existing grid namespace, matching the grid.toolbar / grid.import sub-namespace shape already there.
  • form.dialogDescriptionFallback, beside the existing form.discardTitle / form.discardMessage this file's safe-translation table already owns.
  • Reused, not minted: console.objectView.viewType* (9), common.close (1), and console.settingsHub.beta — the Beta badge is the same release-stage badge on the same feature, and zh deliberately keeps it Latin under an existing entry in untranslated-identity-4376. A settingsView.beta twin would have needed a second allowlist entry saying the same thing.

Net: 32 new keys across ten packs, 10 reused.

The label/value join is a key, not a ': '

The aggregate footer built `${label}: ${formatted}`, which hands the locale the word and keeps the punctuation in English. grid.summary.pattern ({{label}}: {{value}}) owns the whole shape instead — zh sets a fullwidth colon, fr the French space-before-colon. Same reasoning the packs already record for collaboration.resolvedSuffix: "separator included, so a translator owns the whole phrase rather than inheriting an English-shaped glue." Locked by environment: {{key}} and the crypto refusals are parameterized the same way, never concatenated.

Plural mechanism

{dirtyKeys.length} unsaved change{dirtyKeys.length > 1 ? 's' : ''} is an English plural rule executing in every locale. It is now a real i18next family — base key + _one + _other in all ten packs.

This repo has two count conventions and the choice between them is load-bearing. The xxxCount / xxxCountOne two-sibling-key shape (common.itemCount, collaboration.commentCount) was not used: its stated rationale is a parity fear that objectui#3863's base-key fix has since answered, and it hard-codes a two-form English-shaped split that ru and ar do not have. The mechanism used instead is the one perm.facet uses and all-locales-key-parity.test.ts owns.

The base key is the whole point. i18next asks Intl.PluralRules for the one suffix a language needs and, finding no such slot, walks fallbackLng to en. ru has four categories and ar six; no pack here enumerates _few/_many/_two/_zero, so without a base key ru renders English at counts 2-20 — which is objectui#3863 measured on detail.showEmptyRelated. ru and ar take the "noun: {count}" base form their own packs already document for exactly this reason (ru's perm.facet note: «этот приём в паке уже применён 25 раз … он верен при ЛЮБОМ числе»). Verified rendering in-language at counts 1, 2 and 5.

The #4514 provider-less trap — decided per test, and per package

t() outside a provider returns the key, so this had to be decided rather than assumed. The split is by who consumes the code, not by preference:

  • Published components → createSafeTranslation, tests unchanged. ViewSwitcher, useColumnSummary, ModalForm/DrawerForm and CloseSrLabel all resolve through the safe factory, so a provider-less host renders English rather than a raw key. This is not optional: useColumnSummary is a public export whose own sibling suite asserts /Sum: / with no i18n provider, and discardGuard.test.tsx drives ModalForm and clicks its close control by English name. Four new no-provider suites pin this explicitly rather than leaving it assumed.
  • SettingsView.crypto-unavailable.test.tsx → gained an en I18nProvider. Its two most valuable assertions are about interpolation (Cannot encrypt secrets: ai.api_key, Locked by environment: api_key). With no provider the key comes back bare and the {{subject}}/{{key}} holes are never filled — so re-pointing those assertions at key literals would have kept the file green while deleting the thing it tests.
  • SettingsView.envelope.test.tsx → deliberately still provider-less, and a note now records why. Everything it asserts is either manifest-authored content (Timezone, Branding — off the payload, not the pack) or the hub's key literal console.settingsHub.empty, which it pins because t() with no provider returns the key. Adding a provider would delete the one i18n assertion in the file.

Each new locale-mounted suite has its no-provider case in a separate file, following the warning sheet-dialog-close-i18n.test.tsx records: createI18n registers a react-i18next module-global default that survives cleanup(), so a no-provider render sharing a file with locale-mounted ones resolves against whichever locale ran last (it failed there naming a button "Cerrar").

Red-first — verbatim

Predictions were written into every test file header before the first run.

Run 1Test Files 3 failed | 3 passed (6), Tests 12 failed | 8 passed (20):

AssertionError: expected 'Sum: 1,234' to contain '合计'
AssertionError: expected 'Count: 3' to contain '计数'
AssertionError: expected 'Empty: 33.3%' to contain '空值'
AssertionError: expected 'Sum: 1,234' to contain '合計'
AssertionError: expected 'Sum: 1,234' not to match /Sum|Avg|Min|Max|Count|Unique/
AssertionError: expected 'Sum: CN¥1,234' to contain '合计'

Run 2 — plugin-form, Tests 2 failed | 4 passed (6):

AssertionError: expected 'Complete the form fields, then submit…' to be '填写表单字段,然后提交或取消。'

Run 3 — SettingsView, Tests 7 failed | 2 passed (9): all zh/ru cases red.

One prediction was wrong, and it is recorded in the test header rather than quietly corrected. The SettingsView header first claimed the counter cases would go red in both languages including en, reasoning that the bundle had no unsavedCount key yet. The en counter case passed pre-fix — because the concatenation it replaces is the English plural rule, so English is precisely the one language whose output cannot change. That is the defect stated exactly: the screen was correct in English and could not be correct anywhere else. The en counter case is therefore a must-not-change pin; ru and zh carry the evidence.

Reverse verification

git diff > fix.patch + git checkout origin/main -- <19 files> + git apply + sha256sum -c (all 19 OK). No git stash at any point.

With the fix removed: Test Files 5 failed | 4 passed (9), Tests 21 failed | 14 passed (35). The 21 red are every translated assertion; the 14 that stay green are exactly the English and no-provider must-not-change pins — the correct direction here, since an English-only assertion cannot distinguish a literal from a bundle lookup that resolves to the same word.

Must-not-change

  • Every neighbouring already-translated string unmoved — check:i18n-en-drift reports 0 en values changed, 32 added, 0 removed, and all ten packs' source diffs are purely additive (0 lines removed in every locale file).
  • Aggregate NUMBER formatting unmoved (fix(core): one home for the number-display policy — and percent stops drifting between cell and measure (#4576) #4589's landed surface). Every toLocaleString / Intl.NumberFormat call in useColumnSummary is byte-identical; packages/core/src/utils/** untouched. Pinned positively by a case asserting the zh prefix and CN¥ and 1,234 in one label.
  • detail.highlightFields, detail.sortBy, all console.settingsHub.* keys unmoved.
  • SUPPORTED_SUMMARY_TYPES still derives from the total record, so a spec-added member is still a compile error.

Verification

  • Build closure first, each touched package plus its dependencies, under the shared flock.
  • Repo-root pnpm exec vitest run --maxWorkers=2 over the full consumption radius: Test Files 271 passed (271), Tests 3016 passed (3016) — i18n, apps/console settings, plugin-form, plugin-list, plugin-grid, components, plus CreateViewDialog.ariaRequired.test.tsx (the other consumer of the reused viewType keys).
  • type-check green on all six packages.
  • Gates: check:i18n-call-site-keys PASS (2928 en keys, every call-site key resolves, every inline default matches, every interpolation argument matches its holes), check:i18n-en-drift PASS, plus control-bytes, phantom-dependencies, changeset-presence, changeset-no-major, changeset-fixed, type-check-coverage, lint-coverage, doc-links — all PASS.
  • Control-byte self-scan grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f]' over all 29 touched files including untracked: clean.
  • eslint vs an origin/main compare worktree: 0 errors both sides. +10 warnings, all @typescript-eslint/no-explicit-any on the four new test fixtures (const cols: any[], const ds: any), matching the sibling suites' existing convention verbatim.

One gate caught a real mistake

A run filtered on packages/app-shell/src/views/CreateViewDialog.test.tsx — a file that does not exist — was rejected by this repo's vitest path guard (objectui#3288) instead of exiting green with passWithNoTests. Re-run against the real CreateViewDialog.ariaRequired.test.tsx. Recorded because the guard prevented a false green in this very PR.

.d.ts measured both ways (dist + tsbuildinfo cleared)

289 .d.ts files hashed before and after a clean rebuild. Exactly 12 moved:

package .d.ts grading
@object-ui/i18n 11 locale .d.ts gain new readonly literal members minor — published bundle keys added; TranslationKeys widens. Purely additive.
@object-ui/console unchanged minor — new published bundle keys reach users through this app (fixed group).
@object-ui/plugin-grid useColumnSummary.d.ts patch — comment-only. Proven: with comments stripped the before/after declarations are byte-identical; the delta is the two JSDoc words TYPE_LABELSTYPE_LABEL_KEYS. No exported declaration moved.
@object-ui/plugin-list unchanged patch — internal literal→lookup swap, .d.ts byte-identical.
@object-ui/plugin-form unchanged patch — same.
@object-ui/components unchanged patch — same.

No major anywhere; check:changeset-no-major and check:changeset-fixed both pass.

Translation spot-check flags for a human reviewer

Every pack's existing register was censused first and neighbouring words reused verbatim rather than re-invented — common.back, common.close, form.discard, detail.saveChanges, form.saveError, settingsHub.loadError and the existing actionFailed are lifted unchanged per pack. The genuinely new prose, worth a native-speaker eye:

  1. ru / ar counter base keys. Несохранённых изменений: {{count}} and {{count}} تغيير(تغييرات) غير محفوظ follow each pack's own documented idiom for this exact problem, but they are the strings that must read naturally at 2, 5 and 11.
  2. Crypto-refusal sentences (cryptoRefusalTitle, cryptoRefusalSubjectSuffix, cryptoRefusalNoSubject) in all nine — new prose with no neighbour to copy, and the suffix form must grammatically follow a code element in each language.
  3. ko cryptoRefusalSubjectSuffix uses 은(는), the written-form particle hedge. Correct but formal; a native speaker may prefer a rephrasing that avoids the particle entirely.
  4. grid.summary.countUnique — zh 去重 / ja 一意 / ko 고유 are terse column-footer forms; confirm they read as footer labels rather than verbs.
  5. fr pattern is {{label}} : {{value}} (space before colon, French typography) while the other Latin packs keep : . Deliberate.
  6. grid.summary.pattern is identical to en in ja/ko/ru/ar and is allowlisted in untranslated-identity-4376 as a pure format string — two holes and a separator, no prose. The packs that needed a different separator have one (zh, fr), so the identity is a translation decision rather than an omission.

Surface respected

Touched none of the in-flight seats' files: no packages/types or packages/core type declarations (#4580), no scripts/** (#4592, gates run only), no packages/fields/** (#4590), no dashboard-filters.ts or dashboard docs/examples (#4356), no content/docs/releases/**. packages/core/src/utils/** untouched — #4589's number-display surface is consumed, never edited.


Generated by Claude Code

…te prefixes, dialog a11y fallbacks, SettingsView (#4024)

Six strings on the two screens a user looks at most were hardcoded English
literals rather than bundle lookups. None corresponds to authorable metadata,
so no app could work around them.

Two needed no new keys at all. The list-view mode switcher named its nine
visualizations from a private VIEW_LABELS table while console.objectView.
viewType* — the same nine words — had been resolved through the bundle by the
create-view picker for months. And the create/edit dialog's close button is the
remainder of objectstack#5505: that fix reached the two Shadcn-synced
primitives via the declared patch, but MobileDialogContent is a hand-written
custom/ wrapper with its own close button, and it is exactly what ModalForm
renders.

The aggregate footer had a locale-formatted number behind a hardcoded prefix.
All eleven aggregation kinds now read grid.summary.*, and the label/value join
is its own key so a pack owns its separator. Numbers untouched (#4589's
surface). The form dialog's sr-only description fallback joins the packs too.

SettingsView converts as one unit: its sibling SettingsHub already resolved
everything through t('console.settingsHub.*') while the view routed zero
framing copy through i18n. All of it now resolves through console.settingsView.*,
including the crypto-refusal strings #4579 deliberately deferred here.

The save-bar counter was an English plural rule executing in every locale. It is
now a real i18next plural family — base + _one + _other in all ten packs — with
the base key carrying the CLDR categories ru and ar do not enumerate.

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

vercel Bot commented Aug 13, 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 Aug 13, 2026 5:28pm

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

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

📦 Bundle Size Report

Package Size Gzipped
app-shell (index.js) 9.56KB 3.59KB
app-shell (runtime-config.js) 7.42KB 2.32KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 8.92KB 3.41KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 1.17KB 0.53KB
auth (AuthProvider.js) 25.13KB 5.40KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.13KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.64KB 2.21KB
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) 38.46KB 10.17KB
auth (createAuthenticatedFetch.js) 6.34KB 2.43KB
auth (index.js) 2.35KB 1.07KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.02KB 0.88KB
auth (useIsWorkspaceAdmin.js) 1.61KB 0.85KB
collaboration (CommentThread.js) 26.07KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.65KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
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) 489.28KB 108.46KB
core (index.js) 3.79KB 1.52KB
create-plugin (index.js) 10.08KB 3.26KB
data-objectstack (index.js) 163.56KB 44.83KB
fields (index.js) 230.37KB 57.17KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (currency.js) 1.22KB 0.64KB
i18n (i18n.js) 4.32KB 1.77KB
i18n (index.js) 3.35KB 1.38KB
i18n (pickLocalized.js) 3.69KB 1.73KB
i18n (provider.js) 23.12KB 7.62KB
i18n (useDisplayLocale.js) 2.84KB 1.45KB
i18n (useObjectLabel.js) 27.59KB 6.63KB
i18n (useSafeTranslation.js) 7.77KB 3.13KB
layout (index.js) 38.98KB 10.85KB
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) 6.96KB 1.98KB
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) 4.32KB 1.64KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 8.75KB 3.06KB
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.41KB 1.44KB
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.75KB 3.80KB
plugin-calendar (index.js) 46.86KB 12.91KB
plugin-charts (index.js) 62.10KB 17.67KB
plugin-chatbot (index.js) 181.21KB 43.14KB
plugin-dashboard (index.js) 121.04KB 31.57KB
plugin-designer (index.js) 212.58KB 42.83KB
plugin-detail (index.js) 239.93KB 60.01KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 114.72KB 27.70KB
plugin-gantt (index.js) 164.30KB 40.02KB
plugin-grid (index.js) 190.02KB 50.48KB
plugin-kanban (index.js) 52.74KB 14.53KB
plugin-list (index.js) 112.01KB 27.27KB
plugin-map (index.js) 18.16KB 5.81KB
plugin-markdown (index.js) 13.72KB 4.69KB
plugin-report (index.js) 41.38KB 11.09KB
plugin-timeline (index.js) 26.68KB 7.66KB
plugin-tree (index.js) 8.50KB 2.88KB
plugin-view (index.js) 84.09KB 20.56KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
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) 5.67KB 2.37KB
react (LazyPluginLoader.js) 3.77KB 1.33KB
react (SchemaRenderer.js) 27.64KB 9.44KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 1.26KB 0.67KB
react (schema-input.js) 1.45KB 0.83KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 4.09KB 1.74KB
sdui-parser (index.js) 4.47KB 2.03KB
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 (dashboard-filter-alias.js) 6.23KB 2.74KB
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) 1.87KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-retry.js) 4.32KB 2.02KB
types (index.js) 3.05KB 1.52KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 2.59KB 1.31KB
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.05KB 1.93KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 0.20KB 0.18KB
types (ui-action.js) 3.40KB 1.71KB
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

@yinlianghui
yinlianghui marked this pull request as ready for review August 13, 2026 17:47
@yinlianghui
yinlianghui added this pull request to the merge queue Aug 13, 2026
Merged via the queue into main with commit ae10a01 Aug 13, 2026
21 checks passed
@yinlianghui
yinlianghui deleted the claude/issue-4024-console-chrome-i18n branch August 13, 2026 17:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Console: list-view chrome (Grid / Gallery switcher, Avg: / Sum: aggregate footer) and the form dialog's a11y fallback text are hardcoded English

2 participants