Skip to content

i18n: InviteAcceptPage and buildInviteEmail() bypass the locale system (still English when site locale is non-English) #915

Description

@tmyuu

Summary

With emdash:locale = "ja" set on a site that has otherwise switched to Japanese, the invite acceptance page and invite email template remain in English. Both surfaces bypass the lingui i18n machinery used elsewhere in the admin and auth packages.

Repro

  1. Set emdash:locale = "ja" in the options table.
  2. Confirm /_emdash/admin/... (settings, content list, etc.) renders in Japanese — locale is being picked up correctly.
  3. Invite a new user with Send invite. The recipient gets an English email:
    • Subject: You've been invited to <siteName>
    • Body: You've been invited to join <siteName>. Click this link to create your account: <url>
  4. Open the invite link → /admin/invite/accept?token=.... The page renders the following English strings:
    • You've been invited!
    • You'll be joining as <ROLE>
    • Email (label)
    • Your name (optional) (label)
    • Create your passkey (heading)
    • Passkeys are a secure, passwordless way to sign in using your device's biometrics, PIN, or security key.
    • Create Account (button)

Why

Two distinct hard-coded surfaces:

1. @emdash-cms/adminInviteAcceptPage.tsx

Looking at the bundled dist/index.js (v0.9.0), the InviteAcceptPage RegisterStep component places the strings directly inside JSX children rather than wrapping them with the <Trans> macro / lingui catalog. The strings have no message id in locales/ja/messages.mjs (verified by greping for joining, invited, Your name, etc. — no matches for these specific phrases, while sibling passkey strings are translated).

2. @emdash-cms/authbuildInviteEmail() in src/invite.ts

The function returns a hard-coded English subject + text + HTML body. It receives siteName only — no locale parameter — and therefore no way to render localized copy without a fork.

Additionally, the email pipeline's EmailPipeline.sendInner() skips email:beforeSend for source === "system" to protect auth tokens, so plugins cannot rewrite invite/magic-link bodies via hooks. This makes buildInviteEmail the only place where localization can happen.

Expected

  • InviteAcceptPage strings flow through the same <Trans> / lingui catalog that the rest of the admin uses, with corresponding entries in locales/<lang>/messages.mjs.
  • buildInviteEmail() (and any other system-email builders — magic-link, password recovery) accepts a locale argument or reads the active site locale and selects a template per language.

Workaround

None inside an EmDash project: plugin hooks can't rewrite system emails, and the admin SPA strings are baked into the published bundle. Sites either tolerate the mixed-language UX or fall back to the "copy invite link and share manually" flow.

Environment

  • emdash@0.9.0
  • @emdash-cms/admin@0.9.0
  • @emdash-cms/auth@0.9.0
  • @emdash-cms/cloudflare@0.9.0
  • Astro 6 / Cloudflare Workers runtime

Metadata

Metadata

Assignees

No one assigned

    Labels

    roadmap/1.0Issues included in the public EmDash 1.0 roadmap.roadmap/data-safetyRoadmap track: data safety, schema, localization correctness.

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions