Skip to content

fix(i18n): the es pack says Listo at all four Done sites (#3880) - #5971

Merged
yinlianghui merged 1 commit into
mainfrom
claude/issue-3880-shared-en-translation-typos
Aug 24, 2026
Merged

fix(i18n): the es pack says Listo at all four Done sites (#3880)#5971
yinlianghui merged 1 commit into
mainfrom
claude/issue-3880-shared-en-translation-typos

Conversation

@yinlianghui

Copy link
Copy Markdown
Collaborator

Part of #3880

Part of, not a closing keyword, on purpose — see "What stays on the card" below. Two of
that card's three halves are not delivered here: one is documentation by ruling, and one
had its premise fall out from under it.

The change

One Spanish value, plus the pin that recorded it.

grid.bulk.done read Hecho while the other three sites that say Done in en
common.done, view.done, form.fullscreen.done — all read Listo. It moves to Listo.

Why this is a typo and not a deliberate contextual split

That distinction is the whole risk on this card: 58% of shared English strings in this
repo are translated differently per key on purpose
(ru Delete is the noun Удаление
for an activity-feed event type and the verb Удалить elsewhere; ja Clear is 選択解除
for grid.bulkClear). So "make identical English identical" is the wrong instinct by
default, and each of these has to be argued rather than swept. Three independent checks,
each of which could have stopped the change:

  1. The en values are byte-identical. Measured by flattening all ten packs to dotted
    key paths and grouping by exact value, not by grepping for a spelling: exactly four keys
    hold the en value Done.

  2. All four call sites are the same control — a dialog-footer button whose click
    finishes or dismisses the surface. Read at the call site, not inferred from the key name:

    keyfilehandler
    grid.bulk.donepackages/plugin-grid/src/components/BulkActionDialog.tsx:557onClose(result)
    view.donepackages/plugin-view/src/ManageViewsDialog.tsx:583onOpenChange(false)
    form.fullscreen.donepackages/components/src/custom/fullscreen-editor.tsx:432commitFullscreen
    common.donepackages/app-shell/src/console/organizations/manage/InviteMemberDialog.tsx:216invitation-created footer
  3. The nine other packs render all four identically — de Fertig, fr Terminé, pt
    Concluído, ru Готово, ja 完了, ko 완료, zh 完成, ar تم. Nine independent
    translation passes each found no context worth splitting on. This is the check that
    separates this group from the legitimate divergences above, where the split is visible
    in the data.

One hypothesis that would have justified Hecho was tested and refuted: the sibling
grid.bulk.undonePrefix is Deshecho: , which looks like a deliberate Hecho/Deshecho
pairing. It is not one — undonePrefix is a result-line status rendered at
BulkActionDialog.tsx:461 when undoneAt !== null, while done is a button; the
button done would pair with grid.bulk.undo, and that is the verb Deshacer.

Listo is the 3:1 majority and is already the value #3546 slice seven chose for
common.done.

The pin

packages/i18n/src/__tests__/residue-namespaces-3546.test.tsx named this value as a
recorded example of deliberate divergence — the evidence for "picking a neighbour is a
choice that has to be made rather than derived". That note is now false for Done, so it is
rewritten rather than merely re-valued: it keeps the history and the Pending/zh row (which
is still a genuine deliberate split), and pins the four Done values together so the
outlier cannot regrow. The REUSED list itself is unchanged — its ['common.done', 'view.done'] row was already Listo on both sides.

Reverse-verified: with the fix reverted on disk (mutation confirmed by anchored counts —
"Hecho" 1, "Listo" 3 where it had been 4 — not by an editor exit code), the pin fails
with AssertionError: es grid.bulk.done (objectui#3880 converged these on one value): expected 'Hecho' to be 'Listo', 1 failed / 42 passed. The restore leg is proven clean:
git diff vs HEAD is empty and "Hecho" is back to 0 occurrences in es.ts. The
mutation script carried a trap … EXIT INT TERM restore.

What stays on the card

  • The 281/164 denominator is documentation, by ruling, and is not implemented here. 281
    en strings are shared by ≥2 keys; 164 have a legitimately divergent translation in at
    least one pack. A "same en implies same translation" gate would emit 164 false reds, so
    no lint rule, script or test asserting that invariant is added by this PR.
  • The card's second typo group — de Loading... — is not delivered, because its premise
    no longer holds.
    The card cited common.loadingWird geladen... against
    detail.loadingLaden..., both ASCII. There is no ASCII ... left in any of the ten
    packs
    : [finding] en 包 32 个值用 ASCII "..." 结尾、113 个用 U+2026 "…" —— 同一屏上省略号两种字形,且九包各自跟着抄 #3878 landed as PR fix(i18n): one ellipsis glyph across the ten packs, es usted single-form, pt de-contraction (#3878 #3875 #3877) #4378 and converged every one on U+2026, and both of these
    keys are named in that pass's own CONVERGED_KEYS census in
    packages/i18n/src/__tests__/ellipsis-glyph-3878.test.ts. That merged the ASCII and
    U+2026 groups, so what the card saw as a 2-key pair is now one 10-key group carrying
    four German spellings
    : Wird geladen… ×6, Laden… ×2 (detail.loadingand
    report.loading), Lade… ×1 (auth.device.loading), Lädt… ×1
    (approvalsInbox.loadingMore). Changing only the key the card named would leave
    report.loading — the identical spelling at an equally same-context site — untouched, so
    the majority-vote argument that settles Done cleanly does not transfer. That is a
    different decision than the one adjudicated on 2026-08-09 and it is filed separately
    rather than half-made here.

Verification

All at 88707ef9 (the pushed commit), each exit code captured before any pipe:

checkverdict
pnpm exec vitest run packages/i18n/Test Files 53 passed (53) · Tests 893 passed (893)
pnpm --filter @object-ui/i18n type-checkexit 0 (tsc --noEmit && tsc -p tsconfig.test.json)
check:i18n-drift0 en value(s) changed … No en value changed in this range.
check:i18n-keysEvery in-scope call-site key resolves against the en pack (2934 keys)
check:i18n-dead-keysexit 0 (report, not a gate)
check:control-bytesOK (scanned 4951 tracked text file(s))
check-changeset-no-major.mjsNo changeset declares a major bump.

check:i18n-drift reporting zeroen changes is the intended reading, not an empty
result: only an es value moved, so no pack is asked to follow and no waiver is needed.

Lint is a declared narrowing, not a full run.npx eslint . --no-inline-config was run
in packages/i18n — the only package with code changes — giving exit 0, 0 errors, 34
pre-existing warnings, over 78 files that eslint's own config selected (count read from
--format json, not enumerated by hand); both changed files are in that set at 0/0. The
narrowing excludes nothing: type-aware linting is not configured (no projectService
and no parserOptions.project in eslint.config.js — verified with a control probe proving
the grep is well-formed), so a file's verdict depends only on that file plus the shared
config, and this diff changes neither the config nor any file outside packages/i18n. CI
runs the full farm regardless.


Generated by Claude Code

`grid.bulk.done` read `Hecho` while `common.done`, `view.done` and
`form.fullscreen.done` read `Listo` — the same English word rendering two ways
in Spanish. All four keys hold the byte-identical `en` value `Done` and all four
call sites are the same dialog-footer button that finishes or dismisses the
surface, the nine other packs render all four identically, and the neighbouring
`Deshecho: ` is `undonePrefix` (a result-line status) rather than a pair for a
button — so this is a typo, not a contextual split. It moves to the 3:1 majority
`Listo`.
`residue-namespaces-3546.test.tsx` pinned the outlier as a recorded example of
deliberate divergence; it now pins the four as one value, keeping the history
and the `Pending`/zh row that is still a genuine split.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CSoz9uGhaaSgiq3hshtN7L
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 52 chunks)3232.4 KB3990.2 KB
Main entry chunk (gzip)153.6 KB350 KB
Entry fileindex-BbiH2u5J.js
StatusPASS

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)10.13KB3.77KB
app-shell (runtime-config.js)13.57KB4.78KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)504.75KB114.32KB
core (index.js)4.92KB1.97KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)165.30KB45.79KB
fields (index.js)238.40KB59.89KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)23.13KB7.63KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)33.40KB8.71KB
i18n (useSafeTranslation.js)7.77KB3.13KB
layout (index.js)38.95KB10.97KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)9.53KB3.38KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)4.64KB1.50KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)1.93KB0.88KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)46.62KB12.83KB
plugin-charts (index.js)64.66KB18.32KB
plugin-chatbot (index.js)188.21KB44.67KB
plugin-dashboard (index.js)133.35KB34.44KB
plugin-designer (index.js)212.30KB42.80KB
plugin-detail (index.js)244.00KB61.86KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)125.63KB30.64KB
plugin-gantt (index.js)164.15KB39.88KB
plugin-grid (index.js)200.79KB54.26KB
plugin-kanban (index.js)52.93KB14.60KB
plugin-list (index.js)111.86KB27.22KB
plugin-map (index.js)20.06KB6.62KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.49KB11.93KB
plugin-timeline (index.js)26.49KB7.59KB
plugin-tree (index.js)8.50KB2.88KB
plugin-view (index.js)84.57KB20.74KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)3.77KB1.33KB
react (SchemaRenderer.js)52.40KB17.45KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)1.35KB0.70KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)12.13KB3.65KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)7.54KB2.63KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)0.20KB0.18KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)0.20KB0.18KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.87KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (index.js)3.88KB1.85KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)3.40KB1.71KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

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

Sign up for freeto 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

@yinlianghui@claude