Skip to content

fix(admin): translate long-tail i18n leaks in admin UI#940

Merged
ascorbic merged 2 commits into
mainfrom
fix/admin-i18n-long-tail
May 7, 2026
Merged

fix(admin): translate long-tail i18n leaks in admin UI#940
ascorbic merged 2 commits into
mainfrom
fix/admin-i18n-long-tail

Conversation

@ascorbic
Copy link
Copy Markdown
Collaborator

@ascorbic ascorbic commented May 7, 2026

What does this PR do?

Stacked on top of #937. When #937 merges, GitHub will rebase this onto `main` automatically. Set base to `fix/admin-i18n-leaks-v2`.

Wraps the remaining ~80 user-facing English strings in the admin UI (the long tail after #937's top-10 cleanup). After this PR, all currently-known i18n leaks in the admin are fixed.

Files changed (41 total)

Auth flows:

  • `SetupWizard.tsx` — fallback messages on three setup mutations; `Or continue with` divider text. Inlined three module-scope helper functions into mutation closures so they can capture `t`.
  • `SignupPage.tsx`, `LoginPage.tsx` — verified clean, only one verification-email fallback wrapped.
  • `auth/PasskeyRegistration.tsx` — three error fallbacks.
  • `auth/PasskeyLogin.tsx` — three error fallbacks.
  • `DeviceAuthorizePage.tsx` — one Authorization-failed fallback.
  • `InviteAcceptPage.tsx` (already done in fix(admin): translate strings in top-10 admin UI files (i18n leaks) #937).

Settings/marketplace:

  • `Sections.tsx` — converted module-scope `sourceLabels` to `Record<key, MessageDescriptor>` resolved at render.
  • `MarketplaceBrowse.tsx` — same pattern for `SORT_LABELS`.
  • `Redirects.tsx` — three placeholders.
  • `MediaPickerModal.tsx` — image-load error and two attributes; `probeImageDimensions` now takes a translated message as a parameter.
  • `MediaLibrary.tsx` — "Library" tab label (twice); verified `plural()` calls already use the macro form.
  • `WordPressImport.tsx` — six error-fallback messages and four `StepIndicator` labels.
  • `settings/AllowedDomainsSettings.tsx` — verified clean (only illustrative `example.com` placeholder remained).

Plugin sandbox:

  • `SandboxedPluginPage.tsx` — error states.
  • `SandboxedPluginWidget.tsx` — error states.
  • `BlockKitFieldWidget.tsx` — `Select...` and unsupported-element fallback.
  • `PluginFieldErrorBoundary.tsx` — NOT touched. Class component; needs a separate refactor (accept message props or wrap in a hook bridge). Tracked as a follow-up.

Editors / content management:

  • `editor/BlockMenu.tsx` — Back / Turn into / Duplicate / Delete actions; drag-to-reorder aria-label.
  • `editor/DragHandleWrapper.tsx` — `useLingui` added; block-actions aria-label.
  • `ContentEditor.tsx` — Autosave-status aria-label.
  • `MenuList.tsx` — item-count rendered with `` + `plural()`. Fixed a grammar bug: `1 items` → `1 item` (test regex updated).
  • `SectionEditor.tsx` — one placeholder.
  • `TaxonomyManager.tsx` — two News/Genres placeholders.
  • `TaxonomySidebar.tsx` — three module-level fallback messages (uses `i18n._(msg``)` pattern), plus inline button text.
  • `FieldEditor.tsx` — option-list placeholder.
  • `WelcomeModal.tsx` — refactored `dismissWelcome` to take a message parameter, called with `t`Failed to dismiss welcome``.

Module-level lib (`lib/api/*.ts`):

40 `throwResponseError(...)` and `parseApiResponse(...)` fallback strings across 16 files now use `i18n._(msg``)` (the standard Lingui pattern for module-level code that can't call `useLingui()`):

`api-tokens.ts`, `bylines.ts`, `client.ts`, `comments.ts`, `content.ts`, `import.ts`, `marketplace.ts`, `media.ts`, `menus.ts`, `plugins.ts`, `redirects.ts`, `schema.ts`, `sections.ts`, `taxonomies.ts`, `users.ts`, `widgets.ts`.

Test fix:

  • `tests/components/MenuList.test.tsx` — regex updated to match the now-correct singular form (`/footer.*1 item(?!s)/`). Test was previously passing on a buggy plural string.

Approach

Sub-agents (Haiku-based `simple-tasks`) handled most files in parallel — well-specified, mechanical work. The audit was thorough enough that each file's fix was a clear list of lines to wrap.

Closes #

Type of change

  • Bug fix
  • Feature (requires maintainer-approved Discussion)
  • Refactor (no behavior change)
  • Translation
  • Documentation
  • Performance improvement
  • Tests
  • Chore (dependencies, CI, tooling)

Checklist

  • I have read CONTRIBUTING.md
  • `pnpm typecheck` passes
  • `pnpm lint` passes
  • `pnpm test` passes (admin: 858/858)
  • `pnpm format` has been run
  • I have added/updated tests for my changes — `MenuList.test.tsx` regex updated to match the corrected plural form (1 item, not 1 items)
  • User-visible strings in the admin UI are wrapped for translation. Do not include `messages.po` changes except in translation PRs — a workflow extracts catalogs on merge to `main`.
  • I have added a changeset
  • New features link to an approved Discussion: https://github.com/emdash-cms/emdash/discussions/...

AI-generated code disclosure

  • This PR includes AI-generated code — model/tool: Claude Opus 4.7 (orchestration) + Claude Haiku 4.5 (`simple-tasks` subagents) via OpenCode

Screenshots / test output

No visual change in default English locale. Run with `EMDASH_PSEUDO_LOCALE=1` to verify the strings now flow through translation.

Follow-up

  • `PluginFieldErrorBoundary.tsx` (class component) needs a separate refactor.
  • Will open a follow-up issue if not handled in a subsequent stacked PR.

Stack: `main` → #937this PR → (next: raw HTML to Kumo) → (next: aria-labels) → (next: RTL safety) → (next: semantic tokens) → (last: ESLint enforcement)

@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented May 7, 2026

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
docs 7635c96 May 07 2026, 02:53 PM

@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented May 7, 2026

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
emdash-i18n 7635c96 May 07 2026, 02:52 PM

@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented May 7, 2026

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
emdash-playground 7635c96 May 07 2026, 02:53 PM

@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented May 7, 2026

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
emdash-demo-cache 7635c96 May 07 2026, 02:53 PM

@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented May 7, 2026

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
emdash-perf-coordinator 7635c96 May 07 2026, 02:52 PM

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 7, 2026

Scope check

This PR touches 41 files. PRs with a broad scope are harder to review. Please confirm the scope hasn't drifted beyond the intended change.

If this scope is intentional, no action needed. A maintainer will review it. If not, please consider splitting this into smaller PRs.

See CONTRIBUTING.md for contribution guidelines.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 7, 2026

Overlapping PRs

This PR modifies files that are also changed by other open PRs:

This may cause merge conflicts or duplicated work. A maintainer will coordinate.

Base automatically changed from fix/admin-i18n-leaks-v2 to main May 7, 2026 14:37
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 7, 2026

🦋 Changeset detected

Latest commit: 7635c96

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 13 packages
Name Type
@emdash-cms/admin Patch
emdash Patch
@emdash-cms/cloudflare Patch
@emdash-cms/fixture-perf-site Patch
@emdash-cms/perf-demo-site Patch
@emdash-cms/cache-demo-site Patch
@emdash-cms/auth Patch
@emdash-cms/blocks Patch
@emdash-cms/gutenberg-to-portable-text Patch
@emdash-cms/x402 Patch
create-emdash Patch
@emdash-cms/auth-atproto Patch
@emdash-cms/plugin-embeds Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@ascorbic ascorbic requested a review from Copilot May 7, 2026 14:38
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 7, 2026

Open in StackBlitz

@emdash-cms/admin

npm i https://pkg.pr.new/@emdash-cms/admin@940

@emdash-cms/auth

npm i https://pkg.pr.new/@emdash-cms/auth@940

@emdash-cms/blocks

npm i https://pkg.pr.new/@emdash-cms/blocks@940

@emdash-cms/cloudflare

npm i https://pkg.pr.new/@emdash-cms/cloudflare@940

emdash

npm i https://pkg.pr.new/emdash@940

create-emdash

npm i https://pkg.pr.new/create-emdash@940

@emdash-cms/gutenberg-to-portable-text

npm i https://pkg.pr.new/@emdash-cms/gutenberg-to-portable-text@940

@emdash-cms/x402

npm i https://pkg.pr.new/@emdash-cms/x402@940

@emdash-cms/plugin-ai-moderation

npm i https://pkg.pr.new/@emdash-cms/plugin-ai-moderation@940

@emdash-cms/plugin-atproto

npm i https://pkg.pr.new/@emdash-cms/plugin-atproto@940

@emdash-cms/plugin-audit-log

npm i https://pkg.pr.new/@emdash-cms/plugin-audit-log@940

@emdash-cms/plugin-color

npm i https://pkg.pr.new/@emdash-cms/plugin-color@940

@emdash-cms/plugin-embeds

npm i https://pkg.pr.new/@emdash-cms/plugin-embeds@940

@emdash-cms/plugin-forms

npm i https://pkg.pr.new/@emdash-cms/plugin-forms@940

@emdash-cms/plugin-webhook-notifier

npm i https://pkg.pr.new/@emdash-cms/plugin-webhook-notifier@940

commit: 7635c96

Wrap remaining user-facing English strings across settings panels, marketplace,
sandboxed-plugin host, auth flows (Signup, Login, Passkey, DeviceAuthorize,
SetupWizard), taxonomy/menu management, content editor remnants, and lib/api
module-level functions.

For lib/api fallback messages, uses the i18n._(msg``) pattern since module-level
code can't call useLingui(). MenuList item count uses plural() which fixed an
existing grammar bug (1 items -> 1 item; test regex updated).
The previous commit dropped the <span> wrappers around Back/Turn into/Duplicate/Delete
labels when wrapping in t``. The wrappers are needed for consistent flex layout
with the icon and to match the transforms-list pattern.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR continues the admin i18n cleanup (stacked on #937) by wrapping remaining user-facing English strings across the admin UI and admin API client fallbacks so they flow through Lingui translation instead of leaking raw English in non-English locales.

Changes:

  • Wrapped additional admin UI strings in Lingui macros (t, msg, <Trans>, plural) across settings, marketplace, sandboxed-plugin host, editors, and auth flows.
  • Updated module-level admin API client fallback messages to use the i18n._(msg\`)` pattern where hooks aren’t available.
  • Fixed a pluralization grammar bug in MenuList (and updated the corresponding test regex).

Reviewed changes

Copilot reviewed 41 out of 41 changed files in this pull request and generated no comments.

Show a summary per file
File Description
packages/admin/tests/components/MenuList.test.tsx Updates regex assertion to match corrected singular “1 item”.
packages/admin/src/lib/api/api-tokens.ts Localizes throwResponseError fallback string.
packages/admin/src/lib/api/bylines.ts Localizes throwResponseError fallback string.
packages/admin/src/lib/api/client.ts Localizes manifest fetch fallback string.
packages/admin/src/lib/api/comments.ts Localizes throwResponseError fallback string.
packages/admin/src/lib/api/content.ts Localizes multiple content/revision fallback strings.
packages/admin/src/lib/api/import.ts Localizes import-media fallback string.
packages/admin/src/lib/api/marketplace.ts Localizes marketplace install/update/uninstall fallback strings.
packages/admin/src/lib/api/media.ts Localizes upload/delete/provider-delete fallback strings.
packages/admin/src/lib/api/menus.ts Localizes delete menu/menu item fallback strings.
packages/admin/src/lib/api/plugins.ts Localizes fetch plugin fallback strings.
packages/admin/src/lib/api/redirects.ts Localizes delete redirect fallback string.
packages/admin/src/lib/api/schema.ts Localizes collection/field operations fallback strings.
packages/admin/src/lib/api/sections.ts Localizes delete section fallback string.
packages/admin/src/lib/api/taxonomies.ts Localizes delete term fallback string.
packages/admin/src/lib/api/users.ts Localizes multiple user/passkey/allowed-domain fallback strings.
packages/admin/src/lib/api/widgets.ts Localizes widget-area/widget delete/reorder fallback strings.
packages/admin/src/components/auth/PasskeyLogin.tsx Localizes passkey login error fallbacks.
packages/admin/src/components/auth/PasskeyRegistration.tsx Localizes passkey registration error fallbacks.
packages/admin/src/components/BlockKitFieldWidget.tsx Localizes select placeholder and unsupported-element fallback.
packages/admin/src/components/ContentEditor.tsx Localizes autosave status aria-label.
packages/admin/src/components/DeviceAuthorizePage.tsx Localizes authorization failure fallback.
packages/admin/src/components/FieldEditor.tsx Localizes options placeholder text.
packages/admin/src/components/MarketplaceBrowse.tsx Converts sort labels to MessageDescriptors resolved at render.
packages/admin/src/components/MediaLibrary.tsx Localizes “Library” tab label and updates memo deps.
packages/admin/src/components/MediaPickerModal.tsx Localizes image-load errors/labels and threads translated error into probe helper.
packages/admin/src/components/MenuList.tsx Fixes pluralization via <Trans> + plural() for menu item count.
packages/admin/src/components/Redirects.tsx Localizes form placeholder examples.
packages/admin/src/components/SandboxedPluginPage.tsx Localizes sandbox error states for plugin pages.
packages/admin/src/components/SandboxedPluginWidget.tsx Localizes sandbox error/empty states for widgets.
packages/admin/src/components/Sections.tsx Converts source labels to MessageDescriptors resolved with t() at render.
packages/admin/src/components/SectionEditor.tsx Localizes keywords placeholder.
packages/admin/src/components/SetupWizard.tsx Localizes setup flow error fallbacks and divider text; inlines helpers to capture t.
packages/admin/src/components/SignupPage.tsx Localizes verification-email error fallback.
packages/admin/src/components/TaxonomyManager.tsx Localizes example placeholders in taxonomy dialogs.
packages/admin/src/components/TaxonomySidebar.tsx Localizes module-level taxonomy API fallbacks + inline UI strings.
packages/admin/src/components/WelcomeModal.tsx Passes translated fallback message into module-level dismiss helper.
packages/admin/src/components/WordPressImport.tsx Localizes mutation error fallbacks and step labels.
packages/admin/src/components/editor/BlockMenu.tsx Localizes block menu actions and drag handle aria-label.
packages/admin/src/components/editor/DragHandleWrapper.tsx Localizes block-actions aria-label.
.changeset/public-women-behave.md Adds changeset entry for the admin patch release.
Comments suppressed due to low confidence (2)

packages/admin/src/components/SandboxedPluginWidget.tsx:50

  • sendInteraction's useCallback closure reads t, but the dependency array only includes pluginId. This can capture a stale translation function when the locale changes (and will typically trip the hooks exhaustive-deps lint). Include t in the dependency list (or derive the translated strings outside the callback) so error messages stay in sync with the active locale.
				setError(t`Failed to load widget`);
			}
		},
		[pluginId],
	);

packages/admin/src/components/SandboxedPluginPage.tsx:58

  • sendInteraction uses t inside the useCallback, but t is missing from the dependency array. If the active locale changes, this callback can keep using the old t and produce untranslated/stale strings. Add t to the deps (or avoid closing over it) to keep error messages consistent.
				setError(err instanceof Error ? err.message : t`Failed to communicate with plugin`);
			}
		},
		[pluginId],
	);

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@ascorbic ascorbic merged commit 0b8a319 into main May 7, 2026
36 checks passed
@ascorbic ascorbic deleted the fix/admin-i18n-long-tail branch May 7, 2026 15:39
@emdashbot emdashbot Bot mentioned this pull request May 7, 2026
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.

2 participants