From 9f2a3e25fe18c33e3d389e0c3739a019639de36c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Tue, 7 Jul 2026 13:12:33 +0200 Subject: [PATCH] perf(i18n): generate the catalog boot scripts as static assets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The dynamic /i18n/[locale] route handler answered with Next's default Cache-Control (private, max-age=0, must-revalidate) and no validator — its immutable-cache header was overridden, so a non-PWA browser would re-download the full catalog on every hard load. Static public/ files keep an ETag (repeat loads revalidate to a 304) and the service worker's cache-first rule covers the offline relaunch; the ?v= build key the layout appends busts both per deploy. scripts/generate-i18n-boot.mjs writes public/i18n/.js for all six locales as a prebuild/predev step (the sw-version.js pattern); output is gitignored. The route handler is removed. Measured on a production build, authenticated DE session: dashboard document 522 KB raw / 139 KB gz -> 170 KB raw / 24.6 KB gz; the DE catalog moves into a one-time 119 KB gz cached asset. H2 --- .gitignore | 1 + e2e/utils/mock-mood-insights.ts | 5 +- package.json | 3 +- scripts/check-bundle-budget.mjs | 4 +- scripts/generate-i18n-boot.mjs | 52 +++++++++++++++++++ src/app/i18n/[locale]/route.ts | 51 ------------------ src/app/layout.tsx | 11 ++-- src/components/labs/lab-biomarker-detail.tsx | 12 +++-- .../__tests__/use-auth-offline-gate.test.ts | 13 +++-- src/lib/i18n/load-locale.ts | 2 +- 10 files changed, 86 insertions(+), 68 deletions(-) create mode 100644 scripts/generate-i18n-boot.mjs delete mode 100644 src/app/i18n/[locale]/route.ts diff --git a/.gitignore b/.gitignore index 132725d27..fe011fcaa 100644 --- a/.gitignore +++ b/.gitignore @@ -54,6 +54,7 @@ next-env.d.ts # scripts/generate-sw-version.mjs; loaded by public/sw.js to key # the cache-version baseline. /public/sw-version.js +/public/i18n/ # v1.4.27 B3 — MaxMind GeoLite2 databases are fetched at build time # via `scripts/fetch-geolite2.sh`. Too large for git; license key diff --git a/e2e/utils/mock-mood-insights.ts b/e2e/utils/mock-mood-insights.ts index e8dc21e8e..36fe5c144 100644 --- a/e2e/utils/mock-mood-insights.ts +++ b/e2e/utils/mock-mood-insights.ts @@ -113,7 +113,10 @@ export async function mockMoodInsights(page: Page): Promise { await route.fulfill({ status: 200, contentType: "application/json", - body: JSON.stringify({ data: CORRELATION_DISCOVERY_PAYLOAD, error: null }), + body: JSON.stringify({ + data: CORRELATION_DISCOVERY_PAYLOAD, + error: null, + }), }); }); } diff --git a/package.json b/package.json index 367972474..f20ef9ccc 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,8 @@ "scripts": { "prepare": "lefthook install || echo 'lefthook install skipped (no git repo or binary unavailable)'", "dev": "next dev", - "prebuild": "node scripts/generate-sw-version.mjs", + "predev": "node scripts/generate-i18n-boot.mjs", + "prebuild": "node scripts/generate-sw-version.mjs && node scripts/generate-i18n-boot.mjs", "build": "next build", "analyze": "ANALYZE=1 next build", "bundle-report": "node scripts/check-bundle-budget.mjs", diff --git a/scripts/check-bundle-budget.mjs b/scripts/check-bundle-budget.mjs index 713dfb116..d38c17174 100644 --- a/scripts/check-bundle-budget.mjs +++ b/scripts/check-bundle-budget.mjs @@ -150,7 +150,9 @@ const budget = existsSync(BUDGET_PATH) console.log(`shared baseline (rootMainFiles): ${fmt(rootGz)} gz`); console.log(`all client chunks: ${fmt(totalGz)} gz`); -console.log(`largest chunk: ${fmt(largest.gz)} gz ${largest.file}`); +console.log( + `largest chunk: ${fmt(largest.gz)} gz ${largest.file}`, +); console.log(`recharts-fingerprint chunks: ${rechartsChunks}`); console.log(""); diff --git a/scripts/generate-i18n-boot.mjs b/scripts/generate-i18n-boot.mjs new file mode 100644 index 000000000..74bd331c0 --- /dev/null +++ b/scripts/generate-i18n-boot.mjs @@ -0,0 +1,52 @@ +#!/usr/bin/env node +/** + * Generate the locale-catalog boot scripts (`public/i18n/.js`). + * + * The root layout emits `