UNPKG

@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
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