diff --git a/web/src/App.tsx b/web/src/App.tsx
index 228c87ef..c6092b5d 100644
--- a/web/src/App.tsx
+++ b/web/src/App.tsx
@@ -179,7 +179,7 @@ function App() {
style={{ background: 'linear-gradient(135deg, #F0B90B 0%, #FCD535 100%)' }}>
⚡
-
加载中...
+ {t('loading', language)}
);
@@ -299,7 +299,7 @@ function App() {
className="px-3 py-2 rounded text-sm font-semibold transition-all hover:scale-105"
style={{ background: 'rgba(246, 70, 93, 0.1)', color: '#F6465D', border: '1px solid rgba(246, 70, 93, 0.2)' }}
>
- 退出
+ {t('logout', language)}
)}
diff --git a/web/src/components/AITradersPage.tsx b/web/src/components/AITradersPage.tsx
index bc2516cf..9c3da522 100644
--- a/web/src/components/AITradersPage.tsx
+++ b/web/src/components/AITradersPage.tsx
@@ -3,7 +3,7 @@ import useSWR from 'swr';
import { api } from '../lib/api';
import type { TraderInfo, CreateTraderRequest, AIModel, Exchange } from '../types';
import { useLanguage } from '../contexts/LanguageContext';
-import { t } from '../i18n/translations';
+import { t, type Language } from '../i18n/translations';
import { getExchangeIcon } from './ExchangeIcons';
import { getModelIcon } from './ModelIcons';
import { TraderConfigModal } from './TraderConfigModal';
@@ -149,7 +149,7 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
mutateTraders();
} catch (error) {
console.error('Failed to create trader:', error);
- alert('创建交易员失败');
+ alert(t('createTraderFailed', language));
}
};
@@ -160,7 +160,7 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
setShowEditModal(true);
} catch (error) {
console.error('Failed to fetch trader config:', error);
- alert('获取交易员配置失败');
+ alert(t('getTraderConfigFailed', language));
}
};
@@ -170,14 +170,14 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
try {
const model = enabledModels?.find(m => m.id === data.ai_model_id);
const exchange = enabledExchanges?.find(e => e.id === data.exchange_id);
-
+
if (!model) {
- alert('AI模型配置不存在或未启用');
+ alert(t('modelConfigNotExist', language));
return;
}
-
+
if (!exchange) {
- alert('交易所配置不存在或未启用');
+ alert(t('exchangeConfigNotExist', language));
return;
}
@@ -202,19 +202,19 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
mutateTraders();
} catch (error) {
console.error('Failed to update trader:', error);
- alert('更新交易员失败');
+ alert(t('updateTraderFailed', language));
}
};
const handleDeleteTrader = async (traderId: string) => {
if (!confirm(t('confirmDeleteTrader', language))) return;
-
+
try {
await api.deleteTrader(traderId);
mutateTraders();
} catch (error) {
console.error('Failed to delete trader:', error);
- alert('删除交易员失败');
+ alert(t('deleteTraderFailed', language));
}
};
@@ -228,7 +228,7 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
mutateTraders();
} catch (error) {
console.error('Failed to toggle trader:', error);
- alert('操作失败');
+ alert(t('operationFailed', language));
}
};
@@ -247,7 +247,7 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
};
const handleDeleteModelConfig = async (modelId: string) => {
- if (!confirm('确定要删除此AI模型配置吗?')) return;
+ if (!confirm(t('confirmDeleteModel', language))) return;
try {
const updatedModels = allModels?.map(m =>
@@ -272,7 +272,7 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
setEditingModel(null);
} catch (error) {
console.error('Failed to delete model config:', error);
- alert('删除配置失败');
+ alert(t('deleteConfigFailed', language));
}
};
@@ -281,7 +281,7 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
// 找到要配置的模型(从supportedModels中)
const modelToUpdate = supportedModels?.find(m => m.id === modelId);
if (!modelToUpdate) {
- alert('模型不存在');
+ alert(t('modelNotExist', language));
return;
}
@@ -323,12 +323,12 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
setEditingModel(null);
} catch (error) {
console.error('Failed to save model config:', error);
- alert('保存配置失败');
+ alert(t('saveConfigFailed', language));
}
};
const handleDeleteExchangeConfig = async (exchangeId: string) => {
- if (!confirm('确定要删除此交易所配置吗?')) return;
+ if (!confirm(t('confirmDeleteExchange', language))) return;
try {
const updatedExchanges = allExchanges?.map(e =>
@@ -355,7 +355,7 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
setEditingExchange(null);
} catch (error) {
console.error('Failed to delete exchange config:', error);
- alert('删除交易所配置失败');
+ alert(t('deleteExchangeConfigFailed', language));
}
};
@@ -364,7 +364,7 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
// 找到要配置的交易所(从supportedExchanges中)
const exchangeToUpdate = supportedExchanges?.find(e => e.id === exchangeId);
if (!exchangeToUpdate) {
- alert('交易所不存在');
+ alert(t('exchangeNotExist', language));
return;
}
@@ -411,7 +411,7 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
setEditingExchange(null);
} catch (error) {
console.error('Failed to save exchange config:', error);
- alert('保存交易所配置失败');
+ alert(t('saveConfigFailed', language));
}
};
@@ -432,7 +432,7 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
setShowSignalSourceModal(false);
} catch (error) {
console.error('Failed to save signal source:', error);
- alert('保存信号源配置失败');
+ alert(t('saveSignalSourceFailed', language));
}
};
@@ -493,13 +493,13 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
)}
@@ -780,6 +781,7 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
setShowExchangeModal(false);
setEditingExchange(null);
}}
+ language={language}
/>
)}
@@ -790,6 +792,7 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
oiTopUrl={userSignalSource.oiTopUrl}
onSave={handleSaveSignalSource}
onClose={() => setShowSignalSourceModal(false)}
+ language={language}
/>
)}
@@ -801,12 +804,14 @@ function SignalSourceModal({
coinPoolUrl,
oiTopUrl,
onSave,
- onClose
+ onClose,
+ language
}: {
coinPoolUrl: string;
oiTopUrl: string;
onSave: (coinPoolUrl: string, oiTopUrl: string) => void;
onClose: () => void;
+ language: Language;
}) {
const [coinPool, setCoinPool] = useState(coinPoolUrl || '');
const [oiTop, setOiTop] = useState(oiTopUrl || '');
@@ -820,7 +825,7 @@ function SignalSourceModal({
- 📡 信号源配置
+ 📡 {t('signalSourceConfig', language)}
@@ -854,18 +859,18 @@ function SignalSourceModal({
style={{ background: '#0B0E11', border: '1px solid #2B3139', color: '#EAECEF' }}
/>
- 用于获取持仓量排行数据的API地址,留空则不使用此信号源
+ {t('oiTopDescription', language)}
- ℹ️ 说明
+ ℹ️ {t('information', language)}
-
• 信号源配置为用户级别,每个用户可以设置自己的信号源URL
-
• 在创建交易员时可以选择是否使用这些信号源
-
• 配置的URL将用于获取市场数据和交易信号
+
{t('signalSourceInfo1', language)}
+
{t('signalSourceInfo2', language)}
+
{t('signalSourceInfo3', language)}
@@ -876,14 +881,14 @@ function SignalSourceModal({
className="flex-1 px-4 py-2 rounded text-sm font-semibold"
style={{ background: '#2B3139', color: '#848E9C' }}
>
- 取消
+ {t('cancel', language)}
@@ -899,7 +904,8 @@ function ModelConfigModal({
editingModelId,
onSave,
onDelete,
- onClose
+ onClose,
+ language
}: {
allModels: AIModel[];
configuredModels: AIModel[];
@@ -907,6 +913,7 @@ function ModelConfigModal({
onSave: (modelId: string, apiKey: string, baseUrl?: string) => void;
onDelete: (modelId: string) => void;
onClose: () => void;
+ language: Language;
}) {
const [selectedModelId, setSelectedModelId] = useState(editingModelId || '');
const [apiKey, setApiKey] = useState('');
@@ -940,30 +947,30 @@ function ModelConfigModal({
- {editingModelId ? '编辑AI模型' : '添加AI模型'}
+ {editingModelId ? t('editAIModel', language) : t('addAIModel', language)}
{editingModelId && (
)}
-
+
@@ -1083,13 +1090,15 @@ function ExchangeConfigModal({
editingExchangeId,
onSave,
onDelete,
- onClose
+ onClose,
+ language
}: {
allExchanges: Exchange[];
editingExchangeId: string | null;
onSave: (exchangeId: string, apiKey: string, secretKey?: string, testnet?: boolean, hyperliquidWalletAddr?: string, asterUser?: string, asterSigner?: string, asterPrivateKey?: string) => Promise
;
onDelete: (exchangeId: string) => void;
onClose: () => void;
+ language: Language;
}) {
const [selectedExchangeId, setSelectedExchangeId] = useState(editingExchangeId || '');
const [apiKey, setApiKey] = useState('');
@@ -1132,30 +1141,30 @@ function ExchangeConfigModal({
- {editingExchangeId ? '编辑交易所' : '添加交易所'}
+ {editingExchangeId ? t('editExchange', language) : t('addExchange', language)}
{editingExchangeId && (
)}
-
+
diff --git a/web/src/i18n/translations.ts b/web/src/i18n/translations.ts
index 9fc1a854..f89244ff 100644
--- a/web/src/i18n/translations.ts
+++ b/web/src/i18n/translations.ts
@@ -8,8 +8,13 @@ export const translations = {
aiTraders: 'AI Traders',
details: 'Details',
tradingPanel: 'Trading Panel',
+ competition: 'Competition',
running: 'RUNNING',
stopped: 'STOPPED',
+ adminMode: 'Admin Mode',
+ logout: 'Logout',
+ switchTrader: 'Switch Trader:',
+ view: 'View',
// Footer
footerTitle: 'NOFX - AI Trading System',
@@ -75,11 +80,14 @@ export const translations = {
aiCompetition: 'AI Competition',
traders: 'traders',
liveBattle: 'Live Battle',
+ realTimeBattle: 'Real-time Battle',
leader: 'Leader',
leaderboard: 'Leaderboard',
live: 'LIVE',
+ realTime: 'LIVE',
performanceComparison: 'Performance Comparison',
realTimePnL: 'Real-time PnL %',
+ realTimePnLPercent: 'Real-time PnL %',
headToHead: 'Head-to-Head Battle',
leadingBy: 'Leading by {gap}%',
behindBy: 'Behind by {gap}%',
@@ -190,6 +198,59 @@ export const translations = {
loading: 'Loading...',
loadingError: '⚠️ Failed to load AI learning data',
noCompleteData: 'No complete trading data (needs to complete open → close cycle)',
+
+ // AI Traders Page - Additional
+ inUse: 'In Use',
+ noModelsConfigured: 'No configured AI models',
+ noExchangesConfigured: 'No configured exchanges',
+ signalSource: 'Signal Source',
+ signalSourceConfig: 'Signal Source Configuration',
+ coinPoolDescription: 'API endpoint for coin pool data, leave blank to disable this signal source',
+ oiTopDescription: 'API endpoint for open interest rankings, leave blank to disable this signal source',
+ information: 'Information',
+ signalSourceInfo1: '• Signal source configuration is per-user, each user can set their own URLs',
+ signalSourceInfo2: '• When creating traders, you can choose whether to use these signal sources',
+ signalSourceInfo3: '• Configured URLs will be used to fetch market data and trading signals',
+ editAIModel: 'Edit AI Model',
+ addAIModel: 'Add AI Model',
+ confirmDeleteModel: 'Are you sure you want to delete this AI model configuration?',
+ selectModel: 'Select AI Model',
+ pleaseSelectModel: 'Please select a model',
+ customBaseURL: 'Base URL (Optional)',
+ customBaseURLPlaceholder: 'Custom API base URL, e.g.: https://api.openai.com/v1',
+ leaveBlankForDefault: 'Leave blank to use default API address',
+ modelConfigInfo1: '• API Key will be encrypted and stored, please ensure it is valid',
+ modelConfigInfo2: '• Base URL is used for custom API server address',
+ modelConfigInfo3: '• After deleting configuration, traders using this model will not work properly',
+ saveConfig: 'Save Configuration',
+ editExchange: 'Edit Exchange',
+ addExchange: 'Add Exchange',
+ confirmDeleteExchange: 'Are you sure you want to delete this exchange configuration?',
+ selectExchange: 'Select Exchange',
+ pleaseSelectExchange: 'Please select an exchange',
+ enterSecretKey: 'Enter secret key',
+ enterPassphrase: 'Enter Passphrase (Required for OKX)',
+ testnetDescription: 'Enable to connect to exchange test environment for simulated trading',
+ securityWarning: 'Security Warning',
+ exchangeConfigWarning1: '• API keys will be encrypted, recommend using read-only or futures trading permissions',
+ exchangeConfigWarning2: '• Do not grant withdrawal permissions to ensure fund security',
+ exchangeConfigWarning3: '• After deleting configuration, related traders will not be able to trade',
+ edit: 'Edit',
+
+ // Error Messages
+ createTraderFailed: 'Failed to create trader',
+ getTraderConfigFailed: 'Failed to get trader configuration',
+ modelConfigNotExist: 'Model configuration does not exist or is not enabled',
+ exchangeConfigNotExist: 'Exchange configuration does not exist or is not enabled',
+ updateTraderFailed: 'Failed to update trader',
+ deleteTraderFailed: 'Failed to delete trader',
+ operationFailed: 'Operation failed',
+ deleteConfigFailed: 'Failed to delete configuration',
+ modelNotExist: 'Model does not exist',
+ saveConfigFailed: 'Failed to save configuration',
+ exchangeNotExist: 'Exchange does not exist',
+ deleteExchangeConfigFailed: 'Failed to delete exchange configuration',
+ saveSignalSourceFailed: 'Failed to save signal source configuration',
// Login & Register
login: 'Sign In',
@@ -250,8 +311,13 @@ export const translations = {
aiTraders: 'AI交易员',
details: '详情',
tradingPanel: '交易面板',
+ competition: '竞赛',
running: '运行中',
stopped: '已停止',
+ adminMode: '管理员模式',
+ logout: '退出',
+ switchTrader: '切换交易员:',
+ view: '查看',
// Footer
footerTitle: 'NOFX - AI交易系统',
@@ -317,11 +383,14 @@ export const translations = {
aiCompetition: 'AI竞赛',
traders: '交易员',
liveBattle: '实时对战',
+ realTimeBattle: '实时对战',
leader: '领先者',
leaderboard: '排行榜',
live: '实时',
+ realTime: '实时',
performanceComparison: '表现对比',
realTimePnL: '实时收益率',
+ realTimePnLPercent: '实时收益率',
headToHead: '正面对决',
leadingBy: '领先 {gap}%',
behindBy: '落后 {gap}%',
@@ -432,6 +501,59 @@ export const translations = {
loading: '加载中...',
loadingError: '⚠️ 加载AI学习数据失败',
noCompleteData: '暂无完整交易数据(需要完成开仓→平仓的完整周期)',
+
+ // AI Traders Page - Additional
+ inUse: '正在使用',
+ noModelsConfigured: '暂无已配置的AI模型',
+ noExchangesConfigured: '暂无已配置的交易所',
+ signalSource: '信号源',
+ signalSourceConfig: '信号源配置',
+ coinPoolDescription: '用于获取币种池数据的API地址,留空则不使用此信号源',
+ oiTopDescription: '用于获取持仓量排行数据的API地址,留空则不使用此信号源',
+ information: '说明',
+ signalSourceInfo1: '• 信号源配置为用户级别,每个用户可以设置自己的信号源URL',
+ signalSourceInfo2: '• 在创建交易员时可以选择是否使用这些信号源',
+ signalSourceInfo3: '• 配置的URL将用于获取市场数据和交易信号',
+ editAIModel: '编辑AI模型',
+ addAIModel: '添加AI模型',
+ confirmDeleteModel: '确定要删除此AI模型配置吗?',
+ selectModel: '选择AI模型',
+ pleaseSelectModel: '请选择模型',
+ customBaseURL: 'Base URL (可选)',
+ customBaseURLPlaceholder: '自定义API基础URL,如: https://api.openai.com/v1',
+ leaveBlankForDefault: '留空则使用默认API地址',
+ modelConfigInfo1: '• API Key将被加密存储,请确保密钥有效',
+ modelConfigInfo2: '• Base URL用于自定义API服务器地址',
+ modelConfigInfo3: '• 删除配置后,使用此模型的交易员将无法正常工作',
+ saveConfig: '保存配置',
+ editExchange: '编辑交易所',
+ addExchange: '添加交易所',
+ confirmDeleteExchange: '确定要删除此交易所配置吗?',
+ selectExchange: '选择交易所',
+ pleaseSelectExchange: '请选择交易所',
+ enterSecretKey: '输入密钥',
+ enterPassphrase: '输入Passphrase (OKX必填)',
+ testnetDescription: '启用后将连接到交易所测试环境,用于模拟交易',
+ securityWarning: '安全提示',
+ exchangeConfigWarning1: '• API密钥将被加密存储,建议使用只读或期货交易权限',
+ exchangeConfigWarning2: '• 不要授予提现权限,确保资金安全',
+ exchangeConfigWarning3: '• 删除配置后,相关交易员将无法正常交易',
+ edit: '编辑',
+
+ // Error Messages
+ createTraderFailed: '创建交易员失败',
+ getTraderConfigFailed: '获取交易员配置失败',
+ modelConfigNotExist: 'AI模型配置不存在或未启用',
+ exchangeConfigNotExist: '交易所配置不存在或未启用',
+ updateTraderFailed: '更新交易员失败',
+ deleteTraderFailed: '删除交易员失败',
+ operationFailed: '操作失败',
+ deleteConfigFailed: '删除配置失败',
+ modelNotExist: '模型不存在',
+ saveConfigFailed: '保存配置失败',
+ exchangeNotExist: '交易所不存在',
+ deleteExchangeConfigFailed: '删除交易所配置失败',
+ saveSignalSourceFailed: '保存信号源配置失败',
// Login & Register
login: '登录',