feat: complete internationalization (i18n) for components

Implemented comprehensive i18n translation support for the following components:

1. GitSettings.jsx - Git configuration interface
2. ApiKeysSettings.jsx - API keys settings
3. CredentialsSettings.jsx - Credentials settings (GitHub tokens)
4. TasksSettings.jsx - TaskMaster task management settings
5. ChatInterface.jsx - Chat interface (major translation work)

New translation files:
- src/i18n/locales/en/chat.json - English chat interface translations
- src/i18n/locales/zh-CN/chat.json - Chinese chat interface translations

ChatInterface.jsx translations:
- Code block copy buttons (Copy, Copied, Copy code)
- Message type labels (User, Error, Tool, Claude, Cursor, Codex)
- Tool settings tooltip
- Search result display (pattern, in, results)
- Codex permission modes (Default, Accept Edits, Bypass Permissions, Plan)
- Input placeholder and hint text
- Keyboard shortcut hints (Ctrl+Enter/Enter modes)
- Command menu button

i18n configuration updates:
- Registered chat namespace in config.js
- Extended settings.json translations (git, apiKeys, tasks, agents, mcpServers sections)

完成以下组件的 i18n 翻译工作:

1. GitSettings.jsx - Git 配置界面
2. ApiKeysSettings.jsx - API 密钥设置
3. CredentialsSettings.jsx - 凭据设置(GitHub Token)
4. TasksSettings.jsx - TaskMaster 任务管理设置
5. ChatInterface.jsx - 聊天界面(主要翻译工作)

新增翻译文件:
- src/i18n/locales/en/chat.json - 英文聊天界面翻译
- src/i18n/locales/zh-CN/chat.json - 中文聊天界面翻译

ChatInterface.jsx 翻译内容:
- 代码块复制按钮
- 消息类型标签
- 工具设置提示
- 搜索结果显示
- Codex 权限模式(默认、编辑、无限制、计划模式)
- 输入框占位符和提示文本
- 键盘快捷键提示
- 命令菜单按钮

更新 i18n 配置:
- 在 config.js 中注册 chat 命名空间
- 扩展 settings.json 翻译(git、apiKeys、tasks、agents、mcpServers 等部分)
This commit is contained in:
YuanNiancai
2026-01-21 13:56:49 +08:00
parent 50f8c4ba72
commit 0517ee609e
15 changed files with 1214 additions and 311 deletions

View File

