import { cpSync, mkdtempSync, readFileSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import path from "node:path"; import { fileURLToPath } from "node:url"; import type { AllCatalogsType } from "@lingui/cli/api"; import { getCatalogs } from "@lingui/cli/api"; import { getConfig } from "@lingui/conf"; import { afterAll, beforeAll, describe, expect, it } from "vitest"; const webRoot = fileURLToPath(new URL("../..", import.meta.url)); const localesDir = path.join(webRoot, "src", "locales"); const config = getConfig({ cwd: webRoot, configPath: path.join(webRoot, "lingui.config.ts") }); const extractHint = "Run `pnpm --filter @rakazo/web intl:extract` and commit the catalogs."; /** Runs `lingui extract --clean` against a copy of the committed catalogs. */ async function extractIntoCopy(dir: string): Promise { cpSync(localesDir, dir, { recursive: true }); const catalogs = await getCatalogs({ ...config, catalogs: config.catalogs.map((catalog) => ({ ...catalog, path: path.join(dir, "{locale}", "messages"), // Absolute, because the CLI resolves relative paths from apps/web and tests run from the root. include: [path.join(webRoot, "src")], exclude: catalog.exclude ?? [], })), }); expect(catalogs).toHaveLength(1); const result = await catalogs[0]!.make({ clean: true, overwrite: false, orderBy: config.orderBy, }); if (!result) throw new Error("Message extraction failed"); return result; } describe("lingui catalogs stay in sync with the source", () => { let dir: string; let extracted: AllCatalogsType; beforeAll(async () => { dir = mkdtempSync(path.join(tmpdir(), "evoo-locales-")); extracted = await extractIntoCopy(dir); }, 120_000); afterAll(() => { rmSync(dir, { recursive: true, force: true }); }); it("matches a fresh extraction for every locale", () => { const stale = config.locales.filter((locale) => { const file = path.join(locale, "messages.po"); return ( readFileSync(path.join(dir, file), "utf8") !== readFileSync(path.join(localesDir, file), "utf8") ); }); expect(stale, extractHint).toEqual([]); }); it("translates every German message", () => { const untranslated = Object.entries(extracted.de ?? {}) .filter(([, entry]) => !entry.translation) .map(([id, entry]) => entry.message ?? id); expect(untranslated).toEqual([]); }); });