From 139745c74ccf8e75ec7550d6487209049ea7cbe8 Mon Sep 17 00:00:00 2001 From: Antony Leons Date: Sat, 27 Jun 2026 03:44:35 +0100 Subject: [PATCH] feat: implement i18n support for settings components --- src/renderer/components/SettingsModal.tsx | 20 +++--- .../components/settings/GeneralSection.tsx | 34 ++++----- .../components/settings/LLMSection.tsx | 10 +-- .../components/settings/MCPServerSection.tsx | 20 +++--- .../components/settings/MitmProxySection.tsx | 66 ++++++++--------- .../components/settings/ProxySection.tsx | 24 ++++--- src/renderer/i18n/en.ts | 72 +++++++++++++++++++ src/renderer/i18n/zh.ts | 72 +++++++++++++++++++ 8 files changed, 236 insertions(+), 82 deletions(-) diff --git a/src/renderer/components/SettingsModal.tsx b/src/renderer/components/SettingsModal.tsx index 08c8c23..c4cf5ee 100644 --- a/src/renderer/components/SettingsModal.tsx +++ b/src/renderer/components/SettingsModal.tsx @@ -7,16 +7,17 @@ import ProxySection from './settings/ProxySection' import MCPServerSection from './settings/MCPServerSection' import MitmProxySection from './settings/MitmProxySection' import FingerprintSection from './settings/FingerprintSection' +import { useLocale, type LocaleKey } from '../i18n' type SettingsSection = 'general' | 'llm' | 'proxy' | 'mcp-server' | 'mitm-proxy' | 'fingerprint' -const menuItems: { key: SettingsSection; icon: React.FC<{ size?: number | string }>; label: string }[] = [ - { key: 'general', icon: IconApp, label: '通用' }, - { key: 'llm', icon: IconRobot, label: 'LLM' }, - { key: 'proxy', icon: IconGlobe, label: '代理' }, - { key: 'mcp-server', icon: IconBolt, label: 'MCP Server' }, - { key: 'mitm-proxy', icon: IconShield, label: 'MITM 代理' }, - { key: 'fingerprint', icon: IconCode, label: '指纹' }, +const menuItems: { key: SettingsSection; icon: React.FC<{ size?: number | string }>; labelKey: LocaleKey }[] = [ + { key: 'general', icon: IconApp, labelKey: 'settings.general' }, + { key: 'llm', icon: IconRobot, labelKey: 'settings.llm' }, + { key: 'proxy', icon: IconGlobe, labelKey: 'settings.proxy' }, + { key: 'mcp-server', icon: IconBolt, labelKey: 'settings.mcpServer' }, + { key: 'mitm-proxy', icon: IconShield, labelKey: 'settings.mitmProxy' }, + { key: 'fingerprint', icon: IconCode, labelKey: 'settings.fingerprint' }, ] const sectionComponents: Record = { @@ -31,13 +32,14 @@ const sectionComponents: Record = { interface Props { open: boolean; onClose: () => void; currentSessionId?: string | null } export default function SettingsModal({ open, onClose, currentSessionId }: Props) { + const { t } = useLocale() const [activeSection, setActiveSection] = useState('general') const ActiveComponent = sectionComponents[activeSection] return ( - {item.label} + {t(item.labelKey)} ) })} diff --git a/src/renderer/components/settings/GeneralSection.tsx b/src/renderer/components/settings/GeneralSection.tsx index 8180fa4..20cf889 100644 --- a/src/renderer/components/settings/GeneralSection.tsx +++ b/src/renderer/components/settings/GeneralSection.tsx @@ -15,8 +15,10 @@ import { import type { UpdateStatus } from '@shared/types' import PromptTemplateModal from '../PromptTemplateModal' import MCPServerModal from '../MCPServerModal' +import { useLocale } from '../../i18n' export default function GeneralSection() { + const { t } = useLocale() const [appVersion, setAppVersion] = useState('') const [updateStatus, setUpdateStatus] = useState({ state: 'idle' }) const [templateModalOpen, setTemplateModalOpen] = useState(false) @@ -79,33 +81,33 @@ export default function GeneralSection() {
{updateStatus.state === 'idle' && ( )} {updateStatus.state === 'checking' && ( )} {updateStatus.state === 'not-available' && ( <> - 已是最新版本 - + {t('settings.upToDate')} + )} {updateStatus.state === 'available' && ( <> - v{updateStatus.info?.version} 可用 - 正在下载... + {t('settings.versionAvailable', { version: updateStatus.info?.version ?? '' })} + {t('settings.downloading')} )} {updateStatus.state === 'downloaded' && ( <> - v{updateStatus.info?.version} 已就绪 + {t('settings.versionReady', { version: updateStatus.info?.version ?? '' })} )} @@ -113,7 +115,7 @@ export default function GeneralSection() { <> {updateStatus.error} - + )}
@@ -127,14 +129,14 @@ export default function GeneralSection() {
- 管理工具 + {t('settings.managementTools')}
@@ -142,17 +144,17 @@ export default function GeneralSection() { {/* Error Logs */}
- 错误日志 + {t('settings.errorLogs')}
- 遇到问题时可导出日志文件发送给开发者以便排查 + {t('settings.errorLogsHelp')}
diff --git a/src/renderer/components/settings/LLMSection.tsx b/src/renderer/components/settings/LLMSection.tsx index 98c4611..7e62280 100644 --- a/src/renderer/components/settings/LLMSection.tsx +++ b/src/renderer/components/settings/LLMSection.tsx @@ -1,6 +1,7 @@ import { useEffect, useState } from 'react' import { Input, PasswordInput, Select, InputNumber, Button, useToast } from '../../ui' import type { LLMProviderConfig, LLMProviderType, OpenAIApiType } from '@shared/types' +import { useLocale } from '../../i18n' const defaultUrls: Record = { openai: 'https://api.openai.com/v1', @@ -22,6 +23,7 @@ const fieldStyle: React.CSSProperties = { export default function LLMSection() { const toast = useToast() + const { t } = useLocale() const [name, setName] = useState('openai') const [apiType, setApiType] = useState('completions') const [baseUrl, setBaseUrl] = useState(defaultUrls.openai) @@ -57,7 +59,7 @@ export default function LLMSection() { const handleSave = async () => { if (!baseUrl || !apiKey || !model) { - toast.warning('请填写必填项(Base URL、API Key、Model)') + toast.warning(t('settings.llmRequiredFields')) return } const config: LLMProviderConfig = { @@ -69,7 +71,7 @@ export default function LLMSection() { ...(showApiType && apiType ? { apiType } : {}), } await window.electronAPI.saveLLMConfig(config) - toast.success('LLM 配置已保存') + toast.success(t('settings.llmSaved')) } return ( @@ -83,7 +85,7 @@ export default function LLMSection() { { label: 'OpenAI', value: 'openai' }, { label: 'Anthropic', value: 'anthropic' }, { label: 'MiniMax', value: 'minimax' }, - { label: 'Custom (OpenAI Compatible)', value: 'custom' }, + { label: t('settings.customOpenAICompatible'), value: 'custom' }, ]} /> @@ -141,7 +143,7 @@ export default function LLMSection() { ) diff --git a/src/renderer/components/settings/MCPServerSection.tsx b/src/renderer/components/settings/MCPServerSection.tsx index ac0150e..7d6fd59 100644 --- a/src/renderer/components/settings/MCPServerSection.tsx +++ b/src/renderer/components/settings/MCPServerSection.tsx @@ -53,20 +53,20 @@ export default function MCPServerSection() {
- 启用 MCP Server + {t('settings.enableMcpServer')}
{enabled && ( <>
- 端口 + {t('settings.port')}
- 外部工具配置 URL:{' '} + {t('settings.externalToolConfigUrl')}{' '}
- 鉴权 + {t('settings.auth')}
@@ -105,11 +105,11 @@ export default function MCPServerSection() { onBlur={() => setTokenVisible(false)} style={{ flex: 1, fontFamily: 'var(--font-mono)', fontSize: 'var(--font-size-2xs)' }} /> - - + +
- 外部工具需在请求头中设置: { const config: MCPServerSettings = { enabled, port, authEnabled, authToken } await window.electronAPI.saveMCPServerConfig(config) - toast.success('MCP Server 配置已保存') + toast.success(t('settings.mcpServerSaved')) const status = await window.electronAPI.getMCPServerStatus() setRunning(status.running) }}> - 保存 MCP Server 设置 + {t('settings.saveMcpServerSettings')}
) diff --git a/src/renderer/components/settings/MitmProxySection.tsx b/src/renderer/components/settings/MitmProxySection.tsx index 7a8aca7..35461a3 100644 --- a/src/renderer/components/settings/MitmProxySection.tsx +++ b/src/renderer/components/settings/MitmProxySection.tsx @@ -8,9 +8,11 @@ import { IconWifi, } from '../../ui/Icons' import type { MitmProxyConfig } from '@shared/types' +import { useLocale } from '../../i18n' export default function MitmProxySection() { const toast = useToast() + const { t } = useLocale() const [mitmEnabled, setMitmEnabled] = useState(false) const [mitmPort, setMitmPort] = useState(8888) const [mitmRunning, setMitmRunning] = useState(false) @@ -42,20 +44,20 @@ export default function MitmProxySection() {
- 启用 MITM 代理 + {t('settings.enableMitmProxy')}
{mitmEnabled && ( <>
- 端口 + {t('settings.port')} - + - 设为系统代理 + {t('settings.setAsSystemProxy')} {mitmSystemProxy && ( - 所有应用的流量将自动通过 MITM 代理,关闭应用时自动还原 + {t('settings.systemProxyHelp')} )} {/* CA Certificate Management */}
- CA 证书管理 + {t('settings.caManagement')}
{!mitmCaInstalled ? ( @@ -124,7 +126,7 @@ export default function MitmProxySection() { fontSize: 'var(--font-size-sm)', color: 'var(--text-primary)', }}> - ⚠ CA 证书未安装到系统,HTTPS 流量将无法拦截 + {t('settings.caNotInstalledWarning')}
@@ -167,7 +169,7 @@ export default function MitmProxySection() { fontSize: 'var(--font-size-sm)', color: 'var(--text-primary)', }}> - ✓ CA 证书已安装 + {t('settings.caInstalledStatus')}
@@ -212,7 +214,7 @@ export default function MitmProxySection() { {!mitmSystemProxy && (
- 在外部浏览器/设备中配置 HTTP 代理为: + {t('settings.configureExternalProxy')}
{localIPs.length > 0 ? localIPs.map(ip => ( @@ -234,7 +236,7 @@ export default function MitmProxySection() { fontSize: 'var(--font-size-sm)', fontFamily: 'var(--font-mono)', }}> - http://<本机IP>:{mitmPort} + http://<{t('settings.localIp')}>:{mitmPort} )}
@@ -252,7 +254,7 @@ export default function MitmProxySection() { color: 'var(--text-secondary)', lineHeight: 1.6, }}> - 📱 手机安装证书:连接代理后,用浏览器访问{' '} + {t('settings.mobileCertHint')}{' '} - 保存 MITM 代理设置 + {t('settings.saveMitmProxySettings')} {/* Confirm modal for regenerating CA */} setRegenConfirmOpen(false)} - title="重新生成 CA" + title={t('settings.regenerateCa')} footer={
- +
} >

- 重新生成后需要重新安装证书,已配置代理的设备将出现证书错误。确定继续? + {t('settings.regenerateCaConfirm')}

diff --git a/src/renderer/components/settings/ProxySection.tsx b/src/renderer/components/settings/ProxySection.tsx index 7e382b6..854d3f7 100644 --- a/src/renderer/components/settings/ProxySection.tsx +++ b/src/renderer/components/settings/ProxySection.tsx @@ -1,6 +1,7 @@ import { useEffect, useState } from 'react' import { Input, PasswordInput, Select, InputNumber, Button, useToast } from '../../ui' import type { ProxyConfig } from '@shared/types' +import { useLocale } from '../../i18n' const labelStyle: React.CSSProperties = { display: 'block', @@ -15,6 +16,7 @@ const fieldStyle: React.CSSProperties = { export default function ProxySection() { const toast = useToast() + const { t } = useLocale() const [proxyType, setProxyType] = useState('none') const [host, setHost] = useState('') const [port, setPort] = useState(1080) @@ -35,23 +37,23 @@ export default function ProxySection() { const handleSave = async () => { if (proxyType !== 'none' && !host) { - toast.warning('请输入代理主机') + toast.warning(t('settings.proxyHostRequired')) return } const config: ProxyConfig = { type: proxyType, host, port, username, password } await window.electronAPI.saveProxyConfig(config) - toast.success('代理设置已保存并生效') + toast.success(t('settings.proxySaved')) } return (
- + setHost(e.target.value)} @@ -71,7 +73,7 @@ export default function ProxySection() {
- + v !== null && setPort(v)} @@ -83,27 +85,27 @@ export default function ProxySection() {
- + setUsername(e.target.value)} - placeholder="留空则无认证" + placeholder={t('settings.noAuthPlaceholder')} />
- + setPassword(e.target.value)} - placeholder="留空则无认证" + placeholder={t('settings.noAuthPlaceholder')} />
)}
) diff --git a/src/renderer/i18n/en.ts b/src/renderer/i18n/en.ts index 0b2b849..81bf19f 100644 --- a/src/renderer/i18n/en.ts +++ b/src/renderer/i18n/en.ts @@ -87,6 +87,78 @@ export const en = { // Settings 'settings.title': 'Settings', + 'settings.general': 'General', + 'settings.llm': 'LLM', + 'settings.proxy': 'Proxy', + 'settings.mcpServer': 'MCP Server', + 'settings.mitmProxy': 'MITM Proxy', + 'settings.fingerprint': 'Fingerprint', + 'settings.checkForUpdates': 'Check for Updates', + 'settings.checkingUpdates': 'Checking...', + 'settings.upToDate': 'You are up to date', + 'settings.checkAgain': 'Check Again', + 'settings.versionAvailable': 'v{version} available', + 'settings.downloading': 'Downloading...', + 'settings.versionReady': 'v{version} ready', + 'settings.restartToUpdate': 'Restart to Update', + 'settings.retry': 'Retry', + 'settings.managementTools': 'Management Tools', + 'settings.managePromptTemplates': 'Manage Prompt Templates', + 'settings.manageMcpServers': 'Manage MCP Servers', + 'settings.errorLogs': 'Error Logs', + 'settings.errorLogsHelp': 'Export log files when reporting issues to developers.', + 'settings.openLogFolder': 'Open Log Folder', + 'settings.exportLogs': 'Export Logs', + 'settings.llmRequiredFields': 'Fill in required fields: Base URL, API Key, and Model.', + 'settings.llmSaved': 'LLM configuration saved', + 'settings.customOpenAICompatible': 'Custom (OpenAI Compatible)', + 'settings.saveLlmConfig': 'Save LLM Configuration', + 'settings.proxyHostRequired': 'Enter a proxy host', + 'settings.proxySaved': 'Proxy settings saved and applied', + 'settings.proxyType': 'Proxy Type', + 'settings.noProxy': 'No Proxy (Direct)', + 'settings.host': 'Host', + 'settings.port': 'Port', + 'settings.usernameOptional': 'Username (optional)', + 'settings.passwordOptional': 'Password (optional)', + 'settings.noAuthPlaceholder': 'Leave empty for no authentication', + 'settings.saveProxySettings': 'Save Proxy Settings', + 'settings.running': 'Running', + 'settings.stopped': 'Stopped', + 'settings.enableMcpServer': 'Enable MCP Server', + 'settings.externalToolConfigUrl': 'External tool configuration URL:', + 'settings.auth': 'Authentication', + 'settings.copy': 'Copy', + 'settings.regenerate': 'Regenerate', + 'settings.externalToolAuthHeader': 'External tools must set this request header:', + 'settings.mcpServerSaved': 'MCP Server configuration saved', + 'settings.saveMcpServerSettings': 'Save MCP Server Settings', + 'settings.enableMitmProxy': 'Enable MITM Proxy', + 'settings.systemProxyTooltip': 'Point the system HTTP/HTTPS proxy to the MITM proxy to capture all app traffic without manual setup.', + 'settings.setAsSystemProxy': 'Set as System Proxy', + 'settings.systemProxyEnabled': 'System proxy enabled', + 'settings.systemProxyEnableFailed': 'Failed to enable system proxy', + 'settings.systemProxyDisabled': 'System proxy disabled', + 'settings.systemProxyDisableFailed': 'Failed to disable system proxy', + 'settings.systemProxyHelp': 'All app traffic will route through the MITM proxy and be restored when the app closes.', + 'settings.caManagement': 'CA Certificate Management', + 'settings.caNotInstalledWarning': 'CA certificate is not installed. HTTPS traffic cannot be intercepted.', + 'settings.caInstalled': 'CA certificate installed into the system trust store', + 'settings.installFailed': 'Installation failed', + 'settings.installCaCertificate': 'Install CA Certificate', + 'settings.export': 'Export', + 'settings.caInstalledStatus': 'CA certificate installed', + 'settings.caUninstalled': 'CA certificate uninstalled', + 'settings.uninstallFailed': 'Uninstall failed', + 'settings.uninstallCertificate': 'Uninstall Certificate', + 'settings.regenerateCa': 'Regenerate CA', + 'settings.configureExternalProxy': 'Configure HTTP proxy in external browsers/devices as:', + 'settings.localIp': 'local IP', + 'settings.mobileCertHint': 'Install certificate on mobile: after connecting to the proxy, visit', + 'settings.mitmProxySaved': 'MITM proxy settings saved', + 'settings.saveMitmProxySettings': 'Save MITM Proxy Settings', + 'settings.caRegenerated': 'CA regenerated. Please reinstall the certificate.', + 'settings.regenerateCaConfirm': 'After regeneration, you need to reinstall the certificate. Devices already configured with the proxy will show certificate errors. Continue?', // Common 'common.cancel': 'Cancel', diff --git a/src/renderer/i18n/zh.ts b/src/renderer/i18n/zh.ts index 65407e0..868e734 100644 --- a/src/renderer/i18n/zh.ts +++ b/src/renderer/i18n/zh.ts @@ -87,6 +87,78 @@ export const zh = { // Settings 'settings.title': '设置', + 'settings.general': '通用', + 'settings.llm': 'LLM', + 'settings.proxy': '代理', + 'settings.mcpServer': 'MCP Server', + 'settings.mitmProxy': 'MITM 代理', + 'settings.fingerprint': '指纹', + 'settings.checkForUpdates': '检查更新', + 'settings.checkingUpdates': '正在检查...', + 'settings.upToDate': '已是最新版本', + 'settings.checkAgain': '重新检查', + 'settings.versionAvailable': 'v{version} 可用', + 'settings.downloading': '正在下载...', + 'settings.versionReady': 'v{version} 已就绪', + 'settings.restartToUpdate': '立即重启更新', + 'settings.retry': '重试', + 'settings.managementTools': '管理工具', + 'settings.managePromptTemplates': '管理提示词模板', + 'settings.manageMcpServers': '管理 MCP 服务器', + 'settings.errorLogs': '错误日志', + 'settings.errorLogsHelp': '遇到问题时可导出日志文件发送给开发者以便排查', + 'settings.openLogFolder': '打开日志目录', + 'settings.exportLogs': '导出日志', + 'settings.llmRequiredFields': '请填写必填项(Base URL、API Key、Model)', + 'settings.llmSaved': 'LLM 配置已保存', + 'settings.customOpenAICompatible': '自定义(OpenAI 兼容)', + 'settings.saveLlmConfig': '保存 LLM 配置', + 'settings.proxyHostRequired': '请输入代理主机', + 'settings.proxySaved': '代理设置已保存并生效', + 'settings.proxyType': '代理类型', + 'settings.noProxy': '无代理(直连)', + 'settings.host': '主机', + 'settings.port': '端口', + 'settings.usernameOptional': '用户名(可选)', + 'settings.passwordOptional': '密码(可选)', + 'settings.noAuthPlaceholder': '留空则无认证', + 'settings.saveProxySettings': '保存代理设置', + 'settings.running': '运行中', + 'settings.stopped': '已停止', + 'settings.enableMcpServer': '启用 MCP Server', + 'settings.externalToolConfigUrl': '外部工具配置 URL:', + 'settings.auth': '鉴权', + 'settings.copy': '复制', + 'settings.regenerate': '重新生成', + 'settings.externalToolAuthHeader': '外部工具需在请求头中设置:', + 'settings.mcpServerSaved': 'MCP Server 配置已保存', + 'settings.saveMcpServerSettings': '保存 MCP Server 设置', + 'settings.enableMitmProxy': '启用 MITM 代理', + 'settings.systemProxyTooltip': '将系统 HTTP/HTTPS 代理指向 MITM 代理,无需手动配置即可捕获所有应用流量', + 'settings.setAsSystemProxy': '设为系统代理', + 'settings.systemProxyEnabled': '已设为系统代理', + 'settings.systemProxyEnableFailed': '设置系统代理失败', + 'settings.systemProxyDisabled': '已取消系统代理', + 'settings.systemProxyDisableFailed': '取消系统代理失败', + 'settings.systemProxyHelp': '所有应用的流量将自动通过 MITM 代理,关闭应用时自动还原', + 'settings.caManagement': 'CA 证书管理', + 'settings.caNotInstalledWarning': 'CA 证书未安装到系统,HTTPS 流量将无法拦截', + 'settings.caInstalled': 'CA 证书已安装到系统信任链', + 'settings.installFailed': '安装失败', + 'settings.installCaCertificate': '一键安装 CA 证书', + 'settings.export': '导出', + 'settings.caInstalledStatus': 'CA 证书已安装', + 'settings.caUninstalled': 'CA 证书已卸载', + 'settings.uninstallFailed': '卸载失败', + 'settings.uninstallCertificate': '卸载证书', + 'settings.regenerateCa': '重新生成 CA', + 'settings.configureExternalProxy': '在外部浏览器/设备中配置 HTTP 代理为:', + 'settings.localIp': '本机IP', + 'settings.mobileCertHint': '📱 手机安装证书:连接代理后,用浏览器访问', + 'settings.mitmProxySaved': 'MITM 代理设置已保存', + 'settings.saveMitmProxySettings': '保存 MITM 代理设置', + 'settings.caRegenerated': 'CA 已重新生成,请重新安装证书', + 'settings.regenerateCaConfirm': '重新生成后需要重新安装证书,已配置代理的设备将出现证书错误。确定继续?', // Common 'common.cancel': '取消',