From 13a19c127aacc5893b6f18dc5e884bb9446537da Mon Sep 17 00:00:00 2001 From: David Montero Date: Tue, 18 Aug 2026 18:35:01 +0200 Subject: [PATCH] prerender: initialise i18n for SSR; example pages mounted under their route MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that SSR bodies are actually injected, two more prerender defects showed: every t() rendered as its key (i18next was never initialised in entry-server — the English bundle is imported now, plus the overlay's "pro" namespace on pro builds, with an OSS stub), and every example detail page rendered "404 Example not found" because ExampleDetailPage was mounted without a Route so useParams() was empty. Verified locally: /examples/blink-led SSRs breadcrumb, h1, description and JSON-LD; the gallery and editor summary render translated copy. --- frontend/src/__pro_stub__/i18n/register.ts | 5 +++++ frontend/src/entry-server.tsx | 21 +++++++++++++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 frontend/src/__pro_stub__/i18n/register.ts diff --git a/frontend/src/__pro_stub__/i18n/register.ts b/frontend/src/__pro_stub__/i18n/register.ts new file mode 100644 index 00000000..21806329 --- /dev/null +++ b/frontend/src/__pro_stub__/i18n/register.ts @@ -0,0 +1,5 @@ +/** + * OSS stub for the overlay's i18n registration (see entry-server.tsx). + * The real module adds the "pro" namespace; here there is nothing to add. + */ +export function registerProI18n(): void {} diff --git a/frontend/src/entry-server.tsx b/frontend/src/entry-server.tsx index 83fee247..e29b778d 100644 --- a/frontend/src/entry-server.tsx +++ b/frontend/src/entry-server.tsx @@ -8,7 +8,12 @@ import React from 'react'; import { renderToString } from 'react-dom/server'; -import { MemoryRouter } from 'react-router-dom'; +// Initialise i18next with the bundled English resources before any page +// renders: without this every t() call in the SSR output came back as its +// KEY ("landing.hero.titleLine1"), which is what the prerendered bodies +// shipped once they were actually injected. +import './i18n'; +import { MemoryRouter, Routes, Route } from 'react-router-dom'; import { SEO_ROUTES } from './seoRoutes'; // ── SEO page components ───────────────────────────────────────────────────── @@ -56,6 +61,14 @@ export async function loadRouteComponents(): Promise> { if (import.meta.env.VITE_PRO_BUILD) { const m = await import('@pro/pages/marketing'); routeComponents = { ...OSS_ROUTE_COMPONENTS, ...m.MARKETING_ROUTE_COMPONENTS }; + // The overlay's own namespace ("pro": landing sections, pricing...), + // else its t() calls render as keys too. + try { + const reg = await import('@pro/i18n/register'); + reg.registerProI18n?.(); + } catch (err) { + console.warn(' ⚠ pro i18n not registered for SSR:', (err as Error).message); + } } return routeComponents; } @@ -103,9 +116,13 @@ export function getPrerenderedExampleRoutes() { */ export function renderExample(exampleId: string): string { try { + // Mounted under its real route so useParams() sees exampleId; rendered + // bare it had no params and every example page SSR'd as "not found". return renderToString( - + + } /> + , ); } catch (err) {