UNPKG

i18n-ai-translate

Version:

Use LLMs to translate your i18n JSON to any language.

238 lines 10.9 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.translateFile = translateFile; exports.translateFileDiff = translateFileDiff; const diff_1 = require("diff"); const utils_1 = require("./utils"); const translate_1 = require("./translate"); const safe_1 = __importDefault(require("colors/safe")); const fs_1 = __importDefault(require("fs")); const path_1 = __importDefault(require("path")); /** * Wraps translate to take an input file and output its translation to another file * @param options - The file translation's options */ async function translateFile(options) { let inputJSON = {}; try { const inputFile = fs_1.default.readFileSync(options.inputFilePath, "utf-8"); inputJSON = JSON.parse(inputFile); } catch (e) { (0, utils_1.printError)(`Invalid input JSON: ${e}`); return; } const inputLanguage = (0, utils_1.getLanguageCodeFromFilename)(options.inputFilePath); let outputLanguage = ""; if (options.forceLanguageName) { outputLanguage = options.forceLanguageName; } else { outputLanguage = (0, utils_1.getLanguageCodeFromFilename)(options.outputFilePath); } try { const outputJSON = await (0, translate_1.translate)({ apiKey: options.apiKey, batchMaxTokens: options.batchMaxTokens, batchSize: options.batchSize, chatParams: options.chatParams, engine: options.engine, ensureChangedTranslation: options.ensureChangedTranslation, host: options.host, inputJSON, inputLanguage, model: options.model, outputLanguage, overridePrompt: options.overridePrompt, promptMode: options.promptMode, rateLimitMs: options.rateLimitMs, skipStylingVerification: options.skipStylingVerification, skipTranslationVerification: options.skipTranslationVerification, templatedStringPrefix: options.templatedStringPrefix, templatedStringSuffix: options.templatedStringSuffix, verbose: options.verbose, }); const outputText = JSON.stringify(outputJSON, null, 4); if (!options.dryRun) { fs_1.default.writeFileSync(options.outputFilePath, `${outputText}\n`); } else { const translationDiff = (0, diff_1.diffJson)(inputJSON, outputJSON); fs_1.default.writeFileSync(`${options.dryRun.basePath}/${path_1.default.basename(options.outputFilePath)}.new.json`, outputText); const patch = (0, diff_1.createPatch)(options.outputFilePath, JSON.stringify(inputJSON, null, 4), outputText); fs_1.default.writeFileSync(`${options.dryRun.basePath}/${path_1.default.basename(options.outputFilePath)}.patch`, patch); (0, utils_1.printInfo)(`Wrote new JSON to ${options.dryRun.basePath}/${path_1.default.basename(options.outputFilePath)}.new.json`); (0, utils_1.printInfo)(`Wrote patch to ${options.dryRun.basePath}/${path_1.default.basename(options.outputFilePath)}.patch`); if (options.verbose) { for (const part of translationDiff) { const colorFns = { green: safe_1.default.green, grey: safe_1.default.grey, red: safe_1.default.red, }; let color; if (part.added) { color = "green"; } else if (part.removed) { color = "red"; } else { color = "grey"; } process.stderr.write(colorFns[color](part.value)); } } console.log(); } } catch (err) { (0, utils_1.printError)(`Failed to translate file to ${outputLanguage}: ${err}`); throw err; } } /** * Wraps translateDiff to take two versions of a source file and update * the target translation's file by only modifying keys that changed in the source * @param options - The file diff translation's options */ async function translateFileDiff(options) { // Get all the *json files from the same path as beforeInputPath const outputFilesOrPaths = fs_1.default .readdirSync(path_1.default.dirname(options.inputBeforeFileOrPath)) .filter((file) => file.endsWith(".json")) .filter((file) => file !== path_1.default.basename(options.inputBeforeFileOrPath) && file !== path_1.default.basename(options.inputAfterFileOrPath)) .map((file) => path_1.default.resolve(path_1.default.dirname(options.inputBeforeFileOrPath), file)); const jsonFolder = path_1.default.resolve(process.cwd(), "jsons"); let inputBeforePath; let inputAfterPath; if (path_1.default.isAbsolute(options.inputBeforeFileOrPath)) { inputBeforePath = path_1.default.resolve(options.inputBeforeFileOrPath); } else { inputBeforePath = path_1.default.resolve(jsonFolder, options.inputBeforeFileOrPath); if (!fs_1.default.existsSync(inputBeforePath)) { inputBeforePath = path_1.default.resolve(process.cwd(), options.inputBeforeFileOrPath); } } if (path_1.default.isAbsolute(options.inputAfterFileOrPath)) { inputAfterPath = path_1.default.resolve(options.inputAfterFileOrPath); } else { inputAfterPath = path_1.default.resolve(jsonFolder, options.inputAfterFileOrPath); } const outputPaths = []; for (const outputFileOrPath of outputFilesOrPaths) { let outputPath; if (path_1.default.isAbsolute(outputFileOrPath)) { outputPath = path_1.default.resolve(outputFileOrPath); } else { outputPath = path_1.default.resolve(jsonFolder, outputFileOrPath); if (!fs_1.default.existsSync(jsonFolder)) { outputPath = path_1.default.resolve(process.cwd(), outputFileOrPath); } } outputPaths.push(outputPath); } let inputBeforeJSON = {}; let inputAfterJSON = {}; try { let inputFile = fs_1.default.readFileSync(inputBeforePath, "utf-8"); inputBeforeJSON = JSON.parse(inputFile); inputFile = fs_1.default.readFileSync(inputAfterPath, "utf-8"); inputAfterJSON = JSON.parse(inputFile); } catch (e) { (0, utils_1.printError)(`Invalid input JSON: ${e}`); return; } const toUpdateJSONs = {}; const languageCodeToOutputPath = {}; for (const outputPath of outputPaths) { const languageCode = (0, utils_1.getLanguageCodeFromFilename)(path_1.default.basename(outputPath)); if (!languageCode) { throw new Error("Invalid output file name. Use a valid ISO 639-1 language code as the file name."); } try { const outputFile = fs_1.default.readFileSync(outputPath, "utf-8"); toUpdateJSONs[languageCode] = JSON.parse(outputFile); languageCodeToOutputPath[languageCode] = outputPath; } catch (e) { (0, utils_1.printError)(`Invalid output JSON: ${e}`); } } try { const outputJSON = await (0, translate_1.translateDiff)({ apiKey: options.apiKey, batchMaxTokens: options.batchMaxTokens, batchSize: options.batchSize, chatParams: options.chatParams, engine: options.engine, ensureChangedTranslation: options.ensureChangedTranslation, host: options.host, inputJSONAfter: inputAfterJSON, inputJSONBefore: inputBeforeJSON, inputLanguage: options.inputLanguageCode, model: options.model, overridePrompt: options.overridePrompt, promptMode: options.promptMode, rateLimitMs: options.rateLimitMs, skipStylingVerification: options.skipStylingVerification, skipTranslationVerification: options.skipTranslationVerification, templatedStringPrefix: options.templatedStringPrefix, templatedStringSuffix: options.templatedStringSuffix, toUpdateJSONs, verbose: options.verbose, }); for (const language in outputJSON) { if (Object.prototype.hasOwnProperty.call(outputJSON, language)) { const outputText = JSON.stringify(outputJSON[language], null, 4); const inputJSON = toUpdateJSONs[language]; const outputPath = languageCodeToOutputPath[language]; if (!options.dryRun) { fs_1.default.writeFileSync(outputPath, `${outputText}\n`); } else { const translationDiff = (0, diff_1.diffJson)(inputJSON, outputJSON[language]); fs_1.default.writeFileSync(`${options.dryRun.basePath}/${path_1.default.basename(outputPath)}.new.json`, outputText); const patch = (0, diff_1.createPatch)(outputPath, JSON.stringify(inputJSON, null, 4), outputText); fs_1.default.writeFileSync(`${options.dryRun.basePath}/${path_1.default.basename(outputPath)}.patch`, patch); (0, utils_1.printInfo)(`Wrote new JSON to ${options.dryRun.basePath}/${path_1.default.basename(outputPath)}.new.json`); (0, utils_1.printInfo)(`Wrote patch to ${options.dryRun.basePath}/${path_1.default.basename(outputPath)}.patch`); if (options.verbose) { for (const part of translationDiff) { const colorFns = { green: safe_1.default.green, grey: safe_1.default.grey, red: safe_1.default.red, }; let color; if (part.added) { color = "green"; } else if (part.removed) { color = "red"; } else { color = "grey"; } process.stderr.write(colorFns[color](part.value)); } console.log(); } } } } } catch (err) { (0, utils_1.printError)(`Failed to translate file diff: ${err}`); throw err; } } //# sourceMappingURL=translate_file.js.map