UNPKG

vue-translate-auto

Version:

Vue 国际化自动翻译工具 - 自动提取 $t 包裹的字符串并生成多语言文件,支持增量翻译和多种翻译API

237 lines (197 loc) 9.57 kB
const fs = require('fs'); const path = require('path'); const {getConfig} = require('./utils'); const Translator = require('./translator'); // 递归遍历目录 function walkDir(dir) { const files = fs.readdirSync(dir); return files.reduce((acc, file) => { const filePath = path.join(dir, file); const fileStat = fs.statSync(filePath); var fileSuffix = path.extname(file); if (fileStat.isDirectory()) { acc = acc.concat(walkDir(filePath)); } else if (fileStat.isFile() && getConfig().fileSuffix.includes(fileSuffix)) { acc.push(filePath); } return acc; }, []); } // 提取文件中的关键字包裹的字符串 function extractTStrings(file) { const content = fs.readFileSync(file, 'utf-8'); const keywords = getConfig().extractKeywords; // 根据配置的关键字动态生成正则表达式 // 使用负向前瞻确保前面不是字母、数字、下划线、$、. const keywordPattern = keywords.map(keyword => `(?<![a-zA-Z0-9_$.])${keyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}` ).join('|'); const regex = new RegExp(`(${keywordPattern})\\(['"]([^'"]+)['"]\\)`, 'g'); let match; const matches = []; while ((match = regex.exec(content)) !== null) { matches.push(match[2]); // 第二个捕获组是字符串内容 } return matches; } // 翻译并生成多语言文件 async function generateTranslations(sourceJson, config) { if (!config.translation || !config.translation.enabled) { console.log('翻译功能未启用,跳过翻译'); return; } const translator = new Translator(config.translation); const { targetLanguages, sourceLanguage, outputDir } = config.translation; // 确保输出目录存在 if (!fs.existsSync(outputDir)) { fs.mkdirSync(outputDir, { recursive: true }); console.log(`创建输出目录: ${outputDir}`); } // 计算需要翻译的语言数量 const languagesToTranslate = targetLanguages.filter(lang => lang !== sourceLanguage); const totalLanguages = languagesToTranslate.length; if (totalLanguages === 0) { console.log('没有需要翻译的目标语言'); return; } console.log('\n🌍 开始多语言翻译流程'); console.log(`📋 配置信息:`); console.log(` • 源语言: ${sourceLanguage}`); console.log(` • 目标语言: ${languagesToTranslate.join(', ')}`); console.log(` • 输出目录: ${outputDir}`); console.log(` • 词汇总数: ${Object.keys(sourceJson).length}`); console.log('='.repeat(60)); let currentLangIndex = 0; let totalSuccessCount = 0; const overallStartTime = Date.now(); // 记录整体开始时间 // 为每种目标语言生成翻译文件 for (const targetLang of targetLanguages) { if (targetLang === sourceLanguage) { // 源语言直接复制 const sourceFilePath = path.join(outputDir, `${targetLang}.json`); fs.writeFileSync(sourceFilePath, JSON.stringify(sourceJson, null, 2), 'utf-8'); console.log(`\n📁 生成源语言文件: ${sourceFilePath}`); continue; } currentLangIndex++; const langProgress = ((currentLangIndex / totalLanguages) * 100).toFixed(1); console.log(`\n🎯 [${langProgress}%] (${currentLangIndex}/${totalLanguages}) 开始翻译到 ${targetLang}...`); console.log('─'.repeat(60)); try { const targetFilePath = path.join(outputDir, `${targetLang}.json`); // 读取已有的翻译文件(如果存在) let existingTranslations = {}; if (fs.existsSync(targetFilePath)) { try { const existingContent = fs.readFileSync(targetFilePath, 'utf-8'); existingTranslations = JSON.parse(existingContent); console.log(`📖 读取已有翻译文件: ${Object.keys(existingTranslations).length} 个词汇`); } catch (error) { console.log(`⚠️ 读取已有翻译文件失败,将重新创建: ${error.message}`); } } // 找出需要翻译的新词汇(不在已有翻译中的) const newWords = {}; const skippedCount = 0; let newWordCount = 0; for (const [key, value] of Object.entries(sourceJson)) { if (existingTranslations.hasOwnProperty(key)) { // 已翻译过,跳过 } else { // 新词汇,需要翻译 newWords[key] = value; newWordCount++; } } console.log(`📊 翻译统计:`); console.log(` • 总词汇数: ${Object.keys(sourceJson).length}`); console.log(` • 已翻译: ${Object.keys(existingTranslations).length}`); console.log(` • 需要翻译: ${newWordCount}`); let translatedJson = { ...existingTranslations }; // 保留已有翻译 let duration = 0; let successCount = Object.keys(existingTranslations).length; if (newWordCount > 0) { // 只翻译新词汇 const result = await translator.translateBatch(newWords, targetLang, sourceLanguage); const newTranslations = result.results; duration = result.totalTime; // 合并新翻译到已有翻译中 translatedJson = { ...existingTranslations, ...newTranslations }; // 统计新增的成功翻译数 const newSuccessCount = Object.values(newTranslations).filter(text => { const originalKey = Object.keys(newWords).find(key => newWords[key] === text); return text !== newWords[originalKey]; }).length; successCount = Object.keys(existingTranslations).length + newSuccessCount; } else { console.log(`✅ 所有词汇都已翻译,无需重新翻译`); } // 写入完整的翻译文件 fs.writeFileSync(targetFilePath, JSON.stringify(translatedJson, null, 2), 'utf-8'); totalSuccessCount += successCount; console.log(`\n✅ 翻译完成: ${targetLang}`); if (newWordCount > 0) { console.log(` • 耗时: ${duration}秒`); console.log(` • 新增翻译: ${newWordCount}`); } console.log(` • 总翻译数: ${successCount}/${Object.keys(sourceJson).length}`); console.log(` • 文件路径: ${targetFilePath}`); } catch (error) { console.error(`❌ 翻译到 ${targetLang} 失败:`, error.message); } } // 计算整体耗时 const overallEndTime = Date.now(); const overallTotalTime = ((overallEndTime - overallStartTime) / 1000).toFixed(1); // 显示整体翻译统计 console.log('\n' + '='.repeat(60)); console.log('🎊 多语言翻译流程完成!'); console.log('📊 整体统计:'); console.log(` • 目标语言数: ${totalLanguages}`); console.log(` • 总词汇数: ${Object.keys(sourceJson).length}`); console.log(` • 总成功翻译: ${totalSuccessCount}`); console.log(` • 平均成功率: ${((totalSuccessCount / (totalLanguages * Object.keys(sourceJson).length)) * 100).toFixed(1)}%`); console.log(` • 整体耗时: ${overallTotalTime}秒`); console.log('='.repeat(60)); } // 主函数 async function main() { const srcDir = path.resolve(process.cwd(), getConfig().filePath); const files = walkDir(srcDir); const allMatches = []; files.forEach(file => { const matches = extractTStrings(file); if (matches.length > 0) { allMatches.push(...matches); } }); // 构建翻译对象 let jsonObj = {} allMatches.forEach(match => { jsonObj[match] = match }); console.log(`📋 提取到 ${Object.keys(jsonObj).length} 个需要翻译的词汇`); // 检查是否启用翻译功能 const config = getConfig(); if (config.translation && config.translation.enabled) { // 启用翻译功能,生成翻译文件 console.log('\n开始生成翻译文件...'); await generateTranslations(jsonObj, config); console.log('\n翻译完成!'); } else { // 未启用翻译功能,只生成词汇提取文件 const outputPath = path.resolve(process.cwd(), config.targetPath || 'src/lang.json'); const outputDir = path.dirname(outputPath); // 确保输出目录存在 if (!fs.existsSync(outputDir)) { fs.mkdirSync(outputDir, { recursive: true }); } // 写入词汇提取文件 fs.writeFileSync(outputPath, JSON.stringify(jsonObj, null, 2), 'utf-8'); console.log(`\n✅ 词汇提取完成!`); console.log(` • 文件路径: ${outputPath}`); console.log(` • 词汇数量: ${Object.keys(jsonObj).length}`); console.log(` • 如需翻译,请在配置中启用 translation.enabled = true`); } } main();