Merge pull request #162 from tangmengqiu/fix/i18n-english-display

Fix/i18n english display
This commit is contained in:
tinkle-community
2025-11-01 12:20:25 +08:00
committed by GitHub
3 changed files with 210 additions and 81 deletions
+2 -2
View File
@@ -179,7 +179,7 @@ function App() {
style={{ background: 'linear-gradient(135deg, #F0B90B 0%, #FCD535 100%)' }}>
</div>
<p style={{ color: '#EAECEF' }}>...</p>
<p style={{ color: '#EAECEF' }}>{t('loading', language)}</p>
</div>
</div>
);
@@ -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)}
</button>
)}
</div>
+88 -79
View File
@@ -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) {
<button
onClick={() => setShowSignalSourceModal(true)}
className="px-4 py-2 rounded text-sm font-semibold transition-all hover:scale-105"
style={{
background: '#2B3139',
color: '#EAECEF',
border: '1px solid #474D57'
style={{
background: '#2B3139',
color: '#EAECEF',
border: '1px solid #474D57'
}}
>
📡
📡 {t('signalSource', language)}
</button>
<button
@@ -552,7 +552,7 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
<div>
<div className="font-semibold" style={{ color: '#EAECEF' }}>{getShortName(model.name)}</div>
<div className="text-xs" style={{ color: '#848E9C' }}>
{inUse ? '正在使用' : model.enabled ? '已启用' : '已配置'}
{inUse ? t('inUse', language) : model.enabled ? t('enabled', language) : t('configured', language)}
</div>
</div>
</div>
@@ -563,7 +563,7 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
{configuredModels.length === 0 && (
<div className="text-center py-8" style={{ color: '#848E9C' }}>
<Brain className="w-12 h-12 mx-auto mb-2 opacity-50" />
<div className="text-sm">AI模型</div>
<div className="text-sm">{t('noModelsConfigured', language)}</div>
</div>
)}
</div>
@@ -594,7 +594,7 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
<div>
<div className="font-semibold" style={{ color: '#EAECEF' }}>{getShortName(exchange.name)}</div>
<div className="text-xs" style={{ color: '#848E9C' }}>
{exchange.type.toUpperCase()} {inUse ? '正在使用' : exchange.enabled ? '已启用' : '已配置'}
{exchange.type.toUpperCase()} {inUse ? t('inUse', language) : exchange.enabled ? t('enabled', language) : t('configured', language)}
</div>
</div>
</div>
@@ -605,7 +605,7 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
{configuredExchanges.length === 0 && (
<div className="text-center py-8" style={{ color: '#848E9C' }}>
<Landmark className="w-12 h-12 mx-auto mb-2 opacity-50" />
<div className="text-sm"></div>
<div className="text-sm">{t('noExchangesConfigured', language)}</div>
</div>
)}
</div>
@@ -669,19 +669,19 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
style={{ background: 'rgba(99, 102, 241, 0.1)', color: '#6366F1' }}
>
<BarChart3 className="w-4 h-4" />
{t('view', language)}
</button>
<button
onClick={() => handleEditTrader(trader.trader_id)}
disabled={trader.is_running}
className="px-3 py-2 rounded text-sm font-semibold transition-all hover:scale-105 disabled:opacity-50 disabled:cursor-not-allowed"
style={{
background: trader.is_running ? 'rgba(132, 142, 156, 0.1)' : 'rgba(255, 193, 7, 0.1)',
color: trader.is_running ? '#848E9C' : '#FFC107'
style={{
background: trader.is_running ? 'rgba(132, 142, 156, 0.1)' : 'rgba(255, 193, 7, 0.1)',
color: trader.is_running ? '#848E9C' : '#FFC107'
}}
>
{t('edit', language)}
</button>
<button
@@ -766,6 +766,7 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
setShowModelModal(false);
setEditingModel(null);
}}
language={language}
/>
)}
@@ -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}
/>
)}
</div>
@@ -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({
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4">
<div className="bg-gray-800 rounded-lg p-6 w-full max-w-lg relative" style={{ background: '#1E2329' }}>
<h3 className="text-xl font-bold mb-4" style={{ color: '#EAECEF' }}>
📡
📡 {t('signalSourceConfig', language)}
</h3>
<form onSubmit={handleSubmit} className="space-y-4">
@@ -837,7 +842,7 @@ function SignalSourceModal({
style={{ background: '#0B0E11', border: '1px solid #2B3139', color: '#EAECEF' }}
/>
<div className="text-xs mt-1" style={{ color: '#848E9C' }}>
API地址使
{t('coinPoolDescription', language)}
</div>
</div>
@@ -854,18 +859,18 @@ function SignalSourceModal({
style={{ background: '#0B0E11', border: '1px solid #2B3139', color: '#EAECEF' }}
/>
<div className="text-xs mt-1" style={{ color: '#848E9C' }}>
API地址使
{t('oiTopDescription', language)}
</div>
</div>
<div className="p-4 rounded" style={{ background: 'rgba(240, 185, 11, 0.1)', border: '1px solid rgba(240, 185, 11, 0.2)' }}>
<div className="text-sm font-semibold mb-2" style={{ color: '#F0B90B' }}>
{t('information', language)}
</div>
<div className="text-xs space-y-1" style={{ color: '#848E9C' }}>
<div> URL</div>
<div> 使</div>
<div> URL将用于获取市场数据和交易信号</div>
<div>{t('signalSourceInfo1', language)}</div>
<div>{t('signalSourceInfo2', language)}</div>
<div>{t('signalSourceInfo3', language)}</div>
</div>
</div>
@@ -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)}
</button>
<button
type="submit"
className="flex-1 px-4 py-2 rounded text-sm font-semibold"
style={{ background: '#F0B90B', color: '#000' }}
>
{t('save', language)}
</button>
</div>
</form>
@@ -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({
<div className="bg-gray-800 rounded-lg p-6 w-full max-w-lg relative" style={{ background: '#1E2329' }}>
<div className="flex items-center justify-between mb-4">
<h3 className="text-xl font-bold" style={{ color: '#EAECEF' }}>
{editingModelId ? '编辑AI模型' : '添加AI模型'}
{editingModelId ? t('editAIModel', language) : t('addAIModel', language)}
</h3>
{editingModelId && (
<button
type="button"
onClick={() => {
if (confirm('确定要删除此AI模型配置吗?')) {
if (confirm(t('confirmDeleteModel', language))) {
onDelete(editingModelId);
}
}}
className="p-2 rounded hover:bg-red-100 transition-colors"
style={{ background: 'rgba(246, 70, 93, 0.1)', color: '#F6465D' }}
title="删除配置"
title={t('deleteConfigFailed', language)}
>
<Trash2 className="w-4 h-4" />
</button>
)}
</div>
<form onSubmit={handleSubmit} className="space-y-4">
{!editingModelId && (
<div>
<label className="block text-sm font-semibold mb-2" style={{ color: '#EAECEF' }}>
AI模型
{t('selectModel', language)}
</label>
<select
value={selectedModelId}
@@ -972,7 +979,7 @@ function ModelConfigModal({
style={{ background: '#0B0E11', border: '1px solid #2B3139', color: '#EAECEF' }}
required
>
<option value=""></option>
<option value="">{t('pleaseSelectModel', language)}</option>
{availableModels.map(model => (
<option key={model.id} value={model.id}>
{getShortName(model.name)} ({model.provider})
@@ -1016,7 +1023,7 @@ function ModelConfigModal({
type="password"
value={apiKey}
onChange={(e) => setApiKey(e.target.value)}
placeholder="输入API密钥"
placeholder={t('enterAPIKey', language)}
className="w-full px-3 py-2 rounded"
style={{ background: '#0B0E11', border: '1px solid #2B3139', color: '#EAECEF' }}
required
@@ -1025,29 +1032,29 @@ function ModelConfigModal({
<div>
<label className="block text-sm font-semibold mb-2" style={{ color: '#EAECEF' }}>
Base URL ()
{t('customBaseURL', language)}
</label>
<input
type="url"
value={baseUrl}
onChange={(e) => setBaseUrl(e.target.value)}
placeholder="自定义API基础URL,如: https://api.openai.com/v1"
placeholder={t('customBaseURLPlaceholder', language)}
className="w-full px-3 py-2 rounded"
style={{ background: '#0B0E11', border: '1px solid #2B3139', color: '#EAECEF' }}
/>
<div className="text-xs mt-1" style={{ color: '#848E9C' }}>
使API地址
{t('leaveBlankForDefault', language)}
</div>
</div>
<div className="p-4 rounded" style={{ background: 'rgba(240, 185, 11, 0.1)', border: '1px solid rgba(240, 185, 11, 0.2)' }}>
<div className="text-sm font-semibold mb-2" style={{ color: '#F0B90B' }}>
{t('information', language)}
</div>
<div className="text-xs space-y-1" style={{ color: '#848E9C' }}>
<div> API Key将被加密存储</div>
<div> Base URL用于自定义API服务器地址</div>
<div> 使</div>
<div>{t('modelConfigInfo1', language)}</div>
<div>{t('modelConfigInfo2', language)}</div>
<div>{t('modelConfigInfo3', language)}</div>
</div>
</div>
</>
@@ -1060,7 +1067,7 @@ function ModelConfigModal({
className="flex-1 px-4 py-2 rounded text-sm font-semibold"
style={{ background: '#2B3139', color: '#848E9C' }}
>
{t('cancel', language)}
</button>
<button
type="submit"
@@ -1068,7 +1075,7 @@ function ModelConfigModal({
className="flex-1 px-4 py-2 rounded text-sm font-semibold disabled:opacity-50"
style={{ background: '#F0B90B', color: '#000' }}
>
{t('saveConfig', language)}
</button>
</div>
</form>
@@ -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<void>;
onDelete: (exchangeId: string) => void;
onClose: () => void;
language: Language;
}) {
const [selectedExchangeId, setSelectedExchangeId] = useState(editingExchangeId || '');
const [apiKey, setApiKey] = useState('');
@@ -1132,30 +1141,30 @@ function ExchangeConfigModal({
<div className="bg-gray-800 rounded-lg p-6 w-full max-w-lg relative" style={{ background: '#1E2329' }}>
<div className="flex items-center justify-between mb-4">
<h3 className="text-xl font-bold" style={{ color: '#EAECEF' }}>
{editingExchangeId ? '编辑交易所' : '添加交易所'}
{editingExchangeId ? t('editExchange', language) : t('addExchange', language)}
</h3>
{editingExchangeId && (
<button
type="button"
onClick={() => {
if (confirm('确定要删除此交易所配置吗?')) {
if (confirm(t('confirmDeleteExchange', language))) {
onDelete(editingExchangeId);
}
}}
className="p-2 rounded hover:bg-red-100 transition-colors"
style={{ background: 'rgba(246, 70, 93, 0.1)', color: '#F6465D' }}
title="删除配置"
title={t('deleteConfigFailed', language)}
>
<Trash2 className="w-4 h-4" />
</button>
)}
</div>
<form onSubmit={handleSubmit} className="space-y-4">
{!editingExchangeId && (
<div>
<label className="block text-sm font-semibold mb-2" style={{ color: '#EAECEF' }}>
{t('selectExchange', language)}
</label>
<select
value={selectedExchangeId}
@@ -1164,7 +1173,7 @@ function ExchangeConfigModal({
style={{ background: '#0B0E11', border: '1px solid #2B3139', color: '#EAECEF' }}
required
>
<option value=""></option>
<option value="">{t('pleaseSelectExchange', language)}</option>
{availableExchanges.map(exchange => (
<option key={exchange.id} value={exchange.id}>
{getShortName(exchange.name)} ({exchange.type.toUpperCase()})
@@ -1200,7 +1209,7 @@ function ExchangeConfigModal({
type="password"
value={apiKey}
onChange={(e) => setApiKey(e.target.value)}
placeholder="输入API密钥"
placeholder={t('enterAPIKey', language)}
className="w-full px-3 py-2 rounded"
style={{ background: '#0B0E11', border: '1px solid #2B3139', color: '#EAECEF' }}
required
@@ -1215,7 +1224,7 @@ function ExchangeConfigModal({
type="password"
value={secretKey}
onChange={(e) => setSecretKey(e.target.value)}
placeholder="输入密钥"
placeholder={t('enterSecretKey', language)}
className="w-full px-3 py-2 rounded"
style={{ background: '#0B0E11', border: '1px solid #2B3139', color: '#EAECEF' }}
required
@@ -1231,7 +1240,7 @@ function ExchangeConfigModal({
type="password"
value={passphrase}
onChange={(e) => setPassphrase(e.target.value)}
placeholder="输入Passphrase (OKX必填)"
placeholder={t('enterPassphrase', language)}
className="w-full px-3 py-2 rounded"
style={{ background: '#0B0E11', border: '1px solid #2B3139', color: '#EAECEF' }}
required
@@ -1248,21 +1257,21 @@ function ExchangeConfigModal({
className="form-checkbox rounded"
style={{ accentColor: '#F0B90B' }}
/>
<span style={{ color: '#EAECEF' }}>使</span>
<span style={{ color: '#EAECEF' }}>{t('useTestnet', language)}</span>
</label>
<div className="text-xs mt-1" style={{ color: '#848E9C' }}>
{t('testnetDescription', language)}
</div>
</div>
<div className="p-4 rounded" style={{ background: 'rgba(240, 185, 11, 0.1)', border: '1px solid rgba(240, 185, 11, 0.2)' }}>
<div className="text-sm font-semibold mb-2" style={{ color: '#F0B90B' }}>
{t('securityWarning', language)}
</div>
<div className="text-xs space-y-1" style={{ color: '#848E9C' }}>
<div> API密钥将被加密存储使</div>
<div> </div>
<div> </div>
<div>{t('exchangeConfigWarning1', language)}</div>
<div>{t('exchangeConfigWarning2', language)}</div>
<div>{t('exchangeConfigWarning3', language)}</div>
</div>
</div>
</>
@@ -1275,7 +1284,7 @@ function ExchangeConfigModal({
className="flex-1 px-4 py-2 rounded text-sm font-semibold"
style={{ background: '#2B3139', color: '#848E9C' }}
>
{t('cancel', language)}
</button>
<button
type="submit"
@@ -1283,7 +1292,7 @@ function ExchangeConfigModal({
className="flex-1 px-4 py-2 rounded text-sm font-semibold disabled:opacity-50"
style={{ background: '#F0B90B', color: '#000' }}
>
{t('saveConfig', language)}
</button>
</div>
</form>
+120
View File
@@ -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,58 @@ 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?',
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 +310,13 @@ export const translations = {
aiTraders: 'AI交易员',
details: '详情',
tradingPanel: '交易面板',
competition: '竞赛',
running: '运行中',
stopped: '已停止',
adminMode: '管理员模式',
logout: '退出',
switchTrader: '切换交易员:',
view: '查看',
// Footer
footerTitle: 'NOFX - AI交易系统',
@@ -317,11 +382,14 @@ export const translations = {
aiCompetition: 'AI竞赛',
traders: '交易员',
liveBattle: '实时对战',
realTimeBattle: '实时对战',
leader: '领先者',
leaderboard: '排行榜',
live: '实时',
realTime: '实时',
performanceComparison: '表现对比',
realTimePnL: '实时收益率',
realTimePnLPercent: '实时收益率',
headToHead: '正面对决',
leadingBy: '领先 {gap}%',
behindBy: '落后 {gap}%',
@@ -432,6 +500,58 @@ 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: '确定要删除此交易所配置吗?',
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: '登录',