i18n-sheet-convert
Version:
一个用于在i18n JSON文件和Excel文件之间进行转换的工具
148 lines (147 loc) • 5.47 kB
JavaScript
import fs from 'fs';
import path from 'path';
import xlsx from 'xlsx';
class I18nConverter {
constructor(options) {
this.localesPath = options.localesPath;
this.outputPath = options.outputPath;
// 确保输出目录存在
if (!fs.existsSync(this.outputPath)) {
fs.mkdirSync(this.outputPath, { recursive: true });
}
// 如果没有提供语言配置,则自动检测
if (!options.languages) {
this.languages = this.detectLanguages();
}
else {
this.languages = options.languages;
}
}
// 自动检测语言文件
detectLanguages() {
const files = fs.readdirSync(this.localesPath);
const languages = [];
for (const file of files) {
if (file.endsWith('.json')) {
const code = file.replace('.json', '');
languages.push({
code,
name: code // 使用文件名作为显示名称
});
}
}
if (languages.length === 0) {
throw new Error('未在本地化目录中找到语言文件');
}
return languages;
}
// 将扁平的对象转换为带有点符号的键
flattenObject(obj, prefix = '') {
return Object.keys(obj).reduce((acc, key) => {
const prefixedKey = prefix ? `${prefix}.${key}` : key;
if (typeof obj[key] === 'object' && obj[key] !== null) {
Object.assign(acc, this.flattenObject(obj[key], prefixedKey));
}
else {
acc[prefixedKey] = obj[key];
}
return acc;
}, {});
}
// 将带点符号的键转换回嵌套对象
unflattenObject(obj) {
const result = {};
for (const key in obj) {
const keys = key.split('.');
let current = result;
for (let i = 0; i < keys.length; i++) {
const k = keys[i];
if (i === keys.length - 1) {
current[k] = obj[key];
}
else {
current[k] = current[k] || {};
current = current[k];
}
}
}
return result;
}
// JSON 转换为 Excel
async jsonToExcel() {
try {
// 读取所有语言的 JSON 文件
const translations = {};
for (const lang of this.languages) {
const content = JSON.parse(fs.readFileSync(path.join(this.localesPath, `${lang.code}.json`), 'utf8'));
translations[lang.code] = this.flattenObject(content);
}
// 获取所有翻译键
const allKeys = new Set();
for (const langData of Object.values(translations)) {
Object.keys(langData).forEach(key => allKeys.add(key));
}
// 创建数据数组
const data = Array.from(allKeys).map(key => {
const entry = { key };
for (const lang of this.languages) {
entry[lang.code] = translations[lang.code][key] || '';
}
return entry;
});
// 创建工作簿
const wb = xlsx.utils.book_new();
const ws = xlsx.utils.json_to_sheet(data);
// 设置列宽
const colWidths = [
{ wch: 40 }, // 键名列宽
...this.languages.map(() => ({ wch: 50 })) // 每种语言的列宽
];
ws['!cols'] = colWidths;
// 添加工作表到工作簿
xlsx.utils.book_append_sheet(wb, ws, '翻译');
// 写入文件
xlsx.writeFile(wb, path.join(this.outputPath, 'translations.xlsx'));
console.log('Excel文件生成成功!');
}
catch (error) {
console.error('生成Excel文件时出错:', error);
throw error;
}
}
// Excel 转换为 JSON
async excelToJson() {
try {
// 读取 Excel 文件
const workbook = xlsx.readFile(path.join(this.outputPath, 'translations.xlsx'));
const worksheet = workbook.Sheets[workbook.SheetNames[0]];
const data = xlsx.utils.sheet_to_json(worksheet);
// 为每种语言创建数据对象
const languageData = {};
for (const lang of this.languages) {
languageData[lang.code] = {};
}
// 填充数据
data.forEach((row) => {
if (row.key) {
for (const lang of this.languages) {
if (row[lang.code]) {
languageData[lang.code][row.key] = row[lang.code];
}
}
}
});
// 转换回嵌套结构并写入文件
for (const lang of this.languages) {
const jsonData = this.unflattenObject(languageData[lang.code]);
fs.writeFileSync(path.join(this.localesPath, `${lang.code}.json`), JSON.stringify(jsonData, null, 2), 'utf8');
}
console.log('JSON文件生成成功!');
}
catch (error) {
console.error('生成JSON文件时出错:', error);
throw error;
}
}
}
export default I18nConverter;