i18n-ai-translate
Version:
Use LLMs to translate your i18n JSON to any language.
238 lines • 10.9 kB
JavaScript
;
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