import { describe, expect, test } from "bun:test"; import { readdirSync, readFileSync } from "node:fs"; import { join } from "node:path"; const LOCALES_DIR = join(import.meta.dir, "../../src/client/locales"); function flattenKeys(obj: Record, prefix = ""): string[] { const keys: string[] = []; for (const [key, value] of Object.entries(obj)) { const fullKey = prefix ? `${prefix}.${key}` : key; if (typeof value === "object" && value !== null && !Array.isArray(value)) { keys.push(...flattenKeys(value as Record, fullKey)); } else { keys.push(fullKey); } } return keys.sort(); } function loadLocale(locale: string): Record> { const dir = join(LOCALES_DIR, locale); const files = readdirSync(dir).filter((f) => f.endsWith(".json")); const result: Record> = {}; for (const file of files) { const ns = file.replace(".json", ""); result[ns] = JSON.parse(readFileSync(join(dir, file), "utf8")); } return result; } describe("locale key parity", () => { const en = loadLocale("en"); const de = loadLocale("de"); test("en and de have the same namespaces", () => { expect(Object.keys(en).sort()).toEqual(Object.keys(de).sort()); }); for (const ns of Object.keys(en)) { test(`${ns}: every en key exists in de`, () => { const enKeys = flattenKeys(en[ns]); const deKeys = flattenKeys(de[ns]); const missing = enKeys.filter((k) => !deKeys.includes(k)); expect(missing).toEqual([]); }); test(`${ns}: every de key exists in en`, () => { const enKeys = flattenKeys(en[ns]); const deKeys = flattenKeys(de[ns]); const orphan = deKeys.filter((k) => !enKeys.includes(k)); expect(orphan).toEqual([]); }); test(`${ns}: no empty de values`, () => { const deKeys = flattenKeys(de[ns]); for (const key of deKeys) { const value = key .split(".") .reduce( (obj, k) => (obj as Record)?.[k], de[ns] as unknown, ); expect(typeof value === "string" && value.length > 0).toBe(true); } }); } });