/* eslint-disable no-console */ const fs = require('fs') const path = require('path') const projectRoot = process.cwd() const args = process.argv.slice(2) const getArg = (name, defaultValue = '') => { const full = `--${name}` const hit = args.find((item) => item.startsWith(`${full}=`)) if (hit) return hit.slice(full.length + 1) const idx = args.indexOf(full) if (idx !== -1 && args[idx + 1]) return args[idx + 1] return defaultValue } const hasFlag = (flag) => args.includes(`--${flag}`) const targetDirArg = getArg('dir', 'src/views/homePage') const replaceMode = hasFlag('replace') const targetDir = path.resolve(projectRoot, targetDirArg) const zhAutoPath = path.resolve(projectRoot, 'src/i18n/lang/zh-CN.auto.json') const enAutoPath = path.resolve(projectRoot, 'src/i18n/lang/en-US.auto.json') const chinesePattern = /[\u4e00-\u9fa5]/ const ignorePattern = /^(\s*|[-:,.(){}\[\]/\\]+)$/ const readJsonSafe = (filePath) => { if (!fs.existsSync(filePath)) return {} try { return JSON.parse(fs.readFileSync(filePath, 'utf8')) } catch (error) { console.warn(`[warn] JSON parse failed: ${filePath}`) return {} } } const writeJson = (filePath, value) => { const content = JSON.stringify(value, null, 2) + '\n' fs.writeFileSync(filePath, content, 'utf8') } const walkFiles = (dir, bucket) => { const entries = fs.readdirSync(dir, { withFileTypes: true }) entries.forEach((entry) => { const fullPath = path.join(dir, entry.name) if (entry.isDirectory()) { if (['node_modules', '.git', 'dist'].includes(entry.name)) return walkFiles(fullPath, bucket) return } if (!/\.(vue|js)$/.test(entry.name)) return bucket.push(fullPath) }) } const normalizeText = (text) => text .replace(/\s+/g, ' ') .replace(/ /g, ' ') .trim() const keyFromText = (text) => { let hash = 0 for (let i = 0; i < text.length; i += 1) { hash = (hash * 131 + text.charCodeAt(i)) >>> 0 } return `auto.k_${hash.toString(16)}` } const zhMessages = readJsonSafe(zhAutoPath) const enMessages = readJsonSafe(enAutoPath) const reverseMap = new Map() Object.keys(zhMessages).forEach((key) => { reverseMap.set(zhMessages[key], key) }) const ensureKey = (rawText) => { const text = normalizeText(rawText) if (!text || !chinesePattern.test(text) || ignorePattern.test(text)) return null if (reverseMap.has(text)) return reverseMap.get(text) let key = keyFromText(text) while (zhMessages[key] && zhMessages[key] !== text) { key = `${key}_${Math.floor(Math.random() * 10000)}` } zhMessages[key] = text if (!Object.prototype.hasOwnProperty.call(enMessages, key)) { enMessages[key] = '' } reverseMap.set(text, key) return key } const transformTemplate = (template) => { let replacedCount = 0 const textNodeRegex = />([^<>{}\n]*[\u4e00-\u9fa5][^<>{}\n]*) { const key = ensureKey(inner) if (!key || !replaceMode) return full replacedCount += 1 return `>{{ $t('${key}') }}<` }) const attrRegex = /\s([a-zA-Z_][\w-]*)=(["'])([^"']*[\u4e00-\u9fa5][^"']*)\2/g updated = updated.replace(attrRegex, (full, attr, quote, value) => { const key = ensureKey(value) if (!key || !replaceMode) return full replacedCount += 1 return ` :${attr}="$t('${key}')"` }) return { updated, replacedCount } } const processVueFile = (filePath) => { const raw = fs.readFileSync(filePath, 'utf8') const templateMatch = raw.match(/