mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-07 04:12:04 +00:00
Full Russian catalog (11,154 keys, 1:1 with en, identical nesting) and every
locale wiring point the app declares, so ru is a first-class locale instead
of an English fallback:
- messages/ru.json, project.inlang/settings.json, i18n boot script
- i18n config / load-locale, format-locale (ru-RU), date-fns ru
- labs: parse-reference-range RANGE_PROSE for Russian lab sheets
("3,5 - 5,0", "от 3,5 до 5,0", "до 5,6", "не более 5,6")
- AI: citation-coverage normative stems, coach system prompt and insights
reply footer (Russian prompts must override the hard-coded
"Respond in English"), population-norm patterns
- safety: outbound-screen Russian imperative/dose/unit/causality bank
- mental health: PHQ-9, GAD-7, WHO-5 and SCI wording
verify-message-catalogs.mjs passes for all 10 locales.
53 lines
2.0 KiB
JavaScript
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", "pt-BR", "ru"];
|
|
|
|
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`,
|
|
);
|