mirror of
https://github.com/qingchencloud/clawpanel.git
synced 2026-05-12 02:20:58 +08:00
i18n: - Add 9 new locale files (ja/ko/de/es/fr/pt/ru/vi/zh-TW) - Add multilingual README files for all 11 languages - Add locale helper, index, and modular translation system - Add translation generation scripts Website (docs/index.html): - Replace 公益AI接口 branding with 晴辰云AI接口 - Remove OpenClaw 独立安装包 promotion block - Update SEO meta tags (description, keywords, OG, Twitter, JSON-LD) - Add 11-language README links to footer - Update 元宝派 link to new URL Bug fixes: - fix(cron): delivery format mode:'push' → mode:'announce', remove invalid 'to' field (fixes #141) - fix(cron): allow single-channel users to select delivery channel - fix(cron): preserve delivery field in job state for editing - fix(models): add 'ollama' as recognized API type, prevent overwriting native ollama config (fixes #140) - fix(models): skip /v1 append for ollama native API baseUrl - fix(assistant): normalize 'google-generative-ai' consistently, add ollama hints - fix(version): use CLI path classification for source detection on Windows (fixes #139) - fix(version): default to 'official' instead of 'chinese' when source unknown - fix(version): reorder npm global package check based on active CLI
This commit is contained in:
165
scripts/split-locales.cjs
Normal file
165
scripts/split-locales.cjs
Normal file
@@ -0,0 +1,165 @@
|
||||
/**
|
||||
* 迁移脚本:将单体 JSON 语言包拆分为模块化 JS 文件
|
||||
* 读取 11 种语言 JSON → 生成 src/locales/modules/*.js
|
||||
*/
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
|
||||
const LOCALES_DIR = path.resolve(__dirname, '../src/locales')
|
||||
const MODULES_DIR = path.resolve(LOCALES_DIR, 'modules')
|
||||
|
||||
function readLang(file) {
|
||||
const p = path.join(LOCALES_DIR, file)
|
||||
return fs.existsSync(p) ? JSON.parse(fs.readFileSync(p, 'utf8')) : {}
|
||||
}
|
||||
|
||||
const zhCN = readLang('zh-CN.json')
|
||||
const en = readLang('en.json')
|
||||
const zhTW = readLang('zh-TW.json')
|
||||
const ja = readLang('ja.json')
|
||||
const ko = readLang('ko.json')
|
||||
const vi = readLang('vi.json')
|
||||
const es = readLang('es.json')
|
||||
const pt = readLang('pt.json')
|
||||
const ru = readLang('ru.json')
|
||||
const fr = readLang('fr.json')
|
||||
const de = readLang('de.json')
|
||||
|
||||
// 模块名映射(JSON key → 文件名)
|
||||
const MODULE_FILE_MAP = {
|
||||
common: 'common',
|
||||
sidebar: 'sidebar',
|
||||
instance: 'instance',
|
||||
dashboard: 'dashboard',
|
||||
services: 'services',
|
||||
settings: 'settings',
|
||||
models: 'models',
|
||||
agents: 'agents',
|
||||
gateway: 'gateway',
|
||||
security: 'security',
|
||||
communication: 'communication',
|
||||
channels: 'channels',
|
||||
memory: 'memory',
|
||||
cron: 'cron',
|
||||
usage: 'usage',
|
||||
skills: 'skills',
|
||||
chat: 'chat',
|
||||
chatDebug: 'chat-debug',
|
||||
setup: 'setup',
|
||||
about: 'about',
|
||||
ext: 'ext',
|
||||
logs: 'logs',
|
||||
assistant: 'assistant',
|
||||
toast: 'toast',
|
||||
modal: 'modal',
|
||||
}
|
||||
|
||||
// 确保输出目录存在
|
||||
if (!fs.existsSync(MODULES_DIR)) {
|
||||
fs.mkdirSync(MODULES_DIR, { recursive: true })
|
||||
}
|
||||
|
||||
// 转义 JS 字符串中的特殊字符
|
||||
function escapeStr(s) {
|
||||
if (typeof s !== 'string') return ''
|
||||
return s
|
||||
.replace(/\\/g, '\\\\')
|
||||
.replace(/'/g, "\\'")
|
||||
.replace(/\n/g, '\\n')
|
||||
.replace(/\r/g, '')
|
||||
}
|
||||
|
||||
// 参数顺序: zhCN, en, zhTW, ja, ko, vi, es, pt, ru, fr, de
|
||||
const LANG_ORDER = [
|
||||
{ key: 'zhCN', fallback: null },
|
||||
{ key: 'en', fallback: null },
|
||||
{ key: 'zhTW', fallback: 'zhCN' },
|
||||
{ key: 'ja', fallback: 'en' },
|
||||
{ key: 'ko', fallback: 'en' },
|
||||
{ key: 'vi', fallback: 'en' },
|
||||
{ key: 'es', fallback: 'en' },
|
||||
{ key: 'pt', fallback: 'en' },
|
||||
{ key: 'ru', fallback: 'en' },
|
||||
{ key: 'fr', fallback: 'en' },
|
||||
{ key: 'de', fallback: 'en' },
|
||||
]
|
||||
|
||||
// 生成一个模块的 JS 源码(11 语言)
|
||||
function generateModule(sectionKey, sections) {
|
||||
const keys = Object.keys(sections.zhCN)
|
||||
const lines = []
|
||||
|
||||
lines.push("import { _ } from '../helper.js'")
|
||||
lines.push('')
|
||||
lines.push('export default {')
|
||||
|
||||
for (const key of keys) {
|
||||
const vals = {}
|
||||
for (const { key: lk } of LANG_ORDER) {
|
||||
vals[lk] = (sections[lk] && sections[lk][key]) || ''
|
||||
}
|
||||
|
||||
// 计算可省略的参数(和 fallback 相同时省略)
|
||||
const params = LANG_ORDER.map(({ key: lk, fallback }) => {
|
||||
if (!fallback) return vals[lk] // zhCN, en 必填
|
||||
return vals[lk] === vals[fallback] ? '' : vals[lk]
|
||||
})
|
||||
|
||||
// 从尾部截断空参数
|
||||
let lastNonEmpty = 1 // 至少保留 zhCN + en
|
||||
for (let i = params.length - 1; i >= 2; i--) {
|
||||
if (params[i]) { lastNonEmpty = i; break }
|
||||
}
|
||||
|
||||
const parts = params.slice(0, lastNonEmpty + 1).map(v => `'${escapeStr(v)}'`)
|
||||
lines.push(` ${safeKey(key)}: _(${parts.join(', ')}),`)
|
||||
}
|
||||
|
||||
lines.push('}')
|
||||
lines.push('')
|
||||
|
||||
return lines.join('\n')
|
||||
}
|
||||
|
||||
// 确保 key 是合法的 JS 标识符,否则加引号
|
||||
function safeKey(key) {
|
||||
if (/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key)) return key
|
||||
return `'${key}'`
|
||||
}
|
||||
|
||||
let totalKeys = 0
|
||||
let fileCount = 0
|
||||
|
||||
for (const [sectionKey, fileName] of Object.entries(MODULE_FILE_MAP)) {
|
||||
const zhCNSection = zhCN[sectionKey]
|
||||
if (!zhCNSection) {
|
||||
console.warn(`⚠ Section "${sectionKey}" not found in zh-CN.json, skipping`)
|
||||
continue
|
||||
}
|
||||
|
||||
const sections = {
|
||||
zhCN: zhCNSection,
|
||||
en: en[sectionKey] || {},
|
||||
zhTW: zhTW[sectionKey] || {},
|
||||
ja: ja[sectionKey] || {},
|
||||
ko: ko[sectionKey] || {},
|
||||
vi: vi[sectionKey] || {},
|
||||
es: es[sectionKey] || {},
|
||||
pt: pt[sectionKey] || {},
|
||||
ru: ru[sectionKey] || {},
|
||||
fr: fr[sectionKey] || {},
|
||||
de: de[sectionKey] || {},
|
||||
}
|
||||
|
||||
const source = generateModule(sectionKey, sections)
|
||||
const outPath = path.join(MODULES_DIR, `${fileName}.js`)
|
||||
fs.writeFileSync(outPath, source, 'utf8')
|
||||
|
||||
const keyCount = Object.keys(zhCNSection).length
|
||||
totalKeys += keyCount
|
||||
fileCount++
|
||||
console.log(`✓ ${fileName}.js (${keyCount} keys)`)
|
||||
}
|
||||
|
||||
console.log(`\n✓ Done: ${fileCount} module files, ${totalKeys} total keys`)
|
||||
console.log(` Output: ${MODULES_DIR}`)
|
||||
Reference in New Issue
Block a user