Files
HealthLog/scripts/generate-i18n-boot.mjs
T
Rob de Graaf c6f61fad33 feat(i18n): add Dutch as an eighth end-to-end locale
Adds messages/nl.json (AI-initial, informal je/jij register, full key
parity with en.json) and registers nl everywhere the locale list lives:
config, loaders, Intl/date-fns mapping, zod enums, OpenAPI spec, scripts,
the managed-profile picker and the notification locale guards.

Dutch follows the Korean pattern for the AI surfaces (English body plus a
Dutch reply-language footer, no safety-contract matrix) and gets its own
banks in the outbound safety screen (dose, risk, causal, continuation),
the prose-grounding and citation-coverage tables, the reference-range
prose and the no-key fallback copy, each with Dutch test fixtures.

PHQ-9 / GAD-7 / WHO-5 are not marked validated for nl yet: the Dutch
wording has not been read against the published forms, so Dutch readers
get the English items plus the existing note until it has.
2026-10-19 11:06:55 +02:00

53 lines
2.0 KiB
JavaScript

#!/usr/bin/env node
/**
* Generate the locale-catalog boot scripts (`public/i18n/<locale>.js`).
*
* The root layout emits `<script src="/i18n/<locale>.js?v=<version>" defer>`
* ahead of Next's hydration scripts; the body assigns the active locale's
* full message bundle to `self.__HL_I18N`, which `src/lib/i18n/load-locale.ts`
* reads in its browser cache initializer. This replaces the former RSC-prop
* handoff that serialized the ENTIRE active catalog into every document's
* flight payload (392 KB of a 505 KB dashboard HTML, re-downloaded on every
* hard load).
*
* Static `public/` files (rather than a route handler) because Next serves
* them with an ETag — repeat loads revalidate to a 304 instead of
* re-downloading the catalog — and the service worker caches the versioned
* URL cache-first for the offline relaunch. The `?v=` query the layout
* appends busts both caches per deploy.
*
* Runs as a `prebuild`/`predev` step (the `sw-version.js` pattern); output
* is gitignored. Content is the same public catalog JSON that ships in the
* repository — no tenant data, no secrets.
*/
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { join, dirname } from "node:path";
import { fileURLToPath } from "node:url";
const __dirname = dirname(fileURLToPath(import.meta.url));
const repoRoot = join(__dirname, "..");
const outDir = join(repoRoot, "public", "i18n");
const LOCALES = ["de", "en", "fr", "es", "it", "pl", "ko", "nl"];
mkdirSync(outDir, { recursive: true });
const banner =
"// AUTO-GENERATED by scripts/generate-i18n-boot.mjs — do not edit.\n";
for (const locale of LOCALES) {
const messages = JSON.parse(
readFileSync(join(repoRoot, "messages", `${locale}.json`), "utf8"),
);
const payload = JSON.stringify({ locale, messages });
writeFileSync(
join(outDir, `${locale}.js`),
`${banner}self.__HL_I18N = ${payload};\n`,
"utf8",
);
}
process.stdout.write(
`generate-i18n-boot: wrote ${LOCALES.length} catalogs to public/i18n/\n`,
);