appwrite/.github/workflows/static-analysis/locale/index.js
2025-08-12 14:09:36 +02:00

115 lines
3.3 KiB
JavaScript

/*
* Look into all local files, and collect unique keys.
* Ensure fallback locale (English) has translation for all keys.
* If configured as `const strict = true`, all locales will be checked to include all keys.
*/
import { readdir, readFile } from "fs/promises";
import { join, dirname } from "path";
import { fileURLToPath } from "url";
const config = {
strict: false,
fallbackLocale: "en.json",
};
(async () => {
try {
// Prepare current directory equivalent in ES modules
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const translationsPath = join(
__dirname,
"../../../../app/config/locale/translations",
);
const files = (await readdir(translationsPath)).filter((file) =>
file.endsWith(".json"),
);
if (files.length === 0) {
console.error("No translation files found in ", translationsPath);
process.exit(1);
}
// Check if fallback locale exists
if (!files.includes(config.fallbackLocale)) {
console.error(`Fallback locale file ${config.fallbackLocale} not found`);
process.exit(1);
}
console.log(
`Found ${files.length} translation files in ${translationsPath}`,
);
// Collect all unique keys from all translation files
const allKeys = new Set();
for (const file of files) {
const filePath = join(translationsPath, file);
const content = await readFile(filePath, "utf8");
const translations = JSON.parse(content);
// Add all keys from this file
Object.keys(translations).forEach((key) => allKeys.add(key));
}
console.log(`Total unique keys found across all locales: ${allKeys.size}`);
const localesToCheck = [];
if (config.strict) {
localesToCheck.push(...files);
} else {
localesToCheck.push(config.fallbackLocale);
}
let errorsCount = 0;
let missingLocaleCount = 0;
for (const localeToCheck of localesToCheck) {
// Read locale
const path = join(translationsPath, localeToCheck);
const content = await readFile(path, "utf8");
const translations = JSON.parse(content);
// Check for missing keys in the locale
const keys = new Set(Object.keys(translations));
console.log(`Keys in locale (${localeToCheck}): ${keys.size}`);
const missingKeys = [];
for (const key of allKeys) {
if (!keys.has(key)) {
missingKeys.push(key);
}
}
if (missingKeys.length > 0) {
console.error(
`\nERROR: Fallback locale (${localeToCheck}) is missing ${missingKeys.length} key(s):`,
);
missingKeys.sort().forEach((key) => {
console.error(` - ${key}`);
});
console.error(
`\nTo fix this issue, add the missing keys to ${translationsPath}/${localeToCheck}`,
);
errorsCount++;
missingLocaleCount += missingKeys.length;
} else {
console.log(
`\nSUCCESS: Fallback locale (${localeToCheck}) contains all ${allKeys.size} keys.`,
);
}
}
if (errorsCount > 0) {
console.log(`\n${missingLocaleCount} locales missing found across ${errorsCount} locales.`);
process.exit(1);
}
} catch (error) {
console.error("Unexpected error.");
console.error(error);
process.exit(1);
}
})();