- 添加 prompt_locale 参数支持简体中文、繁体中文和英文提示词本地化 - 移除内置 agents 配置以简化系统架构 - 更新 ContextBuilder 使用动态提示词模板而非硬编码内容 - 在 AgentLoop、Web 接口和 AgentService 中传递 locale 参数 - 添加输出语言指令确保用户界面内容按指定语言生成 - 扩展前端 LanguageSwitcher 组件支持三种语言选项 - 优化 Header 和侧边栏组件的响应式布局和文本截断处理 - 更新测试用例验证不同语言环境下的提示词正确性
586 lines
9.5 KiB
TypeScript
586 lines
9.5 KiB
TypeScript
export const APP_LOCALE_COOKIE = 'beaver_locale';
|
|
export const APP_LOCALE_STORAGE_KEY = 'beaver_locale';
|
|
|
|
export const APP_LOCALES = ['zh-CN', 'en-US', 'zh-Hant'] as const;
|
|
|
|
export type AppLocale = (typeof APP_LOCALES)[number];
|
|
|
|
export function isAppLocale(value: string | null | undefined): value is AppLocale {
|
|
return value === 'zh-CN' || value === 'en-US' || value === 'zh-Hant';
|
|
}
|
|
|
|
export function normalizeAppLocale(value?: string | null): AppLocale {
|
|
const probe = value?.trim().toLowerCase() || '';
|
|
if (probe.startsWith('en')) {
|
|
return 'en-US';
|
|
}
|
|
if (
|
|
probe === 'zh-hant' ||
|
|
probe.startsWith('zh-tw') ||
|
|
probe.startsWith('zh-hk') ||
|
|
probe.startsWith('zh-mo')
|
|
) {
|
|
return 'zh-Hant';
|
|
}
|
|
return 'zh-CN';
|
|
}
|
|
|
|
function readCookieLocale(): string | null {
|
|
if (typeof document === 'undefined') {
|
|
return null;
|
|
}
|
|
|
|
const match = document.cookie
|
|
.split('; ')
|
|
.find((item) => item.startsWith(`${APP_LOCALE_COOKIE}=`));
|
|
if (!match) {
|
|
return null;
|
|
}
|
|
return decodeURIComponent(match.slice(APP_LOCALE_COOKIE.length + 1));
|
|
}
|
|
|
|
export function readBrowserAppLocale(): AppLocale {
|
|
if (typeof window === 'undefined') {
|
|
return 'zh-CN';
|
|
}
|
|
|
|
const fromDocument = document.documentElement.lang;
|
|
if (fromDocument) {
|
|
return normalizeAppLocale(fromDocument);
|
|
}
|
|
|
|
const fromStorage = window.localStorage.getItem(APP_LOCALE_STORAGE_KEY);
|
|
if (fromStorage) {
|
|
return normalizeAppLocale(fromStorage);
|
|
}
|
|
|
|
const fromCookie = readCookieLocale();
|
|
if (fromCookie) {
|
|
return normalizeAppLocale(fromCookie);
|
|
}
|
|
|
|
return normalizeAppLocale(window.navigator.language);
|
|
}
|
|
|
|
export function persistAppLocale(locale: AppLocale): void {
|
|
if (typeof window === 'undefined') {
|
|
return;
|
|
}
|
|
|
|
document.documentElement.lang = locale;
|
|
window.localStorage.setItem(APP_LOCALE_STORAGE_KEY, locale);
|
|
document.cookie = `${APP_LOCALE_COOKIE}=${encodeURIComponent(locale)}; path=/; max-age=31536000; samesite=lax`;
|
|
}
|
|
|
|
export function getCurrentAppLocale(): AppLocale {
|
|
if (typeof window === 'undefined') {
|
|
return 'zh-CN';
|
|
}
|
|
return readBrowserAppLocale();
|
|
}
|
|
|
|
export function pickAppText<T>(locale: string | null | undefined, zhValue: T, enValue: T): T {
|
|
const appLocale = normalizeAppLocale(locale);
|
|
if (appLocale === 'en-US') {
|
|
return enValue;
|
|
}
|
|
if (appLocale === 'zh-Hant') {
|
|
return toTraditionalValue(zhValue);
|
|
}
|
|
return zhValue;
|
|
}
|
|
|
|
function toTraditionalValue<T>(value: T): T {
|
|
return typeof value === 'string' ? (toTraditionalChinese(value) as T) : value;
|
|
}
|
|
|
|
const SIMPLIFIED_TO_TRADITIONAL_PHRASES: Array<[string, string]> = [
|
|
['智能体', '智慧體'],
|
|
['Agent Team', 'Agent Team'],
|
|
];
|
|
|
|
const SIMPLIFIED_TO_TRADITIONAL_CHARS: Record<string, string> = {
|
|
个: '個',
|
|
为: '為',
|
|
么: '麼',
|
|
义: '義',
|
|
习: '習',
|
|
书: '書',
|
|
了: '了',
|
|
于: '於',
|
|
云: '雲',
|
|
产: '產',
|
|
仅: '僅',
|
|
从: '從',
|
|
仓: '倉',
|
|
仪: '儀',
|
|
们: '們',
|
|
优: '優',
|
|
会: '會',
|
|
传: '傳',
|
|
体: '體',
|
|
余: '餘',
|
|
侧: '側',
|
|
侦: '偵',
|
|
促: '促',
|
|
俩: '倆',
|
|
值: '值',
|
|
假: '假',
|
|
做: '做',
|
|
停: '停',
|
|
储: '儲',
|
|
像: '像',
|
|
儿: '兒',
|
|
先: '先',
|
|
光: '光',
|
|
关: '關',
|
|
兴: '興',
|
|
具: '具',
|
|
内: '內',
|
|
册: '冊',
|
|
写: '寫',
|
|
军: '軍',
|
|
农: '農',
|
|
况: '況',
|
|
冻: '凍',
|
|
净: '淨',
|
|
准: '準',
|
|
几: '幾',
|
|
击: '擊',
|
|
划: '劃',
|
|
则: '則',
|
|
创: '創',
|
|
初: '初',
|
|
删: '刪',
|
|
别: '別',
|
|
到: '到',
|
|
制: '製',
|
|
剂: '劑',
|
|
剩: '剩',
|
|
办: '辦',
|
|
功: '功',
|
|
加: '加',
|
|
务: '務',
|
|
动: '動',
|
|
助: '助',
|
|
势: '勢',
|
|
包: '包',
|
|
区: '區',
|
|
协: '協',
|
|
单: '單',
|
|
卖: '賣',
|
|
占: '佔',
|
|
卡: '卡',
|
|
历: '歷',
|
|
压: '壓',
|
|
厕: '廁',
|
|
厢: '廂',
|
|
县: '縣',
|
|
参: '參',
|
|
双: '雙',
|
|
发: '發',
|
|
变: '變',
|
|
叠: '疊',
|
|
号: '號',
|
|
后: '後',
|
|
向: '向',
|
|
吗: '嗎',
|
|
启: '啟',
|
|
员: '員',
|
|
命: '命',
|
|
咨: '諮',
|
|
哑: '啞',
|
|
响: '響',
|
|
唤: '喚',
|
|
问: '問',
|
|
單: '單',
|
|
喂: '餵',
|
|
器: '器',
|
|
团: '團',
|
|
园: '園',
|
|
困: '困',
|
|
图: '圖',
|
|
场: '場',
|
|
块: '塊',
|
|
坏: '壞',
|
|
址: '址',
|
|
坚: '堅',
|
|
坛: '壇',
|
|
型: '型',
|
|
垃: '垃',
|
|
域: '域',
|
|
堆: '堆',
|
|
填: '填',
|
|
增: '增',
|
|
墙: '牆',
|
|
声: '聲',
|
|
处: '處',
|
|
备: '備',
|
|
复: '復',
|
|
够: '夠',
|
|
头: '頭',
|
|
奖: '獎',
|
|
好: '好',
|
|
如: '如',
|
|
始: '始',
|
|
委: '委',
|
|
存: '存',
|
|
学: '學',
|
|
宁: '寧',
|
|
它: '它',
|
|
安: '安',
|
|
完: '完',
|
|
实: '實',
|
|
审: '審',
|
|
客: '客',
|
|
宪: '憲',
|
|
宽: '寬',
|
|
对: '對',
|
|
导: '導',
|
|
将: '將',
|
|
尔: '爾',
|
|
尝: '嘗',
|
|
层: '層',
|
|
属: '屬',
|
|
岁: '歲',
|
|
岛: '島',
|
|
州: '州',
|
|
工: '工',
|
|
币: '幣',
|
|
师: '師',
|
|
帐: '帳',
|
|
带: '帶',
|
|
帮: '幫',
|
|
干: '乾',
|
|
并: '並',
|
|
广: '廣',
|
|
庆: '慶',
|
|
库: '庫',
|
|
应: '應',
|
|
废: '廢',
|
|
开: '開',
|
|
异: '異',
|
|
弃: '棄',
|
|
张: '張',
|
|
强: '強',
|
|
归: '歸',
|
|
当: '當',
|
|
录: '錄',
|
|
彻: '徹',
|
|
径: '徑',
|
|
待: '待',
|
|
循: '循',
|
|
忆: '憶',
|
|
志: '誌',
|
|
忧: '憂',
|
|
念: '念',
|
|
态: '態',
|
|
总: '總',
|
|
恢: '恢',
|
|
息: '息',
|
|
您: '您',
|
|
情: '情',
|
|
想: '想',
|
|
意: '意',
|
|
愿: '願',
|
|
戏: '戲',
|
|
战: '戰',
|
|
户: '戶',
|
|
执: '執',
|
|
扩: '擴',
|
|
扫: '掃',
|
|
扬: '揚',
|
|
批: '批',
|
|
找: '找',
|
|
技: '技',
|
|
报: '報',
|
|
护: '護',
|
|
抽: '抽',
|
|
担: '擔',
|
|
拥: '擁',
|
|
择: '擇',
|
|
按: '按',
|
|
挥: '揮',
|
|
换: '換',
|
|
损: '損',
|
|
据: '據',
|
|
授: '授',
|
|
掉: '掉',
|
|
接: '接',
|
|
控: '控',
|
|
推: '推',
|
|
提: '提',
|
|
插: '插',
|
|
揭: '揭',
|
|
搜: '搜',
|
|
携: '攜',
|
|
摄: '攝',
|
|
摘: '摘',
|
|
播: '播',
|
|
操: '操',
|
|
支: '支',
|
|
收: '收',
|
|
改: '改',
|
|
放: '放',
|
|
效: '效',
|
|
数: '數',
|
|
文: '文',
|
|
断: '斷',
|
|
新: '新',
|
|
无: '無',
|
|
时: '時',
|
|
明: '明',
|
|
显: '顯',
|
|
智: '智',
|
|
暂: '暫',
|
|
更: '更',
|
|
替: '替',
|
|
术: '術',
|
|
机: '機',
|
|
权: '權',
|
|
条: '條',
|
|
来: '來',
|
|
极: '極',
|
|
构: '構',
|
|
标: '標',
|
|
栏: '欄',
|
|
树: '樹',
|
|
样: '樣',
|
|
核: '核',
|
|
案: '案',
|
|
档: '檔',
|
|
检: '檢',
|
|
楼: '樓',
|
|
次: '次',
|
|
款: '款',
|
|
步: '步',
|
|
残: '殘',
|
|
段: '段',
|
|
毕: '畢',
|
|
气: '氣',
|
|
汇: '匯',
|
|
汉: '漢',
|
|
没: '沒',
|
|
法: '法',
|
|
注: '註',
|
|
泄: '洩',
|
|
测: '測',
|
|
浏: '瀏',
|
|
消: '消',
|
|
涉: '涉',
|
|
涨: '漲',
|
|
润: '潤',
|
|
添: '添',
|
|
清: '清',
|
|
渠: '渠',
|
|
渲: '渲',
|
|
温: '溫',
|
|
滚: '滾',
|
|
满: '滿',
|
|
漏: '漏',
|
|
演: '演',
|
|
点: '點',
|
|
烦: '煩',
|
|
热: '熱',
|
|
然: '然',
|
|
照: '照',
|
|
爱: '愛',
|
|
父: '父',
|
|
片: '片',
|
|
版: '版',
|
|
状: '狀',
|
|
独: '獨',
|
|
环: '環',
|
|
现: '現',
|
|
理: '理',
|
|
画: '畫',
|
|
畅: '暢',
|
|
疗: '療',
|
|
登: '登',
|
|
监: '監',
|
|
盘: '盤',
|
|
码: '碼',
|
|
础: '礎',
|
|
确: '確',
|
|
碍: '礙',
|
|
礼: '禮',
|
|
离: '離',
|
|
种: '種',
|
|
称: '稱',
|
|
稳: '穩',
|
|
窗: '窗',
|
|
笔: '筆',
|
|
签: '簽',
|
|
简: '簡',
|
|
算: '算',
|
|
管: '管',
|
|
类: '類',
|
|
粘: '黏',
|
|
精: '精',
|
|
系: '系',
|
|
级: '級',
|
|
线: '線',
|
|
组: '組',
|
|
细: '細',
|
|
终: '終',
|
|
经: '經',
|
|
结: '結',
|
|
绝: '絕',
|
|
统: '統',
|
|
维: '維',
|
|
缓: '緩',
|
|
编: '編',
|
|
缩: '縮',
|
|
缺: '缺',
|
|
网: '網',
|
|
置: '置',
|
|
联: '聯',
|
|
聊: '聊',
|
|
肃: '肅',
|
|
背: '背',
|
|
能: '能',
|
|
脚: '腳',
|
|
脱: '脫',
|
|
脑: '腦',
|
|
自动: '自動',
|
|
舰: '艦',
|
|
艺: '藝',
|
|
节: '節',
|
|
范: '範',
|
|
荐: '薦',
|
|
获: '獲',
|
|
营: '營',
|
|
落: '落',
|
|
著: '著',
|
|
藏: '藏',
|
|
虑: '慮',
|
|
虚: '虛',
|
|
虽: '雖',
|
|
行: '行',
|
|
补: '補',
|
|
表: '表',
|
|
装: '裝',
|
|
规: '規',
|
|
视: '視',
|
|
觉: '覺',
|
|
览: '覽',
|
|
计: '計',
|
|
订: '訂',
|
|
认: '認',
|
|
议: '議',
|
|
讯: '訊',
|
|
记: '記',
|
|
讲: '講',
|
|
许: '許',
|
|
论: '論',
|
|
设: '設',
|
|
访: '訪',
|
|
证: '證',
|
|
评: '評',
|
|
识: '識',
|
|
诉: '訴',
|
|
试: '試',
|
|
话: '話',
|
|
详: '詳',
|
|
语: '語',
|
|
误: '誤',
|
|
请: '請',
|
|
读: '讀',
|
|
调: '調',
|
|
谈: '談',
|
|
谢: '謝',
|
|
谷: '谷',
|
|
账: '帳',
|
|
负: '負',
|
|
责: '責',
|
|
败: '敗',
|
|
货: '貨',
|
|
质: '質',
|
|
资: '資',
|
|
赃: '贓',
|
|
起: '起',
|
|
超: '超',
|
|
跃: '躍',
|
|
路: '路',
|
|
踪: '蹤',
|
|
车: '車',
|
|
轮: '輪',
|
|
软: '軟',
|
|
载: '載',
|
|
辑: '輯',
|
|
输: '輸',
|
|
边: '邊',
|
|
达: '達',
|
|
过: '過',
|
|
还: '還',
|
|
这: '這',
|
|
进: '進',
|
|
远: '遠',
|
|
连: '連',
|
|
迟: '遲',
|
|
适: '適',
|
|
选: '選',
|
|
递: '遞',
|
|
通: '通',
|
|
逻: '邏',
|
|
遗: '遺',
|
|
遥: '遙',
|
|
邀: '邀',
|
|
邮: '郵',
|
|
部: '部',
|
|
配: '配',
|
|
释: '釋',
|
|
重: '重',
|
|
针: '針',
|
|
钥: '鑰',
|
|
钟: '鐘',
|
|
钮: '鈕',
|
|
钱: '錢',
|
|
链: '鏈',
|
|
错: '錯',
|
|
键: '鍵',
|
|
镜: '鏡',
|
|
长: '長',
|
|
门: '門',
|
|
闭: '閉',
|
|
间: '間',
|
|
队: '隊',
|
|
阶: '階',
|
|
阳: '陽',
|
|
阴: '陰',
|
|
陈: '陳',
|
|
际: '際',
|
|
隐: '隱',
|
|
难: '難',
|
|
雏: '雛',
|
|
需: '需',
|
|
面: '面',
|
|
页: '頁',
|
|
项: '項',
|
|
顺: '順',
|
|
须: '須',
|
|
预: '預',
|
|
题: '題',
|
|
颜: '顏',
|
|
风: '風',
|
|
飞: '飛',
|
|
馆: '館',
|
|
验: '驗',
|
|
高: '高',
|
|
鱼: '魚',
|
|
鲜: '鮮',
|
|
鸟: '鳥',
|
|
麦: '麥',
|
|
黄: '黃',
|
|
};
|
|
|
|
export function toTraditionalChinese(value: string): string {
|
|
let converted = value;
|
|
for (const [source, target] of SIMPLIFIED_TO_TRADITIONAL_PHRASES) {
|
|
converted = converted.split(source).join(target);
|
|
}
|
|
return Array.from(converted)
|
|
.map((char) => SIMPLIFIED_TO_TRADITIONAL_CHARS[char] ?? char)
|
|
.join('');
|
|
}
|