#!/usr/bin/env node import fs from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; import { localeCatalog, localeMessages } from "../docs/site-locales.mjs"; const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); const indexHtml = fs.readFileSync(path.join(repoRoot, "docs/index.html"), "utf8"); const expectedCodes = [ "en", "zh-CN", "zh-TW", "ja-JP", "es", "pt-BR", "ko", "de", "fr", "ar", "it", "vi", "nl", "tr", "uk", "id", "pl", "fa", "th", "ca", "sv", ]; const catalogCodes = localeCatalog.map((locale) => locale.code); const appLanguageSource = fs.readFileSync( path.join(repoRoot, "Sources/CodexBar/PreferencesGeneralPane.swift"), "utf8", ); assertEqual(catalogCodes, expectedCodes, "locale catalog"); assertEqual( localeCatalog.filter((locale) => locale.direction === "rtl").map((locale) => locale.code), ["ar", "fa"], "RTL locale catalog"); const appCatalogCodes = [...appLanguageSource.matchAll(/case \w+ = "([^"]+)"/g)] .map((match) => match[1]) .filter(Boolean) .map((code) => ({ "zh-Hans": "zh-CN", "zh-Hant": "zh-TW", ja: "ja-JP" })[code] ?? code); assertEqual(appCatalogCodes, expectedCodes, "app language catalog"); const englishKeys = Object.keys(localeMessages.en).sort(); for (const locale of localeCatalog) { const messages = localeMessages[locale.code]; assert(messages, `missing messages for ${locale.code}`); assertEqual(Object.keys(messages).sort(), englishKeys, `${locale.code} message keys`); for (const key of englishKeys) { assert(messages[key].trim(), `${locale.code}.${key} is blank`); assertEqual(tokens(messages[key]), tokens(localeMessages.en[key]), `${locale.code}.${key} tokens`); } } const referencedKeys = new Set(); for (const match of indexHtml.matchAll(/data-i18n(?:-rich|-aria-label|-title|-alt)?="([^"]+)"/g)) { referencedKeys.add(match[1]); } for (const key of referencedKeys) { assert(englishKeys.includes(key), `index.html references unknown locale key ${key}`); } const optionMatches = [...indexHtml.matchAll(/