123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178 |
- //提取中文到zh.json和en.json文件中,方便网页的中英文切换
- import fs from 'node:fs';
- import path from 'node:path';
- import { fileURLToPath } from 'node:url';
- // 基础路径处理
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
- const args = process.argv.slice(2);
- const targetPath = args[0] ? path.resolve(__dirname, '../', args[0]) : null;
- // 语言包路径配置
- const localesDir = path.resolve(__dirname, '../src/locales');
- const zhPath = path.join(localesDir, 'zh.json');
- const enPath = path.join(localesDir, 'en.json');
- // 初始化语言包数据
- let zh = {};
- let en = {};
- // 读取已有语言包(保留已有翻译)
- try {
- if (fs.existsSync(zhPath)) {
- zh = JSON.parse(fs.readFileSync(zhPath, 'utf-8'));
- }
- if (fs.existsSync(enPath)) {
- en = JSON.parse(fs.readFileSync(enPath, 'utf-8'));
- }
- } catch (e) {
- console.error('⚠️ 语言包解析错误,将使用空对象初始化', e);
- zh = {};
- en = {};
- }
- /**
- * 递归设置嵌套对象的值(处理键路径)
- * @param {Object} obj - 目标对象
- * @param {string} keyPath - 键路径(如 "a.b.c")
- * @param {string} value - 要设置的值
- */
- function setNestedValue(obj, keyPath, value) {
- const keys = keyPath.split('.');
- let current = obj;
- for (let i = 0; i < keys.length - 1; i++) {
- const key = keys[i];
- // 处理键名冲突(已存在字符串值时跳过)
- if (typeof current[key] === 'string') {
- console.warn(`⚠️ 键名冲突:"${key}" 已作为字符串存在,跳过路径 "${keyPath}"`);
- return;
- }
- // 初始化不存在的键为对象
- if (!current[key] || typeof current[key] !== 'object') {
- current[key] = {};
- }
- current = current[key];
- }
- const lastKey = keys[keys.length - 1];
- // 仅添加新键,不覆盖已有值
- if (current[lastKey] === undefined) {
- current[lastKey] = value;
- console.log(`✅ 新增翻译:${keyPath} → ${value}`);
- }
- }
- /**
- * 扫描目标路径下的所有Vue和TS文件
- * @param {string[]} fileList - 文件列表容器
- * @returns {string[]} 扫描到的文件路径列表
- */
- function scanFiles(fileList = []) {
- if (!targetPath) {
- // 未指定路径时默认扫描src目录
- const scanRoot = path.resolve(__dirname, '../src');
- scanDir(scanRoot, fileList);
- } else {
- const stats = fs.statSync(targetPath);
- if (stats.isDirectory()) {
- scanDir(targetPath, fileList);
- } else if (targetPath.endsWith('.vue') || targetPath.endsWith('.ts')) {
- fileList.push(targetPath);
- } else {
- console.warn(`⚠️ 跳过非Vue/TS文件:${targetPath}`);
- }
- }
- return fileList;
- }
- /**
- * 递归扫描目录中的文件
- * @param {string} dir - 目录路径
- * @param {string[]} fileList - 文件列表容器
- */
- function scanDir(dir, fileList) {
- const files = fs.readdirSync(dir);
- for (const file of files) {
- const fullPath = path.join(dir, file);
- // 跳过无关目录
- if (file.includes('node_modules') || file.includes('dist') || file.includes('.git')) {
- continue;
- }
- const stats = fs.statSync(fullPath);
- if (stats.isDirectory()) {
- scanDir(fullPath, fileList);
- } else if (fullPath.endsWith('.vue') || fullPath.endsWith('.ts')) {
- fileList.push(fullPath);
- }
- }
- }
- /**
- * 从文件内容中提取i18n标记文本
- * @param {string} content - 文件内容
- * @param {string} filePath - 文件路径(用于错误提示)
- */
- function extractI18nMarkers(content, filePath) {
- // 匹配格式:<!--i18n:key-->中文文本(支持换行和常见标点)
- const regex = /<!--i18n:(\S+)-->([\u4e00-\u9fa5\w\s,.,。;;!!??::()()]+?)(?=\r?\n|['"<]|$)/g;
- let match;
- while ((match = regex.exec(content)) !== null) {
- const key = match[1].trim();
- const text = match[2].trim().replace(/\s+/g, ' '); // 清理多余空格
- if (!key) {
- console.warn(`⚠️ 缺少键名(文件:${filePath}):${match[0]}`);
- continue;
- }
- if (!text) {
- console.warn(`⚠️ 缺少文本内容(文件:${filePath},键:${key})`);
- continue;
- }
- // 写入语言包
- setNestedValue(zh, key, text);
- setNestedValue(en, key, en[key] || ''); // 英文保留已有翻译,否则留空
- }
- }
- /**
- * 执行提取流程
- */
- function runExtraction() {
- const files = scanFiles();
-
- if (files.length === 0) {
- console.log('⚠️ 未找到任何需要处理的Vue/TS文件');
- return;
- }
- // 处理所有扫描到的文件
- files.forEach(file => {
- try {
- const content = fs.readFileSync(file, 'utf-8');
- extractI18nMarkers(content, file);
- } catch (e) {
- console.error(`⚠️ 处理文件失败:${file}`, e);
- }
- });
- // 确保语言包目录存在
- if (!fs.existsSync(localesDir)) {
- fs.mkdirSync(localesDir, { recursive: true });
- }
- // 写入语言包文件
- fs.writeFileSync(zhPath, JSON.stringify(zh, null, 2), 'utf-8');
- fs.writeFileSync(enPath, JSON.stringify(en, null, 2), 'utf-8');
- console.log(`\n✅ 提取完成!共处理 ${files.length} 个文件`);
- console.log(`📄 中文语言包:${zhPath}`);
- console.log(`📄 英文语言包:${enPath}`);
- }
- // 启动提取
- runExtraction();
-
|