i18n-translate-agent
Version:
An intelligent i18n translation agent powered by OpenAI, supporting automatic translation of JSON files with caching and progress tracking
167 lines (166 loc) • 6.56 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import path from "path";
import fs from "fs";
import { intersection, notExistsToCreateFile, readJsonFileSync, } from "../utils.js";
/**
* 和翻译缓存json文件对比 返回存在更改的json文件
* @param {object} cacheObject 已经缓存的对象 (包含译文)
* @param {object} translateObject 需要翻译的对象 (源语言)
* @returns {object} 存在修改的对象
*/
export const translateJSONDiffToJson = (cacheObject, translateObject) => {
if (Object.values(cacheObject).length === 0) {
console.log(`💾 [Cache] No cache found, translating all ${Object.keys(translateObject).length} items`);
return translateObject;
}
// json文件内容diff
const pendingTranslateMap = {};
const totalItems = Object.keys(translateObject).length;
let cacheHits = 0;
Object.entries(translateObject).forEach(([key, value]) => {
//不存在该key 是新增的key,需要翻译
if (!cacheObject.hasOwnProperty(key)) {
pendingTranslateMap[key] = value;
}
else {
// 存在缓存key就算命中缓存,不需要重新翻译
cacheHits++;
}
});
const missedItems = Object.keys(pendingTranslateMap).length;
console.log(`💾 [Cache] Cache hits: ${cacheHits}/${totalItems}, translating ${missedItems} items`);
return pendingTranslateMap;
};
/**
* 获取缓存文件
* @param {string} filePath 缓存文件路径
* @returns {Promise<{key:value}>}
*/
export const getCacheFileSync = (filePath) => __awaiter(void 0, void 0, void 0, function* () {
if (fs.existsSync(filePath)) {
return yield readJsonFileSync(filePath);
}
else {
return {};
}
});
/**
* 注册语言缓存文件
* @param {string} language
*/
export const registerLanguageCacheFile = (params) => __awaiter(void 0, void 0, void 0, function* () {
const { jsonMap, sourceFilePath, fileName, language, folderName } = params;
const cacheFilePath = path.join(folderName, language, fileName);
const sourceObject = yield readJsonFileSync(sourceFilePath);
const cacheObject = yield readJsonFileSync(cacheFilePath);
Object.entries(jsonMap).forEach(([key, value]) => {
cacheObject[key] = value; // 存储翻译后的内容,而不是源语言内容
});
if (Object.values(jsonMap).length === 0)
return;
notExistsToCreateFile(folderName);
notExistsToCreateFile(`${folderName}/${language}`);
yield fs.writeFileSync(cacheFilePath, JSON.stringify(cacheObject, null, 2), "utf8");
});
/**
* 根据已翻译文件生成缓存
*/
export const generateCache = (params) => __awaiter(void 0, void 0, void 0, function* () {
const { sourceFolderPath, languages, exportFolderPath, sourceLanguage } = params;
if (!languages || !Array.isArray(languages) || languages.length === 0)
return;
// 读取源文件夹下的所有文件
const sourceFileData = readAllFilesOfFolder(path.resolve(sourceFolderPath, sourceLanguage));
// 源文件数据map
const sourceFileMap = {};
sourceFileData.forEach((item) => {
sourceFileMap[item.fileName] = item;
});
languages.forEach((language) => {
// 获取当前语言的文件数据
const currentFileData = readAllFilesOfFolder(path.join(sourceFolderPath, language));
currentFileData.forEach(({ data, fileName }) => {
const translated = {};
// 遍历文件中的已经翻译的内容
Object.entries(data).forEach(([key, value]) => {
if (value) {
translated[key] = sourceFileMap[fileName].data[key];
}
});
notExistsToCreateFile(path.resolve(exportFolderPath, language));
fs.writeFileSync(path.resolve(exportFolderPath, language, fileName), JSON.stringify(translated, null, 2), "utf-8");
});
});
});
/**
* 读取文件夹下的所有文件 并返回string类型的内容
* @param sourceFolderPath
* @returns
*/
const readAllFilesOfFolder = (sourceFolderPath) => {
try {
if (!fs.existsSync(sourceFolderPath))
return [];
const sourceFiles = fs.readdirSync(sourceFolderPath);
return sourceFiles.map((fileName) => {
return readFile(sourceFolderPath, fileName);
});
}
catch (error) {
console.error("read folder error:", error);
return [];
}
};
const readFile = (sourceFolderPath, fileName) => {
const filePath = path.resolve(sourceFolderPath, fileName);
try {
const jsonData = fs.readFileSync(filePath, "utf-8");
const data = JSON.parse(jsonData);
return {
fileName,
filePath,
data,
};
}
catch (error) {
console.error("read file error:", error);
return {
fileName,
filePath,
data: {},
};
}
};
/**
* 批量删除指定语言的缓存信息
* @param params
*/
export const deleteBatchCache = (params) => {
const { keys, cacheFolderPath, languages, cacheFileName } = params;
let deleteLanguages = [];
const localLanguages = fs.readdirSync(cacheFolderPath);
if (!languages || !Array.isArray(languages) || languages.length === 0) {
deleteLanguages = localLanguages;
}
else {
deleteLanguages = intersection(localLanguages, languages);
}
if (deleteLanguages.length === 0)
return;
deleteLanguages.forEach((language) => {
const jsonData = fs.readFileSync(path.resolve(cacheFolderPath, language, cacheFileName), "utf-8");
const data = JSON.parse(jsonData);
keys.forEach((key) => {
delete data[key];
});
fs.writeFileSync(path.resolve(cacheFolderPath, language, cacheFileName), JSON.stringify(data, null, 2), "utf-8");
});
};