verstak-official-plugins/scripts/check-locales.mjs

82 lines
3.1 KiB
JavaScript

import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
const pluginsRoot = path.join(root, 'plugins');
const contributionFields = {
views: 'title',
commands: 'title',
settingsPanels: 'title',
sidebarItems: 'title',
fileActions: 'label',
noteActions: 'label',
contextMenuEntries: 'label',
searchProviders: 'label',
statusBarItems: 'label',
openProviders: 'title',
workspaceItems: 'title',
};
const problems = [];
for (const entry of fs.readdirSync(pluginsRoot, { withFileTypes: true })) {
if (!entry.isDirectory()) continue;
const pluginRoot = path.join(pluginsRoot, entry.name);
const manifestPath = path.join(pluginRoot, 'plugin.json');
if (!fs.existsSync(manifestPath)) continue;
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
const localization = manifest.localization;
if (!localization || localization.defaultLocale !== 'en') {
problems.push(`${manifest.id}: localization.defaultLocale must be en`);
continue;
}
if (!localization.locales || !localization.locales.en || !localization.locales.ru) {
problems.push(`${manifest.id}: en and ru catalogs must be declared`);
continue;
}
const catalogs = {};
for (const locale of ['en', 'ru']) {
const relative = localization.locales[locale];
const catalogPath = path.resolve(pluginRoot, relative);
if (!catalogPath.startsWith(pluginRoot + path.sep) || !fs.existsSync(catalogPath)) {
problems.push(`${manifest.id}: missing safe ${locale} catalog at ${relative}`);
continue;
}
try {
catalogs[locale] = JSON.parse(fs.readFileSync(catalogPath, 'utf8'));
} catch (error) {
problems.push(`${manifest.id}: invalid ${locale} catalog: ${error.message}`);
continue;
}
for (const [key, value] of Object.entries(catalogs[locale])) {
if (typeof value !== 'string') problems.push(`${manifest.id}: ${locale}.${key} must be a string`);
}
}
if (!catalogs.en || !catalogs.ru) continue;
const enKeys = Object.keys(catalogs.en).sort();
const ruKeys = Object.keys(catalogs.ru).sort();
if (JSON.stringify(enKeys) !== JSON.stringify(ruKeys)) {
problems.push(`${manifest.id}: en/ru catalog keys differ`);
}
const required = ['manifest.name'];
if (manifest.description) required.push('manifest.description');
for (const [point, field] of Object.entries(contributionFields)) {
for (const item of manifest.contributes?.[point] || []) {
required.push(`contributions.${point}.${item.id}.${field}`);
}
}
for (const key of required) {
if (!Object.prototype.hasOwnProperty.call(catalogs.en, key)) problems.push(`${manifest.id}: en catalog missing ${key}`);
if (!Object.prototype.hasOwnProperty.call(catalogs.ru, key)) problems.push(`${manifest.id}: ru catalog missing ${key}`);
}
}
if (problems.length > 0) {
problems.forEach((problem) => console.error(` FAIL ${problem}`));
process.exit(1);
}
console.log(' OK official plugin locale catalogs are complete and aligned');