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):
- Sign in as an admin at
/users/login.
- Land on
/dashboard/ via the post-login Inertia redirect.
- 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
- 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.
Summary
sm new's host template still gatesupdateI18non 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 thedashboard.home.titleexample. 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 bysm newsince then has the bug.Affected file
framework/cli/simple_module_cli/templates/host/client_app/app.tsxA non-null
messagespayload is the server's signal that the client needs it — the backend already sendsnullwhen 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):
/users/login./dashboard/via the post-login Inertia redirect.dashboard.home.title(<h1>)dashboard.home.description(<p>)dashboard.home.stats.total_usersdashboard.home.stats.active_usersdashboard.home.stats.modulesThe split is a clean tell: on that page every string that renders correctly (
↑ 7d, the<Head title>) is a hardcoded literal indashboard/pages/Home.tsx, and every string routed throught()renders raw. Nothing is wrong with the catalogs themselves —dashboard/locales/en.jsonships all five keys, and they resolve on a full load.Suggested fix
Port the host's
subscribeI18nToNavigationlogic into the template — adopt whatever catalog the server sends, rather than gating on locale:updateI18nalready callsaddResourceBundle(..., deep, overwrite), so applying it on every visit is additive and idempotent;activeLocalebecomes dead and can go.Worth considering whether the template should import the same
i18n.tshelpers the host uses instead of restating the wiring, so the two cannot drift again — this is the second time the scaffold has shipped brokent()(cf. #83).Impact
Any app scaffolded by
sm newafter #261. Cosmetic but highly visible: it hits the first screen an admin sees after signing in, and looks like a broken deployment.