@@ -3,6 +3,7 @@ import { Button } from '../ui/button';
import { Input } from '../ui/input';
import { Badge } from '../ui/badge';
import { Server, Plus, Edit3, Trash2, Terminal, Globe, Zap, X } from 'lucide-react';
import { useTranslation } from 'react-i18next';
const getTransportIcon = (type) => {
switch (type) {
@@ -25,16 +26,17 @@ function ClaudeMcpServers({
serverTools,
toolsLoading,
}) {
const { t } = useTranslation('settings');
return (
<div className="space-y-4">
<div className="flex items-center gap-3">
<Server className="w-5 h-5 text-purple-500" />
<h3 className="text-lg font-medium text-foreground">
MCP Servers
{t('mcpServers.title')}
</h3>
</div>
<p className="text-sm text-muted-foreground">
Model Context Protocol servers provide additional tools and data sources to Claude
{t('mcpServers.description.claude')}
</p>
<div className="flex justify-between items-center">
@@ -44,7 +46,7 @@ function ClaudeMcpServers({
size="sm"
>
<Plus className="w-4 h-4 mr-2" />
Add MCP Server
{t('mcpServers.addButton')}
</Button>
</div>
@@ -60,19 +62,19 @@ function ClaudeMcpServers({
{server.type}
</Badge>
<Badge variant="outline" className="text-xs">
{server.scope === 'local' ? 'local' : server.scope === 'user' ? 'user' : server.scope}
{server.scope === 'local' ? t('mcpServers.scope.local') : server.scope === 'user' ? t('mcpServers.scope.user') : server.scope}
</Badge>
</div>
<div className="text-sm text-muted-foreground space-y-1">
{server.type === 'stdio' && server.config?.command && (
<div>Command: <code className="bg-gray-100 dark:bg-gray-800 px-1 rounded text-xs">{server.config.command}</code></div>
<div>{t('mcpServers.config.command')}: <code className="bg-gray-100 dark:bg-gray-800 px-1 rounded text-xs">{server.config.command}</code></div>
)}
{(server.type === 'sse' || server.type === 'http') && server.config?.url && (
<div>URL: <code className="bg-gray-100 dark:bg-gray-800 px-1 rounded text-xs">{server.config.url}</code></div>
<div>{t('mcpServers.config.url')}: <code className="bg-gray-100 dark:bg-gray-800 px-1 rounded text-xs">{server.config.url}</code></div>
)}
{server.config?.args && server.config.args.length > 0 && (
<div>Args: <code className="bg-gray-100 dark:bg-gray-800 px-1 rounded text-xs">{server.config.args.join(' ')}</code></div>
<div>{t('mcpServers.config.args')}: <code className="bg-gray-100 dark:bg-gray-800 px-1 rounded text-xs">{server.config.args.join(' ')}</code></div>
)}
</div>
@@ -90,13 +92,13 @@ function ClaudeMcpServers({
{/* Tools Discovery Results */}
{serverTools?.[server.id] && serverTools[server.id].tools?.length > 0 && (
<div className="mt-2 p-2 rounded text-xs bg-blue-50 dark:bg-blue-900/20 text-blue-800 dark:text-blue-200">
<div className="font-medium">Tools ({serverTools[server.id].tools.length}):</div>
<div className="font-medium">{t('mcpServers.tools.title')} {t('mcpServers.tools.count', { count: serverTools[server.id].tools.length })}</div>
<div className="flex flex-wrap gap-1 mt-1">
{serverTools[server.id].tools.slice(0, 5).map((tool, i) => (
<code key={i} className="bg-blue-100 dark:bg-blue-800 px-1 rounded">{tool.name}</code>
))}
{serverTools[server.id].tools.length > 5 && (
<span className="text-xs opacity-75">+{serverTools[server.id].tools.length - 5} more</span>
<span className="text-xs opacity-75">{t('mcpServers.tools.more', { count: serverTools[server.id].tools.length - 5 })}</span>
)}
</div>
</div>
@@ -109,7 +111,7 @@ function ClaudeMcpServers({
variant="ghost"
size="sm"
className="text-gray-600 hover:text-gray-700"
title="Edit server"
title={t('mcpServers.actions.edit')}
>
<Edit3 className="w-4 h-4" />
</Button>
@@ -118,7 +120,7 @@ function ClaudeMcpServers({
variant="ghost"
size="sm"
className="text-red-600 hover:text-red-700"
title="Delete server"
title={t('mcpServers.actions.delete')}
>
<Trash2 className="w-4 h-4" />
</Button>
@@ -128,7 +130,7 @@ function ClaudeMcpServers({
))}
{servers.length === 0 && (
<div className="text-center py-8 text-gray-500 dark:text-gray-400">
No MCP servers configured
{t('mcpServers.empty')}
</div>
)}
</div>
@@ -138,16 +140,17 @@ function ClaudeMcpServers({
// Cursor MCP Servers
function CursorMcpServers({ servers, onAdd, onEdit, onDelete }) {
const { t } = useTranslation('settings');
return (
<div className="space-y-4">
<div className="flex items-center gap-3">
<Server className="w-5 h-5 text-purple-500" />
<h3 className="text-lg font-medium text-foreground">
MCP Servers
{t('mcpServers.title')}
</h3>
</div>
<p className="text-sm text-muted-foreground">
Model Context Protocol servers provide additional tools and data sources to Cursor
{t('mcpServers.description.cursor')}
</p>
<div className="flex justify-between items-center">
@@ -157,7 +160,7 @@ function CursorMcpServers({ servers, onAdd, onEdit, onDelete }) {
size="sm"
>
<Plus className="w-4 h-4 mr-2" />
Add MCP Server
{t('mcpServers.addButton')}
</Button>
</div>
@@ -173,7 +176,7 @@ function CursorMcpServers({ servers, onAdd, onEdit, onDelete }) {
</div>
<div className="text-sm text-muted-foreground">
{server.config?.command && (
<div>Command: <code className="bg-gray-100 dark:bg-gray-800 px-1 rounded text-xs">{server.config.command}</code></div>
<div>{t('mcpServers.config.command')}: <code className="bg-gray-100 dark:bg-gray-800 px-1 rounded text-xs">{server.config.command}</code></div>
)}
</div>
</div>
@@ -183,6 +186,7 @@ function CursorMcpServers({ servers, onAdd, onEdit, onDelete }) {
variant="ghost"
size="sm"
className="text-gray-600 hover:text-gray-700"
title={t('mcpServers.actions.edit')}
>
<Edit3 className="w-4 h-4" />
</Button>
@@ -191,6 +195,7 @@ function CursorMcpServers({ servers, onAdd, onEdit, onDelete }) {
variant="ghost"
size="sm"
className="text-red-600 hover:text-red-700"
title={t('mcpServers.actions.delete')}
>
<Trash2 className="w-4 h-4" />
</Button>
@@ -200,7 +205,7 @@ function CursorMcpServers({ servers, onAdd, onEdit, onDelete }) {
))}
{servers.length === 0 && (
<div className="text-center py-8 text-gray-500 dark:text-gray-400">
No MCP servers configured
{t('mcpServers.empty')}
</div>
)}
</div>
@@ -210,16 +215,17 @@ function CursorMcpServers({ servers, onAdd, onEdit, onDelete }) {
// Codex MCP Servers
function CodexMcpServers({ servers, onAdd, onEdit, onDelete }) {
const { t } = useTranslation('settings');
return (
<div className="space-y-4">
<div className="flex items-center gap-3">
<Server className="w-5 h-5 text-gray-700 dark:text-gray-300" />
<h3 className="text-lg font-medium text-foreground">
MCP Servers
{t('mcpServers.title')}
</h3>
</div>
<p className="text-sm text-muted-foreground">
Model Context Protocol servers provide additional tools and data sources to Codex
{t('mcpServers.description.codex')}
</p>
<div className="flex justify-between items-center">
@@ -229,7 +235,7 @@ function CodexMcpServers({ servers, onAdd, onEdit, onDelete }) {
size="sm"
>
<Plus className="w-4 h-4 mr-2" />
Add MCP Server
{t('mcpServers.addButton')}
</Button>
</div>
@@ -246,13 +252,13 @@ function CodexMcpServers({ servers, onAdd, onEdit, onDelete }) {
<div className="text-sm text-muted-foreground space-y-1">
{server.config?.command && (
<div>Command: <code className="bg-gray-100 dark:bg-gray-800 px-1 rounded text-xs">{server.config.command}</code></div>
<div>{t('mcpServers.config.command')}: <code className="bg-gray-100 dark:bg-gray-800 px-1 rounded text-xs">{server.config.command}</code></div>
)}
{server.config?.args && server.config.args.length > 0 && (
<div>Args: <code className="bg-gray-100 dark:bg-gray-800 px-1 rounded text-xs">{server.config.args.join(' ')}</code></div>
<div>{t('mcpServers.config.args')}: <code className="bg-gray-100 dark:bg-gray-800 px-1 rounded text-xs">{server.config.args.join(' ')}</code></div>
)}
{server.config?.env && Object.keys(server.config.env).length > 0 && (
<div>Environment: <code className="bg-gray-100 dark:bg-gray-800 px-1 rounded text-xs">{Object.entries(server.config.env).map(([k, v]) => `${k}=${v}`).join(', ')}</code></div>
<div>{t('mcpServers.config.environment')}: <code className="bg-gray-100 dark:bg-gray-800 px-1 rounded text-xs">{Object.entries(server.config.env).map(([k, v]) => `${k}=${v}`).join(', ')}</code></div>
)}
</div>
</div>
@@ -263,7 +269,7 @@ function CodexMcpServers({ servers, onAdd, onEdit, onDelete }) {
variant="ghost"
size="sm"
className="text-gray-600 hover:text-gray-700"
title="Edit server"
title={t('mcpServers.actions.edit')}
>
<Edit3 className="w-4 h-4" />
</Button>
@@ -272,7 +278,7 @@ function CodexMcpServers({ servers, onAdd, onEdit, onDelete }) {
variant="ghost"
size="sm"
className="text-red-600 hover:text-red-700"
title="Delete server"
title={t('mcpServers.actions.delete')}
>
<Trash2 className="w-4 h-4" />
</Button>
@@ -282,17 +288,16 @@ function CodexMcpServers({ servers, onAdd, onEdit, onDelete }) {
))}
{servers.length === 0 && (
<div className="text-center py-8 text-gray-500 dark:text-gray-400">
No MCP servers configured
{t('mcpServers.empty')}
</div>
)}
</div>
{/* Help Section */}
<div className="bg-gray-100 dark:bg-gray-800/50 border border-gray-300 dark:border-gray-600 rounded-lg p-4">
<h4 className="font-medium text-gray-900 dark:text-gray-100 mb-2">About Codex MCP</h4>
<h4 className="font-medium text-gray-900 dark:text-gray-100 mb-2">{t('mcpServers.help.title')}</h4>
<p className="text-sm text-gray-700 dark:text-gray-300">
Codex supports stdio-based MCP servers. You can add servers that extend Codex's capabilities
with additional tools and resources.
{t('mcpServers.help.description')}
</p>
</div>
</div>