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

106 lines • 4.9 kB
import { getMissingKeys, findExistingTranslations, loadLocalesFile, writeLocalesFile, translateKey, } from "../lib/utils.js"; export const checkMissing = async (config) => { const { loadPath, savePath, defaultLocale, namespaces, locales, context, openai, model, disableTranslationDuringScan, } = config; const newKeys = await getMissingKeys(config); if (newKeys.length === 0) { console.log("āœ… No missing translations found."); return; } console.log(`\nšŸ” Found ${newKeys.length} missing key(s) in default locale (${defaultLocale}):`); // Group keys by namespace for clearer output const keysByNamespace = {}; for (const key of newKeys) { for (const ns of key.namespaces) { if (!keysByNamespace[ns]) { keysByNamespace[ns] = []; } keysByNamespace[ns].push(key.key); } } for (const [namespace, keys] of Object.entries(keysByNamespace)) { console.log(`\n šŸ“¦ ${namespace}: ${keys.length} missing key(s)`); for (const key of keys) { console.log(` - ${key}`); } } // Try to auto-fill from other namespaces (like scan does) const keysList = newKeys.map((k) => k.key); const existingTranslationResults = await findExistingTranslations(keysList, namespaces, defaultLocale, loadPath); const keysWithValues = []; const reusedKeys = []; const stillMissingKeys = []; for (const newKey of newKeys) { const existingValue = existingTranslationResults[newKey.key]; if (existingValue !== null) { reusedKeys.push(newKey.key); keysWithValues.push({ key: newKey.key, namespaces: newKey.namespaces, value: existingValue, }); } else { stillMissingKeys.push(newKey.key); keysWithValues.push({ key: newKey.key, namespaces: newKey.namespaces, value: null, }); } } // If we can auto-fill some keys, do it if (reusedKeys.length > 0) { console.log(`\nšŸ”„ Auto-reusing ${reusedKeys.length} existing value(s) from other namespaces...`); const keysToWrite = keysWithValues.filter((k) => k.value !== null); const newKeysObject = keysToWrite.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 && !disableTranslationDuringScan) { console.log(` šŸ“ Translating to ${nonDefaultLocales.length} other locale(s)...`); await Promise.all(nonDefaultLocales.map(async (locale) => { const translatedValues = await translateKey({ inputLanguage: defaultLocale, outputLanguage: locale, context, object: newKeysObject, openai, model, }); translationCache[locale] = translatedValues; })); } // Process all locale/namespace combinations in parallel await Promise.all(allLocales.flatMap((locale) => namespaces.map(async (namespace) => { const existingKeys = await loadLocalesFile(loadPath, locale, namespace); const relevantKeys = keysToWrite.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); console.log(` āœ… Updated ${locale}/${namespace}.json with ${relevantKeys.length} key(s)`); }))); console.log(`\nāœ… Successfully auto-filled ${reusedKeys.length} key(s) from existing translations.`); } // Now check if there are still missing keys that couldn't be auto-filled if (stillMissingKeys.length > 0) { console.error(`\nāŒ Error: ${stillMissingKeys.length} translation(s) could not be auto-filled:`); for (const key of stillMissingKeys) { console.error(` - ${key}`); } console.error(`\nPlease run 'i18n-magic scan' to provide values for these keys.`); process.exit(1); } console.log("\nāœ… All translations are complete."); }; //# sourceMappingURL=check-missing.js.map