UNPKG

@scoutello/i18n-magic

Version:

Intelligent CLI toolkit that automates internationalization workflows with AI-powered translations for JavaScript/TypeScript projects

150 lines (132 loc) 4.35 kB
import type { Configuration } from "../lib/types" import { getKeysWithNamespaces, getPureKey, getTextInput, loadLocalesFile, translateKey, writeLocalesFile, } from "../lib/utils" const getKeyToReplace = async ( allAvailableKeys: Record<string, { namespace: string; value: string }[]>, ): Promise<{ key: string; namespaces: string[] }> => { const keyToReplace = await getTextInput( "Enter the key to replace the translation for: ", ) if (!allAvailableKeys[keyToReplace]) { console.log(`The key "${keyToReplace}" does not exist.`) return await getKeyToReplace(allAvailableKeys) } const namespaces = allAvailableKeys[keyToReplace].map((k) => k.namespace) console.log( `The key "${keyToReplace}" exists in namespaces: ${namespaces.join(", ")}.`, ) return { key: keyToReplace, namespaces } } export const replaceTranslation = async ( config: Configuration, key?: string, ) => { const { loadPath, savePath, defaultLocale, defaultNamespace, namespaces, locales, globPatterns, context, openai, } = config // Find all keys with their namespaces from the codebase const keysWithNamespaces = await getKeysWithNamespaces({ globPatterns, defaultNamespace, }) // Build a map of all available keys across all namespaces const allAvailableKeys: Record< string, { namespace: string; value: string }[] > = {} for (const namespace of namespaces) { const keys = await loadLocalesFile(loadPath, defaultLocale, namespace) for (const [keyName, value] of Object.entries(keys)) { if (!allAvailableKeys[keyName]) { allAvailableKeys[keyName] = [] } allAvailableKeys[keyName].push({ namespace, value }) } } let keyToReplace: string let targetNamespaces: string[] = [] if (key) { if (allAvailableKeys[key]) { keyToReplace = key // Determine which namespaces this key should be updated in based on usage const keyUsage = keysWithNamespaces.filter((k) => { const pureKey = getPureKey(k.key, defaultNamespace, true) return pureKey === key || k.key === key }) if (keyUsage.length > 0) { // Use namespaces from actual usage const allNamespaces: string[] = [] for (const k of keyUsage) { allNamespaces.push(...k.namespaces) } targetNamespaces = [...new Set(allNamespaces)] } else { // Fallback to all namespaces where the key exists targetNamespaces = allAvailableKeys[key].map((k) => k.namespace) } console.log( `The key "${keyToReplace}" exists in namespaces: ${targetNamespaces.join(", ")}.`, ) } else { console.log(`The key "${key}" does not exist.`) const result = await getKeyToReplace(allAvailableKeys) keyToReplace = result.key targetNamespaces = result.namespaces } } else { const result = await getKeyToReplace(allAvailableKeys) keyToReplace = result.key targetNamespaces = result.namespaces } // Show current translations across namespaces for (const namespace of targetNamespaces) { const keys = await loadLocalesFile(loadPath, defaultLocale, namespace) if (keys[keyToReplace]) { console.log( `Current translation in ${defaultLocale} (${namespace}): "${keys[keyToReplace]}"`, ) } } const newTranslation = await getTextInput("Enter the new translation: ") // Update the key in all relevant namespaces and locales for (const namespace of targetNamespaces) { for (const locale of locales) { let newValue = "" if (locale === defaultLocale) { newValue = newTranslation } else { const translation = await translateKey({ context, inputLanguage: defaultLocale, outputLanguage: locale, object: { [keyToReplace]: newTranslation, }, openai, model: config.model, }) newValue = translation[keyToReplace] } const existingKeys = await loadLocalesFile(loadPath, locale, namespace) existingKeys[keyToReplace] = newValue await writeLocalesFile(savePath, locale, namespace, existingKeys) console.log( `Updated "${keyToReplace}" in ${locale} (${namespace}): "${newValue}"`, ) } } }