i18n-translate-agent
Version:
An intelligent i18n translation agent powered by OpenAI, supporting automatic translation of JSON files with caching and progress tracking
183 lines (157 loc) • 4.7 kB
text/typescript
import path from "path";
import fs from "fs";
import { ICreateJsonFileParams } from "../types";
/**
* 不存在的文件夹则创建
* @param {string} path
*/
export const notExistsToCreateFile = (path: string) => {
if (fs.existsSync(path)) return;
fs.mkdirSync(path);
};
/**
* 获取json文件 并返回 [[key,value],[key,value]...]]
* @param {*} path
* @returns
*/
export const readJsonFileSync = async (path: string) => {
try {
if (!fs.existsSync(path)) return {};
const jsonStr = await fs.readFileSync(path, "utf8");
// 将JSON字符串解析为对象
return JSON.parse(jsonStr);
} catch (error) {
console.error("解析JSON时出错:", error);
return {};
}
};
/**
* 创建json文件
* @param {string} fileName 文件名
* @param {string} folderName 文件夹名
* @param {string} language 语言环境
* @param {object} jsonData 文件数据
*/
export const createJsonFile = (params: ICreateJsonFileParams) => {
const { fileName, folderName, jsonData, language } = params;
notExistsToCreateFile(folderName);
notExistsToCreateFile(path.resolve(`${folderName}/${language}`));
fs.writeFileSync(
path.resolve(`${folderName}/${language}/${fileName}`),
JSON.stringify(jsonData, null, 2),
"utf8"
);
};
export const getRandomNumber = (min: number, max: number) => {
return Math.floor(Math.random() * (max - min + 1)) + min;
};
export const isDirectoryPath = (path: string) => {
if (!fs.existsSync(path)) return false;
return fs.statSync(path).isDirectory();
};
export const readFileOfDirSync = (dirPath: string) => {
if (!isDirectoryPath(dirPath)) return [];
const files = fs.readdirSync(dirPath);
// 筛选出所有文件夹
return files.filter((file) => path.extname(file) === ".json");
};
export function chunkArray<T extends object>(array: T[], chunkSize: number) {
const result = [];
let index = 0;
while (index < array.length) {
result.push(array.slice(index, index + chunkSize));
index += chunkSize;
}
return result;
}
export function intersection<T>(arr1: T[], arr2: T[]): T[] {
const setA = new Set(arr1);
const setB = new Set(arr2);
// 创建一个新的 Set 来存储交集
let intersectionSet = new Set<T>();
// 遍历较小的集合,检查每个元素是否在另一个集合中
for (let item of setA) {
if (setB.has(item)) {
intersectionSet.add(item);
}
}
return [...intersectionSet];
}
/**
* 扁平化嵌套 JSON 对象,使用点符号作为键
* @param obj 要扁平化的对象
* @param prefix 键前缀
* @returns 扁平化后的对象
*/
export const flattenJson = (
obj: any,
prefix: string = ""
): Record<string, string> => {
const flattened: Record<string, string> = {};
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
const newKey = prefix ? `${prefix}.${key}` : key;
if (
typeof obj[key] === "object" &&
obj[key] !== null &&
!Array.isArray(obj[key])
) {
// 递归处理嵌套对象
Object.assign(flattened, flattenJson(obj[key], newKey));
} else {
// 直接赋值字符串、数字等基本类型
flattened[newKey] = String(obj[key]);
}
}
}
return flattened;
};
/**
* 检测扁平化JSON中是否存在路径冲突
* @param flatObj 扁平化的对象
* @returns 是否存在冲突
*/
const hasPathConflicts = (flatObj: Record<string, string>): boolean => {
const keys = Object.keys(flatObj);
for (let i = 0; i < keys.length; i++) {
for (let j = i + 1; j < keys.length; j++) {
const key1 = keys[i];
const key2 = keys[j];
// 检查是否一个key是另一个key的前缀
if (key1.startsWith(key2 + ".") || key2.startsWith(key1 + ".")) {
return true;
}
}
}
return false;
};
/**
* 将扁平化的 JSON 重构为嵌套结构
* @param flatObj 扁平化的对象
* @returns 重构后的嵌套对象
*/
export const unflattenJson = (flatObj: Record<string, string>): any => {
// 如果存在路径冲突,直接返回扁平化结构,不进行反扁平化
if (hasPathConflicts(flatObj)) {
console.warn(
"Path conflicts detected in JSON structure, returning flattened structure"
);
return flatObj;
}
const result: any = {};
for (const key in flatObj) {
if (flatObj.hasOwnProperty(key)) {
const keys = key.split(".");
let current = result;
for (let i = 0; i < keys.length - 1; i++) {
const k = keys[i];
if (!(k in current)) {
current[k] = {};
}
current = current[k];
}
current[keys[keys.length - 1]] = flatObj[key];
}
}
return result;
};