chore: initial import
This commit is contained in:
314
scripts/sync-global-messages.js
Normal file
314
scripts/sync-global-messages.js
Normal file
@@ -0,0 +1,314 @@
|
||||
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()
|
||||
Reference in New Issue
Block a user