halo/ui/scripts/find_missing_translations.mjs

113 lines
3.2 KiB
JavaScript

/**
* Find Missing Translations
* -------------------------
* This script identifies missing translations in language files by comparing them
* with the English base file (en.yaml).
*
* For each language file, it:
* 1. Compares it with the English base file
* 2. Identifies keys that are missing in the target language
* 3. Creates a "_missing_translations_[lang].yaml" file with those missing keys
*
* Usage:
* node scripts/find_missing_translations.mjs
*
* Example output:
* Generated src/locales/_missing_translations_zh-TW.yaml with 25 missing translations
*
* After running this script, you can translate the missing entries in the generated files,
* then use apply_missing_translations.mjs to merge them into the main language files.
*/
import fs from "fs/promises";
import yaml from "js-yaml";
import path from "path";
import { fileURLToPath } from "url";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const translationsDirPath = path.resolve(__dirname, "../src/locales");
const baseFile = `${translationsDirPath}/en.yaml`;
async function main() {
try {
const baseTranslations = await loadYamlFile(baseFile);
const dirEntries = await fs.readdir(translationsDirPath, {
withFileTypes: true,
});
const translationFiles = dirEntries
.filter(
(entry) =>
entry.isFile() &&
entry.name.endsWith(".yaml") &&
entry.name !== "en.yaml" &&
!entry.name.includes("_missing_translations_")
)
.map((entry) => path.join(translationsDirPath, entry.name));
for (const transFile of translationFiles) {
const langCode = path.basename(transFile, ".yaml");
try {
const translations = await loadYamlFile(transFile);
const missing = findMissingTranslations(baseTranslations, translations);
if (Object.keys(missing).length > 0) {
const missingFile = `${translationsDirPath}/_missing_translations_${langCode}.yaml`;
await saveYamlFile(missing, missingFile);
console.log(
`Generated ${missingFile} with ${
Object.keys(missing).length
} missing translations`
);
}
} catch (e) {
console.log(`Error processing ${transFile}: ${e}`);
}
}
} catch (e) {
console.log(`Error: ${e}`);
}
}
async function loadYamlFile(filePath) {
const content = await fs.readFile(filePath, "utf8");
return yaml.load(content) || {};
}
async function saveYamlFile(data, filePath) {
const yamlContent = yaml.dump(data, {
indent: 2,
lineWidth: -1,
});
await fs.writeFile(filePath, yamlContent, "utf8");
}
function findMissingTranslations(baseDict, compareDict) {
const missing = {};
for (const key of Object.keys(baseDict)) {
if (!Object.prototype.hasOwnProperty.call(compareDict, key)) {
missing[key] = baseDict[key];
} else if (
typeof baseDict[key] === "object" &&
baseDict[key] !== null &&
typeof compareDict[key] === "object" &&
compareDict[key] !== null
) {
const subMissing = findMissingTranslations(
baseDict[key],
compareDict[key]
);
if (Object.keys(subMissing).length > 0) {
missing[key] = subMissing;
}
}
}
return missing;
}
main();