Skip to content

sm new template gates updateI18n on locale change — admin screens render raw i18n keys after sign-in #321

Description

@antosubash

Summary

sm new's host template still gates updateI18n on a locale change, so a scaffolded app drops the locale catalog that arrives when the audience changes. Signing in swaps the anonymous catalog for one including admin-only modules at the same locale (en → en), the update is skipped, and every admin screen renders raw keys until a hard refresh.

This is the exact failure the framework's own host already fixes in host/client_app/i18n.ts — its comment describes this scenario verbatim, down to the dashboard.home.title example. The fix landed in #261 but was never carried across to the scaffold template, which was last touched in #276 for an unrelated change. Every app generated by sm new since then has the bug.

Affected file

framework/cli/simple_module_cli/templates/host/client_app/app.tsx

let activeLocale = initial?.locale ?? null;
router.on('success', (event) => {
  const block = (event.detail.page.props as { i18n?: I18nBlock }).i18n;
  if (!block) return;
  if (block.locale !== activeLocale && block.messages) {   // ← drops same-locale catalogs
    updateI18n({ locale: block.locale, messages: block.messages });
    activeLocale = block.locale;
  }
});

A non-null messages payload is the server's signal that the client needs it — the backend already sends null when the cached catalog is still good (see #248, which introduced the per-audience catalogs that make the locale gate wrong).

Reproduction

Against a scaffolded app running in production mode (reproduced on a real deployment, 2/2 in fresh browser contexts):

  1. Sign in as an admin at /users/login.
  2. Land on /dashboard/ via the post-login Inertia redirect.
  3. The dashboard renders five raw keys:
    • dashboard.home.title (<h1>)
    • dashboard.home.description (<p>)
    • dashboard.home.stats.total_users
    • dashboard.home.stats.active_users
    • dashboard.home.stats.modules
  4. Hard-reload the same URL — all five resolve correctly.

The split is a clean tell: on that page every string that renders correctly (↑ 7d, the <Head title>) is a hardcoded literal in dashboard/pages/Home.tsx, and every string routed through t() renders raw. Nothing is wrong with the catalogs themselves — dashboard/locales/en.json ships all five keys, and they resolve on a full load.

Suggested fix

Port the host's subscribeI18nToNavigation logic into the template — adopt whatever catalog the server sends, rather than gating on locale:

router.on('success', (event) => {
  const block = (event.detail.page.props as { i18n?: I18nBlock }).i18n;
  if (block?.messages) {
    updateI18n({ locale: block.locale, messages: block.messages });
  }
});

updateI18n already calls addResourceBundle(..., deep, overwrite), so applying it on every visit is additive and idempotent; activeLocale becomes dead and can go.

Worth considering whether the template should import the same i18n.ts helpers the host uses instead of restating the wiring, so the two cannot drift again — this is the second time the scaffold has shipped broken t() (cf. #83).

Impact

Any app scaffolded by sm new after #261. Cosmetic but highly visible: it hits the first screen an admin sees after signing in, and looks like a broken deployment.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions