const fs = require('fs') const path = require('path') const vm = require('vm') const projectRoot = path.resolve(__dirname, '..') const messagesFile = path.join(projectRoot, 'locales', 'messages.js') const targetRoots = [ path.join(projectRoot, 'pages'), path.join(projectRoot, 'pkgAccount'), path.join(projectRoot, 'pkgBooking'), path.join(projectRoot, 'pkgExplore'), path.join(projectRoot, 'pkgService') ] const ignoredPaths = ['.history', 'unpackage', 'node_modules'] function walk(dir, files = []) { const entries = fs.readdirSync(dir, { withFileTypes: true }) for (const entry of entries) { const fullPath = path.join(dir, entry.name) if (ignoredPaths.some((segment) => fullPath.includes(`${path.sep}${segment}${path.sep}`))) { continue } if (entry.isDirectory()) { walk(fullPath, files) continue } if (entry.isFile() && fullPath.endsWith('.vue')) { files.push(fullPath) } } return files } function createBaseMessages() { return { zh: { navigation: { routes: { 'pages/index/index': '绿居公寓', 'pkgAccount/login/login': '登录', 'pkgAccount/register/register': '注册账号', 'pkgAccount/auth_result/auth_result': '实名结果', 'pkgAccount/registration_records/registration_records': '注册记录', 'pkgBooking/book_viewing/book_viewing': '预约看房', 'pkgBooking/book/book': '预订确认', 'pkgBooking/pay/pay': '在线支付', 'pkgBooking/booking_detail/booking_detail': '预订详情', 'pkgBooking/booking_list/booking_list': '预订记录', 'pkgService/application_detail/application_detail': '申请详情', 'pkgService/my_application/my_application': '我的申请', 'pkgService/my_contract/my_contract': '我的合同', 'pkgService/contract_detail/contract_detail': '合同详情', 'pkgService/contract_sign/contract_sign': '合同签约', 'pkgService/message_detail/message_detail': '消息详情', 'pkgService/submit_request/submit_request': '我要提交', 'pkgService/service_stub/service_stub': '业务办理', 'pkgService/utility_recharge/utility_recharge': '水电充值', 'pkgService/smart_control/smart_control': '智能控制', 'pkgService/device_control/device_control': '设备控制', 'pkgExplore/filter/filter': '数据筛选', 'pkgExplore/sign/sign': '签到打卡', 'pkgExplore/download/download': '资料图库', 'pkgExplore/room_detail/room_detail': '房型详情', 'pkgExplore/store_detail/store_detail': '小区详情', 'pkgExplore/vr_preview/vr_preview': 'VR看房' } }, pkgAccount: { registration_records: { navTitle: '注册记录', providerUnavailableTitle: '当前无法获取微信身份', providerUnavailableDesc: '请在微信小程序中打开后,再查看当前微信账号提交的注册记录。', registrationFallbackTitle: '人员注册申请', accountLabel: '注册账号', phoneLabel: '手机号', idNumberLabel: '证件号', submittedAtLabel: '提交时间', approvedAtLabel: '审批时间', rejectReasonLabel: '驳回原因', approvalRemarkLabel: '审批意见', loadingMore: '加载中...', noMore: '没有更多记录了', pullUpMore: '上拉加载更多', emptyTitle: '暂无注册记录', emptyDesc: '当前微信账号还没有提交过人员注册申请。', goRegister: '去提交注册申请', loadingTitle: '注册记录加载中', loadingDesc: '正在同步当前微信账号提交的注册申请记录,请稍候。', statusAll: '全部', statusPending: '审核中', statusApproved: '已通过', statusRejected: '已驳回', statusProcessing: '处理中', loadError: '注册记录加载失败' } } }, en: { navigation: { routes: { 'pages/index/index': 'Luju Apartments', 'pkgAccount/login/login': 'Sign In', 'pkgAccount/register/register': 'Register', 'pkgAccount/auth_result/auth_result': 'Verification Result', 'pkgAccount/registration_records/registration_records': 'Registration Records', 'pkgBooking/book_viewing/book_viewing': 'Book Viewing', 'pkgBooking/book/book': 'Booking Confirm', 'pkgBooking/pay/pay': 'Online Payment', 'pkgBooking/booking_detail/booking_detail': 'Booking Detail', 'pkgBooking/booking_list/booking_list': 'Booking Records', 'pkgService/application_detail/application_detail': 'Application Detail', 'pkgService/my_application/my_application': 'My Applications', 'pkgService/my_contract/my_contract': 'My Contracts', 'pkgService/contract_detail/contract_detail': 'Contract Detail', 'pkgService/contract_sign/contract_sign': 'Contract Signing', 'pkgService/message_detail/message_detail': 'Message Detail', 'pkgService/submit_request/submit_request': 'Submit Request', 'pkgService/service_stub/service_stub': 'Service Center', 'pkgService/utility_recharge/utility_recharge': 'Utility Recharge', 'pkgService/smart_control/smart_control': 'Smart Control', 'pkgService/device_control/device_control': 'Device Control', 'pkgExplore/filter/filter': 'Filter', 'pkgExplore/sign/sign': 'Check In', 'pkgExplore/download/download': 'Gallery', 'pkgExplore/room_detail/room_detail': 'Room Detail', 'pkgExplore/store_detail/store_detail': 'Community Detail', 'pkgExplore/vr_preview/vr_preview': 'VR Viewing' } }, pkgAccount: { registration_records: { navTitle: 'Registration Records', providerUnavailableTitle: 'Unable to access Wechat identity', providerUnavailableDesc: 'Please open this page in Wechat Mini Program to view registration records submitted by this Wechat account.', registrationFallbackTitle: 'Registration Application', accountLabel: 'Account', phoneLabel: 'Phone', idNumberLabel: 'ID Number', submittedAtLabel: 'Submitted At', approvedAtLabel: 'Approved At', rejectReasonLabel: 'Rejection Reason', approvalRemarkLabel: 'Review Notes', loadingMore: 'Loading...', noMore: 'No more records', pullUpMore: 'Pull up to load more', emptyTitle: 'No registration records', emptyDesc: 'This Wechat account has not submitted any registration application yet.', goRegister: 'Submit Registration', loadingTitle: 'Loading registration records', loadingDesc: 'Syncing registration records submitted by this Wechat account. Please wait.', statusAll: 'All', statusPending: 'Under Review', statusApproved: 'Approved', statusRejected: 'Rejected', statusProcessing: 'Processing', loadError: 'Failed to load registration records' } } } } } function extractObjectLiteral(source, startIndex) { const objectStart = source.indexOf('{', startIndex) if (objectStart < 0) { return '' } let depth = 0 let inString = false let stringQuote = '' let escaped = false for (let index = objectStart; index < source.length; index += 1) { const char = source[index] if (inString) { if (escaped) { escaped = false } else if (char === '\\') { escaped = true } else if (char === stringQuote) { inString = false stringQuote = '' } continue } if (char === '\'' || char === '"' || char === '`') { inString = true stringQuote = char continue } if (char === '{') { depth += 1 continue } if (char === '}') { depth -= 1 if (depth === 0) { return source.slice(objectStart, index + 1) } } } return '' } function extractI18nObject(source) { const marker = 'i18n:' const markerIndex = source.indexOf(marker) if (markerIndex < 0) { return null } const objectLiteral = extractObjectLiteral(source, markerIndex) if (!objectLiteral) { return null } try { return vm.runInNewContext(`(${objectLiteral})`) } catch (error) { return null } } function setNestedValue(target, namespace, value) { const segments = namespace.split('.').filter(Boolean) const lastSegment = segments.pop() let current = target for (const segment of segments) { if (!current[segment] || typeof current[segment] !== 'object') { current[segment] = {} } current = current[segment] } current[lastSegment] = value } function getNamespace(filePath) { const relativePath = path.relative(projectRoot, filePath).replace(/\\/g, '/') return relativePath.replace(/\.vue$/, '').replace(/\//g, '.') } function updateTranslatorMethod(source, namespace) { return source .replace( /t\s*\(\s*key\s*,\s*params\s*\)\s*\{\s*return this\.\$translate\(this\.i18n,\s*key,\s*params\)\s*\}/m, `t(key, params) {\n return this.$tm('${namespace}', key, params)\n }` ) .replace( /t\s*\(\s*key\s*\)\s*\{\s*return this\.\$translate\(this\.i18n,\s*key\)\s*\}/m, `t(key) {\n return this.$tm('${namespace}', key)\n }` ) } function sortObject(value) { if (Array.isArray(value)) { return value.map(sortObject) } if (!value || typeof value !== 'object') { return value } return Object.keys(value).sort().reduce((result, key) => { result[key] = sortObject(value[key]) return result }, {}) } function toJsLiteral(value, indent = 0) { const spaces = ' '.repeat(indent) const childSpaces = ' '.repeat(indent + 1) if (Array.isArray(value)) { if (!value.length) { return '[]' } return `[\n${value.map(item => `${childSpaces}${toJsLiteral(item, indent + 1)}`).join(',\n')}\n${spaces}]` } if (value && typeof value === 'object') { const entries = Object.entries(value) if (!entries.length) { return '{}' } return `{\n${entries.map(([key, child]) => { const renderedKey = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key) ? key : JSON.stringify(key) return `${childSpaces}${renderedKey}: ${toJsLiteral(child, indent + 1)}` }).join(',\n')}\n${spaces}}` } return JSON.stringify(value) } function writeMessagesModule(filePath, messages) { const content = `const messages = ${toJsLiteral(sortObject(messages))}\n\nexport default messages\n` fs.writeFileSync(filePath, content, 'utf8') } function main() { const messages = createBaseMessages() const files = targetRoots.flatMap(root => walk(root)) for (const filePath of files) { const source = fs.readFileSync(filePath, 'utf8') const i18nObject = extractI18nObject(source) if (!i18nObject || !i18nObject.zh || !i18nObject.en) { continue } const namespace = getNamespace(filePath) setNestedValue(messages.zh, namespace, i18nObject.zh) setNestedValue(messages.en, namespace, i18nObject.en) const nextSource = updateTranslatorMethod(source, namespace) if (nextSource !== source) { fs.writeFileSync(filePath, nextSource, 'utf8') } } writeMessagesModule(messagesFile, messages) } main()