i18n-management-cli
Version:
CLI tool for i18n management platform - import, export, and sync translations
159 lines (133 loc) • 4.09 kB
JavaScript
const fs = require('fs-extra');
const path = require('path');
const { glob } = require('glob');
class FileUtils {
static async readJsonFile(filePath) {
try {
const fullPath = path.resolve(filePath);
if (!await fs.pathExists(fullPath)) {
throw new Error(`File not found: ${filePath}`);
}
return await fs.readJson(fullPath);
} catch (error) {
throw new Error(`Failed to read JSON file ${filePath}: ${error.message}`);
}
}
static async writeJsonFile(filePath, data) {
try {
const fullPath = path.resolve(filePath);
await fs.ensureDir(path.dirname(fullPath));
await fs.writeJson(fullPath, data, { spaces: 2 });
return true;
} catch (error) {
throw new Error(`Failed to write JSON file ${filePath}: ${error.message}`);
}
}
static async findJsonFiles(dir, pattern = '**/*.json') {
try {
const fullPath = path.resolve(dir);
if (!await fs.pathExists(fullPath)) {
return [];
}
const files = await glob(pattern, { cwd: fullPath });
return files.map(file => path.join(fullPath, file));
} catch (error) {
throw new Error(`Failed to find JSON files in ${dir}: ${error.message}`);
}
}
static extractLanguageFromPath(filePath) {
// 匹配 /zh/, /en/, /tw/ 等模式
const match = filePath.match(/\/([a-z]{2,3})(?:[-_][A-Z]{2})?\/[^\/]*\.json$/i);
return match ? match[1].toLowerCase() : null;
}
static generateModuleName(filePath) {
const fileName = path.basename(filePath, path.extname(filePath));
// 如果文件名是 index,使用父目录名
if (fileName === 'index') {
const parentDir = path.basename(path.dirname(filePath));
return parentDir || 'common';
}
return fileName;
}
static flattenObject(obj, prefix = '') {
const result = {};
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(result, this.flattenObject(obj[key], newKey));
} else {
result[newKey] = String(obj[key]);
}
}
}
return result;
}
static unflattenObject(obj) {
const result = {};
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
const keys = key.split('.');
let current = result;
for (let i = 0; i < keys.length - 1; i++) {
if (!current[keys[i]]) {
current[keys[i]] = {};
}
current = current[keys[i]];
}
current[keys[keys.length - 1]] = obj[key];
}
}
return result;
}
static compareObjects(local, remote) {
const localKeys = new Set(Object.keys(local));
const remoteKeys = new Set(Object.keys(remote));
const added = {};
const modified = {};
const deleted = [];
const unchanged = {};
// 查找新增和修改的键
for (const key of localKeys) {
if (!remoteKeys.has(key)) {
added[key] = local[key];
} else if (local[key] !== remote[key]) {
modified[key] = {
local: local[key],
remote: remote[key]
};
} else {
unchanged[key] = local[key];
}
}
// 查找删除的键
for (const key of remoteKeys) {
if (!localKeys.has(key)) {
deleted.push(key);
}
}
return {
added,
modified,
deleted,
unchanged
};
}
static async ensureDirectoryExists(dirPath) {
try {
await fs.ensureDir(path.resolve(dirPath));
return true;
} catch (error) {
throw new Error(`Failed to create directory ${dirPath}: ${error.message}`);
}
}
static validateJsonFile(filePath) {
try {
require(path.resolve(filePath));
return true;
} catch (error) {
throw new Error(`Invalid JSON file ${filePath}: ${error.message}`);
}
}
}
module.exports = FileUtils;