Files
clawpanel/scripts/gen-locales.cjs
晴天 f8af3bea4a feat(i18n): full i18n for all pages + sidebar lang switcher + zh-TW locale
- All pages now use t() for internationalization
- Sidebar footer: searchable upward dropdown language switcher
- Generated zh-TW.json (Traditional Chinese) via gen-locales.cjs
- CSS for lang switcher with mobile/collapsed sidebar support
- Removed language toggle from settings page
2026-03-24 18:51:36 +08:00

259 lines
14 KiB
JavaScript

/**
* Generate zh-TW / ja / ko locale files from zh-CN.json and en.json
* Run: node scripts/gen-locales.js
*/
const fs = require('fs')
const path = require('path')
const zhCN = JSON.parse(fs.readFileSync(path.join(__dirname, '../src/locales/zh-CN.json'), 'utf8'))
const en = JSON.parse(fs.readFileSync(path.join(__dirname, '../src/locales/en.json'), 'utf8'))
// ========== Simplified → Traditional Chinese mapping ==========
// Covers common UI characters; context-dependent chars handled via phrase table first
const PHRASE_MAP = {
'里面': '裡面', '里程': '裡程', '这里': '這裡', '哪里': '哪裡',
'复制': '複製', '恢复': '恢復', '重复': '重複', '回复': '回覆',
'头发': '頭髮', '发送': '發送', '发现': '發現', '发布': '發佈', '发起': '發起', '发生': '發生', '发出': '發出', '触发': '觸發',
'信息': '資訊', '系统': '系統', '干预': '干預',
'面板': '面板', '仪表盘': '儀表盤', '仪表板': '儀表板',
'只读': '唯讀',
'默认': '預設', '并发': '並行', '并且': '並且',
'采集': '採集', '采用': '採用',
'准确': '準確', '准备': '準備', '标准': '標準',
'获取': '取得', '获得': '取得',
'运行': '執行', '运营': '營運', '运输': '運輸',
'模拟': '模擬', '类型': '類型',
'历史': '歷史', '日历': '日曆',
'存储': '儲存', '内存': '記憶體',
'鼠标': '滑鼠', '光标': '游標',
'网络': '網路', '联网': '聯網',
'视频': '影片', '音频': '音訊',
'软件': '軟體', '硬件': '硬體',
'程序': '程式', '应用程序': '應用程式',
'文件': '檔案', '文件夹': '資料夾',
'数据': '資料', '数据库': '資料庫',
'用户': '使用者', '客户端': '用戶端',
'服务器': '伺服器', '服务端': '伺服端',
'日志': '日誌', '博客': '部落格',
'功能': '功能', '智能': '智慧',
'支持': '支援', '支持的': '支援的',
'响应': '回應',
'优先': '優先', '优化': '最佳化',
'后台': '後台', '后端': '後端', '后续': '後續', '之后': '之後', '然后': '然後', '最后': '最後',
'前台': '前台', '前端': '前端',
'拖拽': '拖曳', '链接': '連結',
'字节': '位元組',
'适配': '適配', '兼容': '相容',
'注册': '註冊', '登录': '登入', '退出登录': '登出',
'下载': '下載', '上传': '上傳',
'启动': '啟動', '激活': '啟用',
'调试': '除錯', '调用': '呼叫',
'确认': '確認', '确定': '確定',
'扩展': '擴充', '插件': '外掛',
'渠道': '頻道', '频道': '頻道',
'任务': '任務', '计划': '計畫', '定时': '定時',
'签名': '簽章', '令牌': '權杖',
'权限': '權限', '授权': '授權',
'工具': '工具', '终端': '終端',
'实例': '執行個體', '进程': '處理程序',
'消息': '訊息', '通知': '通知',
'正在': '正在', '成功': '成功', '失败': '失敗',
'警告': '警告', '错误': '錯誤', '异常': '異常',
'加载': '載入', '刷新': '重新整理',
'编辑': '編輯', '删除': '刪除', '创建': '建立',
'修改': '修改', '更新': '更新', '添加': '新增',
'搜索': '搜尋', '查找': '尋找', '过滤': '篩選', '筛选': '篩選',
'排序': '排序', '升序': '升冪', '降序': '降冪',
'备份': '備份', '还原': '還原',
'配置': '設定', '设置': '設定', '选项': '選項',
'参数': '參數', '属性': '屬性',
'代理': '代理', '网关': '閘道器',
'密码': '密碼', '密钥': '金鑰',
'地址': '位址', '端口': '連接埠',
'状态': '狀態', '在线': '線上', '离线': '離線',
'连接': '連線', '断开': '斷開', '超时': '逾時',
'请求': '請求', '接口': '介面',
'缓存': '快取', '队列': '佇列',
'全局': '全域', '局部': '區域',
'模型': '模型', '会话': '對話', '对话': '對話',
'提示词': '提示詞', '提示': '提示',
'回答': '回答', '问题': '問題',
'输入': '輸入', '输出': '輸出',
'复选': '核取', '单选': '單選', '勾选': '勾選',
'折叠': '摺疊', '展开': '展開',
'上限': '上限', '下限': '下限',
'升级': '升級', '降级': '降級',
'安装': '安裝', '卸载': '解除安裝',
'绑定': '綁定', '解绑': '解除綁定',
'拦截': '攔截', '转发': '轉發',
'遥测': '遙測', '监控': '監控',
'格式': '格式', '模板': '範本',
'变量': '變數', '常量': '常數',
'关闭': '關閉', '打开': '開啟',
'隐藏': '隱藏', '显示': '顯示',
'禁用': '停用', '启用': '啟用',
'允许': '允許', '拒绝': '拒絕',
'取消': '取消', '保存': '儲存',
'重试': '重試', '跳过': '略過',
'已知': '已知', '未知': '未知',
'手动': '手動', '自动': '自動',
'已完成': '已完成', '进行中': '進行中', '待处理': '待處理',
'暂无': '暫無', '暂停': '暫停',
'粘贴': '貼上', '剪切': '剪下',
'选择': '選擇', '当前': '目前',
'已选': '已選', '全选': '全選',
'最近': '最近', '最新': '最新',
'详情': '詳情', '详细': '詳細',
'简介': '簡介', '描述': '描述',
'名称': '名稱', '标题': '標題', '标签': '標籤',
'注意': '注意', '说明': '說明',
'总量': '總量', '总计': '總計',
'费用': '費用', '价格': '價格',
'文档': '文件',
'记忆': '記憶', '记录': '紀錄',
'独立': '獨立', '共享': '共用',
'工作区': '工作區',
'无限制': '無限制',
'已停止': '已停止',
'运行中': '執行中',
'挂载': '掛載', '卸载': '卸載',
}
// Single character fallback mapping (only applied after phrase mapping)
const CHAR_MAP = {
'与': '與', '万': '萬', '专': '專', '业': '業', '丢': '丟', '两': '兩',
'严': '嚴', '个': '個', '丰': '豐', '临': '臨', '为': '為', '举': '舉',
'义': '義', '乐': '樂', '习': '習', '书': '書', '买': '買', '乱': '亂',
'争': '爭', '于': '於', '亏': '虧', '云': '雲', '亚': '亞', '产': '產',
'亲': '親', '仅': '僅', '从': '從', '仓': '倉', '们': '們', '价': '價',
'众': '眾', '优': '優', '会': '會', '伟': '偉', '传': '傳', '伤': '傷',
'体': '體', '佣': '傭', '余': '餘', '侠': '俠', '侧': '側', '侦': '偵',
'债': '債', '值': '值', '偿': '償', '像': '像', '允': '允', '兆': '兆',
'党': '黨', '兰': '蘭', '关': '關', '兴': '興', '养': '養', '内': '內',
'冲': '沖', '决': '決', '况': '況', '净': '淨', '准': '準', '凑': '湊',
'减': '減', '几': '幾', '凭': '憑', '出': '出', '击': '擊', '划': '劃',
'创': '創', '别': '別', '刘': '劉', '则': '則', '刚': '剛', '剂': '劑',
'剧': '劇', '剩': '剩', '劝': '勸', '办': '辦', '动': '動', '务': '務',
'劳': '勞', '势': '勢', '勋': '勳', '匀': '勻', '区': '區', '医': '醫',
'华': '華', '单': '單', '卖': '賣', '占': '佔', '卫': '衛', '厂': '廠',
'厅': '廳', '历': '歷', '压': '壓', '厉': '厲', '县': '縣', '参': '參',
'双': '雙', '发': '發', '变': '變', '叙': '敘', '叶': '葉', '号': '號',
'叹': '嘆', '吓': '嚇', '吕': '呂', '听': '聽', '启': '啟', '呐': '吶',
'员': '員', '响': '響', '哑': '啞', '唤': '喚', '啬': '嗇', '团': '團',
'园': '園', '围': '圍', '国': '國', '图': '圖', '圣': '聖', '场': '場',
'块': '塊', '坏': '壞', '坚': '堅', '坛': '壇', '垒': '壘', '垄': '壟',
'型': '型', '域': '域', '堕': '墮', '塑': '塑', '墙': '牆', '壮': '壯',
'声': '聲', '处': '處', '备': '備', '够': '夠', '头': '頭', '夹': '夾',
'夺': '奪', '奋': '奮', '奖': '獎', '奥': '奧', '妇': '婦', '妈': '媽',
'娱': '娛', '孙': '孫', '学': '學', '宝': '寶', '实': '實', '宠': '寵',
'审': '審', '宪': '憲', '宽': '寬', '将': '將', '尔': '爾', '尘': '塵',
'层': '層', '岁': '歲', '岂': '豈', '岛': '島', '岭': '嶺', '岸': '岸',
'币': '幣', '帅': '帥', '师': '師', '帐': '帳', '帜': '幟', '带': '帶',
'帮': '幫', '干': '乾', '并': '並', '广': '廣', '庄': '莊', '庆': '慶',
'库': '庫', '应': '應', '废': '廢', '开': '開', '异': '異', '弃': '棄',
'张': '張', '弹': '彈', '归': '歸', '当': '當', '录': '錄', '彻': '徹',
'径': '徑', '态': '態', '怀': '懷', '总': '總', '恶': '惡', '恼': '惱',
'悬': '懸', '惊': '驚', '惧': '懼', '惩': '懲', '惯': '慣', '愤': '憤',
'慎': '慎', '懒': '懶', '戏': '戲', '战': '戰', '户': '戶', '执': '執',
'扩': '擴', '扫': '掃', '扬': '揚', '扰': '擾', '抚': '撫', '抛': '拋',
'担': '擔', '拟': '擬', '拥': '擁', '择': '擇', '挂': '掛', '挡': '擋',
'挤': '擠', '挥': '揮', '损': '損', '换': '換', '据': '據', '掷': '擲',
'描': '描', '摄': '攝', '摆': '擺', '摇': '搖', '操': '操', '撑': '撐',
'撤': '撤', '播': '播', '擅': '擅', '数': '數', '整': '整', '斗': '鬥',
'斩': '斬', '断': '斷', '无': '無', '旧': '舊', '时': '時', '昼': '晝',
'显': '顯', '晋': '晉', '晒': '曬', '术': '術', '机': '機', '权': '權',
'杀': '殺', '杂': '雜', '条': '條', '来': '來', '极': '極', '构': '構',
'柜': '櫃', '标': '標', '栈': '棧', '样': '樣', '检': '檢', '楼': '樓',
'榄': '欖', '横': '橫', '档': '檔', '桥': '橋', '梦': '夢', '毁': '毀',
'殇': '殤', '残': '殘', '毕': '畢', '汇': '匯', '汉': '漢', '污': '汙',
'汤': '湯', '沟': '溝', '没': '沒', '泪': '淚', '浅': '淺', '测': '測',
'济': '濟', '浏': '瀏', '涌': '湧', '涛': '濤', '润': '潤', '涨': '漲',
'淀': '澱', '渐': '漸', '渠': '渠', '温': '溫', '湾': '灣', '滞': '滯',
'满': '滿', '滤': '濾', '潜': '潛', '灭': '滅', '灵': '靈', '灿': '燦',
'烂': '爛', '烛': '燭', '烦': '煩', '烧': '燒', '热': '熱', '犹': '猶',
'独': '獨', '狭': '狹', '猎': '獵', '猪': '豬', '献': '獻', '玩': '玩',
'环': '環', '现': '現', '珍': '珍', '瓶': '瓶', '电': '電', '画': '畫',
'畅': '暢', '疗': '療', '症': '症', '盘': '盤', '盖': '蓋', '监': '監',
'盐': '鹽', '目': '目', '码': '碼', '础': '礎', '确': '確', '碍': '礙',
'禅': '禪', '离': '離', '种': '種', '积': '積', '称': '稱', '稳': '穩',
'穷': '窮', '窃': '竊', '窜': '竄', '窝': '窩', '竞': '競', '笔': '筆',
'签': '簽', '简': '簡', '算': '算', '类': '類', '粮': '糧', '紧': '緊',
'纠': '糾', '纤': '纖', '红': '紅', '纯': '純', '纲': '綱', '纳': '納',
'纵': '縱', '纷': '紛', '纸': '紙', '线': '線', '组': '組', '细': '細',
'织': '織', '终': '終', '绍': '紹', '经': '經', '结': '結', '绑': '綁',
'绕': '繞', '统': '統', '继': '繼', '绩': '績', '绪': '緒', '续': '續',
'综': '綜', '缓': '緩', '编': '編', '缘': '緣', '缝': '縫', '缩': '縮',
'缴': '繳', '网': '網', '罗': '羅', '罚': '罰', '翻': '翻', '耻': '恥',
'联': '聯', '肃': '肅', '肤': '膚', '胀': '脹', '胁': '脅', '脉': '脈',
'脑': '腦', '脸': '臉', '腊': '臘', '舆': '輿', '舰': '艦', '艰': '艱',
'节': '節', '芦': '蘆', '苹': '蘋', '范': '範', '荐': '薦', '药': '藥',
'获': '獲', '虑': '慮', '虚': '虛', '虽': '雖', '蚕': '蠶', '蛮': '蠻',
'蝇': '蠅', '补': '補', '衬': '襯', '袜': '襪', '装': '裝', '规': '規',
'觉': '覺', '览': '覽', '观': '觀', '角': '角', '解': '解', '触': '觸',
'言': '言', '计': '計', '订': '訂', '认': '認', '讨': '討', '让': '讓',
'议': '議', '讯': '訊', '记': '記', '讲': '講', '许': '許', '论': '論',
'设': '設', '访': '訪', '证': '證', '评': '評', '识': '識', '词': '詞',
'试': '試', '话': '話', '该': '該', '详': '詳', '语': '語', '误': '誤',
'说': '說', '请': '請', '诸': '諸', '读': '讀', '课': '課', '调': '調',
'谁': '誰', '谈': '談', '谢': '謝', '谱': '譜', '贝': '貝', '负': '負',
'贡': '貢', '财': '財', '责': '責', '质': '質', '贴': '貼', '费': '費',
'资': '資', '赋': '賦', '赏': '賞', '赛': '賽', '赞': '贊', '趋': '趨',
'跃': '躍', '践': '踐', '转': '轉', '轨': '軌', '载': '載', '较': '較',
'辅': '輔', '辑': '輯', '输': '輸', '辩': '辯', '边': '邊', '达': '達',
'过': '過', '迁': '遷', '运': '運', '进': '進', '远': '遠', '违': '違',
'连': '連', '迟': '遲', '适': '適', '选': '選', '逻': '邏', '遗': '遺',
'邮': '郵', '郑': '鄭', '释': '釋', '钉': '釘', '钟': '鐘', '钥': '鑰',
'钮': '鈕', '链': '鏈', '锁': '鎖', '锐': '銳', '错': '錯', '键': '鍵',
'锻': '鍛', '镇': '鎮', '镜': '鏡', '长': '長', '门': '門', '闭': '閉',
'问': '問', '闲': '閒', '间': '間', '阅': '閱', '阶': '階', '际': '際',
'陆': '陸', '险': '險', '随': '隨', '隐': '隱', '障': '障', '难': '難',
'集': '集', '雾': '霧', '需': '需', '静': '靜', '页': '頁', '顶': '頂',
'项': '項', '须': '須', '预': '預', '频': '頻', '题': '題', '颜': '顏',
'风': '風', '飞': '飛', '驱': '驅', '验': '驗', '骤': '驟', '鸡': '雞',
'鸣': '鳴', '鹏': '鵬', '龄': '齡', '龙': '龍',
}
function s2t(text) {
if (typeof text !== 'string') return text
// Apply phrase mapping first (longer matches first)
const phrases = Object.keys(PHRASE_MAP).sort((a, b) => b.length - a.length)
let result = text
for (const phrase of phrases) {
result = result.split(phrase).join(PHRASE_MAP[phrase])
}
// Then apply single character mapping
let out = ''
for (const ch of result) {
out += CHAR_MAP[ch] || ch
}
return out
}
function transformValues(obj, fn) {
const result = {}
for (const [k, v] of Object.entries(obj)) {
if (typeof v === 'string') {
result[k] = fn(v)
} else if (typeof v === 'object' && v !== null) {
result[k] = transformValues(v, fn)
} else {
result[k] = v
}
}
return result
}
// Generate zh-TW
const zhTW = transformValues(zhCN, s2t)
fs.writeFileSync(
path.join(__dirname, '../src/locales/zh-TW.json'),
JSON.stringify(zhTW, null, 2) + '\n',
'utf8'
)
console.log('✓ zh-TW.json generated')
// Verify
const parsed = JSON.parse(fs.readFileSync(path.join(__dirname, '../src/locales/zh-TW.json'), 'utf8'))
function countKeys(o) { let n = 0; for (const v of Object.values(o)) { if (typeof v === 'string') n++; else if (typeof v === 'object') n += countKeys(v) } return n }
console.log(` ${countKeys(parsed)} keys`)