import { describe, expect, it } from "vitest"; import en from "@/i18n/locales/en.json"; import ja from "@/i18n/locales/ja.json"; import zhTW from "@/i18n/locales/zh-TW.json"; import zh from "@/i18n/locales/zh.json"; type TranslationTree = Record; function flattenStrings( value: unknown, path: string[] = [], result = new Map(), ): Map { if (typeof value === "string") { result.set(path.join("."), value); } else if (typeof value === "object" && value !== null) { for (const [key, child] of Object.entries(value)) { flattenStrings(child, [...path, key], result); } } return result; } function interpolationVariables(value: string): string[] { return Array.from( value.matchAll(/\{\{\s*([^}]+?)\s*\}\}/g), ([, name]) => name, ).sort(); } const reference = flattenStrings(en); const piKeysOutsideNamespace = new Set([ "apps.pi", "deeplink.api", "sessionManager.piDiscoveryUnavailable", "sessionManager.piRelativeSessionDir", "settings.browsePlaceholderPi", "settings.piConfigDir", "settings.piConfigDirDescription", "settings.piImportExportBoundary", "usage.appFilter.pi", ]); const piReference = new Map( [...reference].filter( ([key]) => key.startsWith("pi.") || key.startsWith("proxy.piGateway.") || key.startsWith("skills.piStatus.") || piKeysOutsideNamespace.has(key), ), ); const locales = [ ["zh", zh], ["ja", ja], ["zh-TW", zhTW], ] as const; describe("locale coverage", () => { it.each(locales)("covers every Pi translation key in %s", (_name, tree) => { const translations = flattenStrings(tree as TranslationTree); const missing = [...piReference.keys()].filter( (key) => !translations.has(key), ); expect(missing).toEqual([]); }); it.each(locales)( "preserves every Pi interpolation variable in %s", (_name, tree) => { const translations = flattenStrings(tree as TranslationTree); const mismatched = [...piReference].flatMap(([key, expected]) => { const actual = translations.get(key); return actual !== undefined && interpolationVariables(actual).join("\0") !== interpolationVariables(expected).join("\0") ? [key] : []; }); expect(mismatched).toEqual([]); }, ); });