mirror of
https://github.com/YspCoder/clawgo.git
synced 2026-04-15 00:27:29 +08:00
fix webui i18n
This commit is contained in:
@@ -47,7 +47,7 @@ const Header: React.FC = () => {
|
||||
className="flex items-center gap-2 text-sm font-medium text-zinc-400 hover:text-zinc-200 transition-colors bg-zinc-900 hover:bg-zinc-800 border border-zinc-800 px-3 py-1.5 rounded-lg"
|
||||
>
|
||||
<Globe className="w-4 h-4" />
|
||||
{i18n.language === 'en' ? '中文' : 'English'}
|
||||
{i18n.language === 'en' ? t('languageZh') : t('languageEn')}
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
@@ -223,6 +223,10 @@ const resources = {
|
||||
actionFailed: 'Action failed',
|
||||
cronExpressionPlaceholder: '*/5 * * * *',
|
||||
recipientId: 'recipient id',
|
||||
languageZh: '中文',
|
||||
languageEn: 'English',
|
||||
configRoot: '(root)',
|
||||
configCommaSeparatedHint: ', a, b',
|
||||
configLabels: {
|
||||
gateway: 'Gateway',
|
||||
host: 'Host',
|
||||
@@ -302,7 +306,114 @@ const resources = {
|
||||
factor: 'Factor',
|
||||
min_delay: 'Min Delay',
|
||||
max_delay: 'Max Delay',
|
||||
jitter: 'Jitter'
|
||||
jitter: 'Jitter',
|
||||
channels: 'Channels',
|
||||
cron: 'Cron',
|
||||
workspace: 'Workspace',
|
||||
proxy_fallbacks: 'Proxy Fallbacks',
|
||||
heartbeat: 'Heartbeat',
|
||||
every_sec: 'Interval (Seconds)',
|
||||
ack_max_chars: 'Ack Max Chars',
|
||||
prompt_template: 'Prompt Template',
|
||||
autonomy: 'Autonomy',
|
||||
tick_interval_sec: 'Tick Interval (Seconds)',
|
||||
min_run_interval_sec: 'Min Run Interval (Seconds)',
|
||||
max_pending_duration_sec: 'Max Pending Duration (Seconds)',
|
||||
max_consecutive_stalls: 'Max Consecutive Stalls',
|
||||
max_dispatch_per_tick: 'Max Dispatch Per Tick',
|
||||
notify_cooldown_sec: 'Notify Cooldown (Seconds)',
|
||||
notify_same_reason_cooldown_sec: 'Same-reason Notify Cooldown (Seconds)',
|
||||
quiet_hours: 'Quiet Hours',
|
||||
user_idle_resume_sec: 'User Idle Resume (Seconds)',
|
||||
max_rounds_without_user: 'Max Rounds Without User',
|
||||
task_history_retention_days: 'Task History Retention (Days)',
|
||||
waiting_resume_debounce_sec: 'Waiting Resume Debounce (Seconds)',
|
||||
idle_round_budget_release_sec: 'Idle Round Budget Release (Seconds)',
|
||||
allowed_task_keywords: 'Allowed Task Keywords',
|
||||
ekg_consecutive_error_threshold: 'EKG Consecutive Error Threshold',
|
||||
texts: 'Text Templates',
|
||||
no_response_fallback: 'No-response Fallback',
|
||||
think_only_fallback: 'Think-only Fallback',
|
||||
memory_recall_keywords: 'Memory Recall Keywords',
|
||||
lang_usage: 'Language Usage Hint',
|
||||
lang_invalid: 'Invalid Language Message',
|
||||
lang_updated_template: 'Language Updated Template',
|
||||
subagents_none: 'No-subagents Message',
|
||||
sessions_none: 'No-sessions Message',
|
||||
unsupported_action: 'Unsupported Action Message',
|
||||
system_rewrite_template: 'System Rewrite Template',
|
||||
runtime_compaction_note: 'Runtime Compaction Note',
|
||||
startup_compaction_note: 'Startup Compaction Note',
|
||||
autonomy_important_keywords: 'Autonomy Important Keywords',
|
||||
autonomy_completion_template: 'Autonomy Completion Template',
|
||||
autonomy_blocked_template: 'Autonomy Blocked Template',
|
||||
context_compaction: 'Context Compaction',
|
||||
mode: 'Mode',
|
||||
trigger_messages: 'Trigger Messages',
|
||||
keep_recent_messages: 'Keep Recent Messages',
|
||||
max_summary_chars: 'Max Summary Chars',
|
||||
max_transcript_chars: 'Max Transcript Chars',
|
||||
runtime_control: 'Runtime Control',
|
||||
intent_max_input_chars: 'Intent Max Input Chars',
|
||||
autonomy_tick_interval_sec: 'Autonomy Tick Interval (Seconds)',
|
||||
autonomy_min_run_interval_sec: 'Autonomy Min Run Interval (Seconds)',
|
||||
autonomy_idle_threshold_sec: 'Autonomy Idle Threshold (Seconds)',
|
||||
autonomy_max_rounds_without_user: 'Autonomy Max Rounds Without User',
|
||||
autonomy_max_pending_duration_sec: 'Autonomy Max Pending Duration (Seconds)',
|
||||
autonomy_max_consecutive_stalls: 'Autonomy Max Consecutive Stalls',
|
||||
autolearn_max_rounds_without_user: 'Autolearn Max Rounds Without User',
|
||||
run_state_ttl_seconds: 'Run State TTL (Seconds)',
|
||||
run_state_max: 'Run State Max',
|
||||
tool_parallel_safe_names: 'Tool Parallel Safe Names',
|
||||
tool_max_parallel_calls: 'Tool Max Parallel Calls',
|
||||
system_summary: 'System Summary',
|
||||
marker: 'Summary Marker',
|
||||
completed_prefix: 'Completed Prefix',
|
||||
changes_prefix: 'Changes Prefix',
|
||||
outcome_prefix: 'Outcome Prefix',
|
||||
completed_title: 'Completed Title',
|
||||
changes_title: 'Changes Title',
|
||||
outcomes_title: 'Outcomes Title',
|
||||
inbound_message_id_dedupe_ttl_seconds: 'Inbound Message Dedupe TTL (Seconds)',
|
||||
inbound_content_dedupe_window_seconds: 'Inbound Content Dedupe Window (Seconds)',
|
||||
outbound_dedupe_window_seconds: 'Outbound Dedupe Window (Seconds)',
|
||||
telegram: 'Telegram',
|
||||
allow_from: 'Allowed Senders',
|
||||
allow_chats: 'Allowed Chats',
|
||||
enable_groups: 'Enable Groups',
|
||||
require_mention_in_groups: 'Require Mention In Groups',
|
||||
discord: 'Discord',
|
||||
maixcam: 'MaixCam',
|
||||
whatsapp: 'WhatsApp',
|
||||
bridge_url: 'Bridge URL',
|
||||
feishu: 'Feishu',
|
||||
app_id: 'App ID',
|
||||
app_secret: 'App Secret',
|
||||
encrypt_key: 'Encrypt Key',
|
||||
verification_token: 'Verification Token',
|
||||
dingtalk: 'DingTalk',
|
||||
filesystem: 'Filesystem',
|
||||
working_dir: 'Working Directory',
|
||||
timeout: 'Timeout',
|
||||
auto_install_missing: 'Auto-install Missing',
|
||||
sandbox: 'Sandbox',
|
||||
image: 'Image',
|
||||
web: 'Web',
|
||||
search: 'Search',
|
||||
max_results: 'Max Results',
|
||||
proxies: 'Proxies',
|
||||
cross_session_call_id: 'Cross-session Call ID',
|
||||
supports_responses_compact: 'Supports Responses Compact',
|
||||
min_sleep_sec: 'Min Sleep (Seconds)',
|
||||
max_sleep_sec: 'Max Sleep (Seconds)',
|
||||
retry_backoff_base_sec: 'Retry Backoff Base (Seconds)',
|
||||
retry_backoff_max_sec: 'Retry Backoff Max (Seconds)',
|
||||
max_consecutive_failure_retries: 'Max Consecutive Failure Retries',
|
||||
max_workers: 'Max Workers',
|
||||
dir: 'Directory',
|
||||
filename: 'Filename',
|
||||
max_size_mb: 'Max Size (MB)',
|
||||
retention_days: 'Retention Days'
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -526,6 +637,10 @@ const resources = {
|
||||
actionFailed: '操作失败',
|
||||
cronExpressionPlaceholder: '*/5 * * * *',
|
||||
recipientId: '接收者 ID',
|
||||
languageZh: '中文',
|
||||
languageEn: 'English',
|
||||
configRoot: '(根)',
|
||||
configCommaSeparatedHint: ',例如 a,b',
|
||||
configLabels: {
|
||||
gateway: '网关',
|
||||
host: '主机',
|
||||
@@ -605,7 +720,114 @@ const resources = {
|
||||
factor: '因子',
|
||||
min_delay: '最小延迟',
|
||||
max_delay: '最大延迟',
|
||||
jitter: '抖动'
|
||||
jitter: '抖动',
|
||||
channels: '通道',
|
||||
cron: '定时任务',
|
||||
workspace: '工作目录',
|
||||
proxy_fallbacks: '代理回退链',
|
||||
heartbeat: '心跳',
|
||||
every_sec: '间隔(秒)',
|
||||
ack_max_chars: '确认最大字符数',
|
||||
prompt_template: '提示模板',
|
||||
autonomy: '自治',
|
||||
tick_interval_sec: '轮询间隔(秒)',
|
||||
min_run_interval_sec: '最小运行间隔(秒)',
|
||||
max_pending_duration_sec: '最大挂起时长(秒)',
|
||||
max_consecutive_stalls: '最大连续停滞次数',
|
||||
max_dispatch_per_tick: '每次轮询最大派发数',
|
||||
notify_cooldown_sec: '通知冷却(秒)',
|
||||
notify_same_reason_cooldown_sec: '同原因通知冷却(秒)',
|
||||
quiet_hours: '静默时段',
|
||||
user_idle_resume_sec: '用户空闲恢复(秒)',
|
||||
max_rounds_without_user: '无用户最大轮数',
|
||||
task_history_retention_days: '任务历史保留天数',
|
||||
waiting_resume_debounce_sec: '等待恢复防抖(秒)',
|
||||
idle_round_budget_release_sec: '空闲轮次预算释放(秒)',
|
||||
allowed_task_keywords: '允许任务关键词',
|
||||
ekg_consecutive_error_threshold: 'EKG 连续错误阈值',
|
||||
texts: '文本模板',
|
||||
no_response_fallback: '无响应兜底文案',
|
||||
think_only_fallback: '仅思考兜底文案',
|
||||
memory_recall_keywords: '记忆召回关键词',
|
||||
lang_usage: '语言用法提示',
|
||||
lang_invalid: '语言非法提示',
|
||||
lang_updated_template: '语言更新模板',
|
||||
subagents_none: '无子代理提示',
|
||||
sessions_none: '无会话提示',
|
||||
unsupported_action: '不支持操作提示',
|
||||
system_rewrite_template: '系统改写模板',
|
||||
runtime_compaction_note: '运行期压缩说明',
|
||||
startup_compaction_note: '启动期压缩说明',
|
||||
autonomy_important_keywords: '自治重要关键词',
|
||||
autonomy_completion_template: '自治完成模板',
|
||||
autonomy_blocked_template: '自治阻塞模板',
|
||||
context_compaction: '上下文压缩',
|
||||
mode: '模式',
|
||||
trigger_messages: '触发消息数',
|
||||
keep_recent_messages: '保留最近消息数',
|
||||
max_summary_chars: '摘要最大字符数',
|
||||
max_transcript_chars: '转录最大字符数',
|
||||
runtime_control: '运行时控制',
|
||||
intent_max_input_chars: '意图输入最大字符数',
|
||||
autonomy_tick_interval_sec: '自治轮询间隔(秒)',
|
||||
autonomy_min_run_interval_sec: '自治最小运行间隔(秒)',
|
||||
autonomy_idle_threshold_sec: '自治空闲阈值(秒)',
|
||||
autonomy_max_rounds_without_user: '自治无用户最大轮数',
|
||||
autonomy_max_pending_duration_sec: '自治最大挂起时长(秒)',
|
||||
autonomy_max_consecutive_stalls: '自治最大连续停滞次数',
|
||||
autolearn_max_rounds_without_user: '自学习无用户最大轮数',
|
||||
run_state_ttl_seconds: '运行状态 TTL(秒)',
|
||||
run_state_max: '运行状态上限',
|
||||
tool_parallel_safe_names: '工具并行安全名单',
|
||||
tool_max_parallel_calls: '工具最大并行调用数',
|
||||
system_summary: '系统摘要',
|
||||
marker: '摘要标记',
|
||||
completed_prefix: '完成前缀',
|
||||
changes_prefix: '变更前缀',
|
||||
outcome_prefix: '结果前缀',
|
||||
completed_title: '完成标题',
|
||||
changes_title: '变更标题',
|
||||
outcomes_title: '结果标题',
|
||||
inbound_message_id_dedupe_ttl_seconds: '入站消息去重 TTL(秒)',
|
||||
inbound_content_dedupe_window_seconds: '入站内容去重窗口(秒)',
|
||||
outbound_dedupe_window_seconds: '出站去重窗口(秒)',
|
||||
telegram: 'Telegram',
|
||||
allow_from: '允许发送者',
|
||||
allow_chats: '允许会话',
|
||||
enable_groups: '启用群组',
|
||||
require_mention_in_groups: '群组需 @ 提及',
|
||||
discord: 'Discord',
|
||||
maixcam: 'MaixCam',
|
||||
whatsapp: 'WhatsApp',
|
||||
bridge_url: '桥接地址',
|
||||
feishu: '飞书',
|
||||
app_id: '应用 ID',
|
||||
app_secret: '应用密钥',
|
||||
encrypt_key: '加密 Key',
|
||||
verification_token: '校验 Token',
|
||||
dingtalk: '钉钉',
|
||||
filesystem: '文件系统',
|
||||
working_dir: '工作目录',
|
||||
timeout: '超时',
|
||||
auto_install_missing: '自动安装缺失依赖',
|
||||
sandbox: '沙箱',
|
||||
image: '镜像',
|
||||
web: 'Web',
|
||||
search: '搜索',
|
||||
max_results: '最大结果数',
|
||||
proxies: '代理集合',
|
||||
cross_session_call_id: '跨会话调用 ID',
|
||||
supports_responses_compact: '支持紧凑 responses',
|
||||
min_sleep_sec: '最小休眠(秒)',
|
||||
max_sleep_sec: '最大休眠(秒)',
|
||||
retry_backoff_base_sec: '重试退避基准(秒)',
|
||||
retry_backoff_max_sec: '重试退避上限(秒)',
|
||||
max_consecutive_failure_retries: '最大连续失败重试次数',
|
||||
max_workers: '最大 worker 数',
|
||||
dir: '目录',
|
||||
filename: '文件名',
|
||||
max_size_mb: '最大大小(MB)',
|
||||
retention_days: '保留天数'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,11 @@ const Config: React.FC = () => {
|
||||
const [search, setSearch] = useState('');
|
||||
const [newProxyName, setNewProxyName] = useState('');
|
||||
|
||||
const configLabels = useMemo(
|
||||
() => t('configLabels', { returnObjects: true }) as Record<string, string>,
|
||||
[t]
|
||||
);
|
||||
|
||||
const hotPrefixes = useMemo(() => hotReloadFieldDetails.map((x) => String(x.path || '').replace(/\.\*$/, '')).filter(Boolean), [hotReloadFieldDetails]);
|
||||
|
||||
const allTopKeys = useMemo(() => Object.keys(cfg || {}).filter(k => typeof (cfg as any)?.[k] === 'object' && (cfg as any)?.[k] !== null), [cfg]);
|
||||
@@ -63,7 +68,7 @@ const Config: React.FC = () => {
|
||||
const walk = (a: any, b: any, p: string) => {
|
||||
const keys = new Set([...(a && typeof a === 'object' ? Object.keys(a) : []), ...(b && typeof b === 'object' ? Object.keys(b) : [])]);
|
||||
if (keys.size === 0) {
|
||||
if (JSON.stringify(a) !== JSON.stringify(b)) out.push({ path: p || '(root)', before: a, after: b });
|
||||
if (JSON.stringify(a) !== JSON.stringify(b)) out.push({ path: p || t('configRoot'), before: a, after: b });
|
||||
return;
|
||||
}
|
||||
keys.forEach((k) => {
|
||||
@@ -193,7 +198,7 @@ const Config: React.FC = () => {
|
||||
onClick={() => setSelectedTop(k)}
|
||||
className={`w-full text-left px-3 py-2 rounded-lg text-sm transition-colors ${activeTop === k ? 'bg-indigo-500/20 text-indigo-300 border border-indigo-500/30' : 'text-zinc-300 hover:bg-zinc-800/60'}`}
|
||||
>
|
||||
{k}
|
||||
{configLabels[k] || k}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
@@ -216,7 +221,7 @@ const Config: React.FC = () => {
|
||||
<input value={String(p?.api_base || '')} onChange={(e)=>updateProxyField(name, 'api_base', e.target.value)} placeholder={t('configLabels.api_base')} className="md:col-span-2 px-2 py-1 rounded bg-zinc-950 border border-zinc-800" />
|
||||
<input value={String(p?.api_key || '')} onChange={(e)=>updateProxyField(name, 'api_key', e.target.value)} placeholder={t('configLabels.api_key')} className="md:col-span-2 px-2 py-1 rounded bg-zinc-950 border border-zinc-800" />
|
||||
<input value={String(p?.protocol || '')} onChange={(e)=>updateProxyField(name, 'protocol', e.target.value)} placeholder={t('configLabels.protocol')} className="md:col-span-1 px-2 py-1 rounded bg-zinc-950 border border-zinc-800" />
|
||||
<input value={Array.isArray(p?.models) ? p.models.join(',') : ''} onChange={(e)=>updateProxyField(name, 'models', e.target.value.split(',').map(s=>s.trim()).filter(Boolean))} placeholder={`${t('configLabels.models')},a,b`} className="md:col-span-1 px-2 py-1 rounded bg-zinc-950 border border-zinc-800" />
|
||||
<input value={Array.isArray(p?.models) ? p.models.join(',') : ''} onChange={(e)=>updateProxyField(name, 'models', e.target.value.split(',').map(s=>s.trim()).filter(Boolean))} placeholder={`${t('configLabels.models')}${t('configCommaSeparatedHint')}`} className="md:col-span-1 px-2 py-1 rounded bg-zinc-950 border border-zinc-800" />
|
||||
<button onClick={()=>removeProxy(name)} className="md:col-span-1 px-2 py-1 rounded bg-red-900/60 hover:bg-red-800 text-red-100">{t('delete')}</button>
|
||||
</div>
|
||||
))}
|
||||
@@ -229,7 +234,7 @@ const Config: React.FC = () => {
|
||||
{activeTop ? (
|
||||
<RecursiveConfig
|
||||
data={(cfg as any)?.[activeTop] || {}}
|
||||
labels={t('configLabels', { returnObjects: true }) as Record<string, string>}
|
||||
labels={configLabels}
|
||||
path={activeTop}
|
||||
hotPaths={hotReloadFieldDetails.map((x) => x.path)}
|
||||
onlyHot={hotOnly}
|
||||
|
||||
Reference in New Issue
Block a user