@scoutello/i18n-magic
Version:
Intelligent CLI toolkit that automates internationalization workflows for JavaScript/TypeScript projects and auto-translates new string keys to other languages
95 lines โข 4.21 kB
JavaScript
import { checkAllKeysExist, findExistingTranslations, getMissingKeys, getTextInput, loadLocalesFile, translateKey, writeLocalesFile, } from "../lib/utils.js";
import { findUnusedKeys, removeUnusedKeys } from "./clean.js";
export const translateMissing = async (config) => {
const { loadPath, savePath, defaultLocale, namespaces, locales, context, openai, disableTranslationDuringScan, autoClear, } = config;
if (autoClear) {
console.log("๐งน Checking for unused translations before scanning...");
const report = await findUnusedKeys(config);
if (report.unusedCount > 0) {
await removeUnusedKeys(config);
console.log("");
}
else {
console.log("โ
No unused keys found.\n");
}
}
const newKeys = await getMissingKeys(config);
if (newKeys.length === 0) {
console.log("No new keys found.");
await checkAllKeysExist(config);
return;
}
console.log(`${newKeys.length} keys are missing. Please provide the values for the following keys in ${defaultLocale}:`);
const newKeysWithDefaultLocale = [];
// Check for existing translations in parallel
const keysList = newKeys.map((k) => k.key);
const existingTranslationResults = await findExistingTranslations(keysList, namespaces, defaultLocale, loadPath);
const reusedKeys = [];
for (const newKey of newKeys) {
const existingValue = existingTranslationResults[newKey.key];
let answer;
// Use explicit null check instead of truthy check to handle empty string values
if (existingValue !== null) {
reusedKeys.push(newKey.key);
answer = existingValue;
}
else {
answer = await getTextInput(newKey.key, newKey.namespaces);
}
newKeysWithDefaultLocale.push({
key: newKey.key,
namespace: newKey.namespace,
namespaces: newKey.namespaces,
value: answer,
});
}
// Batch log reused keys
if (reusedKeys.length > 0) {
console.log(`๐ Auto-reused ${reusedKeys.length} existing values from other namespaces`);
}
const newKeysObject = newKeysWithDefaultLocale.reduce((prev, next) => {
prev[next.key] = next.value;
return prev;
}, {});
const allLocales = disableTranslationDuringScan ? [defaultLocale] : locales;
// Batch translate for all non-default locales in parallel
const translationCache = {
[defaultLocale]: newKeysObject,
};
const nonDefaultLocales = allLocales.filter((l) => l !== defaultLocale);
if (nonDefaultLocales.length > 0) {
await Promise.all(nonDefaultLocales.map(async (locale) => {
const translatedValues = await translateKey({
inputLanguage: defaultLocale,
outputLanguage: locale,
context,
object: newKeysObject,
openai,
model: config.model,
});
translationCache[locale] = translatedValues;
}));
}
// Process all locale/namespace combinations in parallel
const writeResults = [];
await Promise.all(allLocales.flatMap((locale) => namespaces.map(async (namespace) => {
const existingKeys = await loadLocalesFile(loadPath, locale, namespace);
const relevantKeys = newKeysWithDefaultLocale.filter((key) => key.namespaces?.includes(namespace));
if (relevantKeys.length === 0) {
return;
}
const translatedValues = translationCache[locale];
for (const key of relevantKeys) {
existingKeys[key.key] = translatedValues[key.key];
}
await writeLocalesFile(savePath, locale, namespace, existingKeys);
writeResults.push({ locale, namespace, keyCount: relevantKeys.length });
})));
// Log where keys were written
for (const { locale, namespace, keyCount } of writeResults) {
console.log(` ๐ Wrote ${keyCount} key(s) to ${locale}/${namespace}.json`);
}
await checkAllKeysExist(config);
console.log(`Successfully translated ${newKeys.length} keys.`);
};
//# sourceMappingURL=scan.js.map