sync locales
This commit is contained in:
parent
b1b1f08306
commit
e502100cd9
2 changed files with 98 additions and 1 deletions
|
|
@ -21,7 +21,8 @@
|
|||
"package:desktop": "pnpm build && pnpm --filter @open-pets/desktop package",
|
||||
"release:desktop": "node apps/desktop/scripts/release-local.mjs",
|
||||
"release:npm": "node scripts/release-npm.mjs",
|
||||
"plugins:test": "node scripts/test-plugins.mjs",
|
||||
"plugins:locales": "node scripts/check-plugin-locales.mjs",
|
||||
"plugins:test": "pnpm plugins:locales && node scripts/test-plugins.mjs",
|
||||
"plugins:check": "node web/scripts/sync-plugins.js --dry-run --skip-r2",
|
||||
"plugins:package": "node web/scripts/sync-plugins.js --skip-r2",
|
||||
"plugins:publish": "node web/scripts/sync-plugins.js",
|
||||
|
|
|
|||
96
scripts/check-plugin-locales.mjs
Normal file
96
scripts/check-plugin-locales.mjs
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
import { readdir, readFile } from "node:fs/promises";
|
||||
import { join, relative } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const repoRoot = fileURLToPath(new URL("..", import.meta.url));
|
||||
const officialDir = join(repoRoot, "plugins", "official");
|
||||
|
||||
const errors = [];
|
||||
|
||||
async function pathExists(path) {
|
||||
try {
|
||||
await readdir(path);
|
||||
return true;
|
||||
} catch (error) {
|
||||
if (error.code === "ENOENT") return false;
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async function readLocaleJson(path) {
|
||||
try {
|
||||
const parsed = JSON.parse(await readFile(path, "utf8"));
|
||||
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
||||
errors.push(`${relative(repoRoot, path)} must be a JSON object.`);
|
||||
return {};
|
||||
}
|
||||
return flattenLocale(parsed);
|
||||
} catch (error) {
|
||||
errors.push(`${relative(repoRoot, path)} is not valid JSON: ${error.message}`);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
function flattenLocale(value, prefix = "") {
|
||||
const out = {};
|
||||
for (const [key, entry] of Object.entries(value)) {
|
||||
const fullKey = prefix ? `${prefix}.${key}` : key;
|
||||
if (entry && typeof entry === "object" && !Array.isArray(entry)) {
|
||||
Object.assign(out, flattenLocale(entry, fullKey));
|
||||
} else if (typeof entry === "string") {
|
||||
out[fullKey] = entry;
|
||||
} else {
|
||||
errors.push(`Locale key ${fullKey} must be a string.`);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function compareKeys(pluginName, localeName, englishKeys, localeKeys) {
|
||||
const localeKeySet = new Set(localeKeys);
|
||||
const englishKeySet = new Set(englishKeys);
|
||||
const missing = englishKeys.filter((key) => !localeKeySet.has(key));
|
||||
const extra = localeKeys.filter((key) => !englishKeySet.has(key));
|
||||
for (const key of missing) errors.push(`${pluginName}/${localeName} is missing key: ${key}`);
|
||||
for (const key of extra) errors.push(`${pluginName}/${localeName} has extra key not in en: ${key}`);
|
||||
}
|
||||
|
||||
async function checkPlugin(plugin) {
|
||||
const localesDir = join(officialDir, plugin.name, "locales");
|
||||
if (!(await pathExists(localesDir))) return;
|
||||
|
||||
const entries = (await readdir(localesDir, { withFileTypes: true }))
|
||||
.filter((entry) => entry.isFile() && entry.name.endsWith(".json") && !entry.name.startsWith("."))
|
||||
.sort((a, b) => a.name.localeCompare(b.name));
|
||||
|
||||
if (entries.length === 0) return;
|
||||
if (!entries.some((entry) => entry.name === "en.json")) {
|
||||
errors.push(`${plugin.name}/locales has locale files but no en.json source locale.`);
|
||||
return;
|
||||
}
|
||||
|
||||
const english = await readLocaleJson(join(localesDir, "en.json"));
|
||||
const englishKeys = Object.keys(english).sort();
|
||||
for (const entry of entries) {
|
||||
if (entry.name === "en.json") continue;
|
||||
const locale = await readLocaleJson(join(localesDir, entry.name));
|
||||
compareKeys(plugin.name, entry.name, englishKeys, Object.keys(locale).sort());
|
||||
}
|
||||
}
|
||||
|
||||
if (await pathExists(officialDir)) {
|
||||
const plugins = (await readdir(officialDir, { withFileTypes: true }))
|
||||
.filter((entry) => entry.isDirectory() && !entry.name.startsWith("."))
|
||||
.sort((a, b) => a.name.localeCompare(b.name));
|
||||
for (const plugin of plugins) await checkPlugin(plugin);
|
||||
}
|
||||
|
||||
if (errors.length > 0) {
|
||||
console.error(`Plugin locale key check failed with ${errors.length} issue${errors.length === 1 ? "" : "s"}:`);
|
||||
for (const error of errors) console.error(`- ${error}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log("Plugin locale key check passed.");
|
||||
Loading…
Add table
Reference in a new issue