feat: translate all UI strings to English for international release

Complete French→English translation across all frontend pages and backend
services — every label, button, header, empty state, toast, and nav item
is now in English. Build verified clean (tsc + vite). No i18n library
added; direct string replacement throughout.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
OpenSquared
2026-06-22 09:06:37 +02:00
parent f8a0a6d023
commit dcbc9f19fc
24 changed files with 1443 additions and 1444 deletions

View File

@@ -19,10 +19,10 @@ import {
import { scoreColor } from '../components/TradeIdeas'
const riskGauge = (score: number) => {
if (score < 25) return { color: 'text-emerald-400', bg: 'bg-emerald-500', label: 'FAIBLE' }
if (score < 50) return { color: 'text-yellow-400', bg: 'bg-yellow-500', label: 'MODÉRÉ' }
if (score < 75) return { color: 'text-orange-400', bg: 'bg-orange-500', label: 'ÉLEVÉ' }
return { color: 'text-red-400', bg: 'bg-red-500', label: 'EXTRÊME' }
if (score < 25) return { color: 'text-emerald-400', bg: 'bg-emerald-500', label: 'LOW' }
if (score < 50) return { color: 'text-yellow-400', bg: 'bg-yellow-500', label: 'MODERATE' }
if (score < 75) return { color: 'text-orange-400', bg: 'bg-orange-500', label: 'HIGH' }
return { color: 'text-red-400', bg: 'bg-red-500', label: 'EXTREME' }
}
const assetEmoji: Record<string, string> = {
@@ -37,9 +37,9 @@ const ASSET_COLORS: Record<string, string> = {
}
const REGIME_LABELS: Record<string, string> = {
goldilocks: 'Goldilocks', desinflation: 'Désinflation', stagflation: 'Stagflation',
recession: 'Récession', crise_liquidite: 'Crise liq.', reflation: 'Reflation',
soft_landing: 'Soft Landing', inflation_shock: 'Infl. Choc', incertain: 'Incertain',
goldilocks: 'Goldilocks', desinflation: 'Disinflation', stagflation: 'Stagflation',
recession: 'Recession', crise_liquidite: 'Liq. Crisis', reflation: 'Reflation',
soft_landing: 'Soft Landing', inflation_shock: 'Infl. Shock', incertain: 'Uncertain',
}
function ViewToggle({ value, onChange }: { value: 'simulated' | 'portfolio'; onChange: (v: 'simulated' | 'portfolio') => void }) {
@@ -51,7 +51,7 @@ function ViewToggle({ value, onChange }: { value: 'simulated' | 'portfolio'; onC
<button onClick={click('simulated')}
className={clsx('px-1.5 py-0.5 rounded transition-colors',
value === 'simulated' ? 'bg-blue-600 text-white' : 'text-slate-600 hover:text-slate-400')}>
Simulé
Simulated
</button>
<button onClick={click('portfolio')}
className={clsx('px-1.5 py-0.5 rounded transition-colors',
@@ -195,7 +195,7 @@ export default function Dashboard() {
{/* Header */}
<div className="flex items-center justify-between">
<div>
<h1 className="text-xl font-bold text-white">Cockpit GeoOptions</h1>
<h1 className="text-xl font-bold text-white">GeoOptions Cockpit</h1>
<p className="text-xs text-slate-500 mt-0.5">
{format(new Date(), "EEEE d MMMM yyyy · HH:mm", { locale: fr })}
</p>
@@ -235,10 +235,10 @@ export default function Dashboard() {
<div className="card col-span-1">
<div className="flex items-center justify-between mb-2">
<div className="section-title flex items-center gap-1 mb-0">
<Globe className="w-3 h-3" /> Risque Géopolitique
<Globe className="w-3 h-3" /> Geopolitical Risk
</div>
<Link to="/geo" className="flex items-center gap-0.5 text-[10px] text-slate-600 hover:text-slate-300 transition-colors">
News géo <ArrowUpRight className="w-2.5 h-2.5" />
Geo news <ArrowUpRight className="w-2.5 h-2.5" />
</Link>
</div>
{riskLoading ? (
@@ -259,12 +259,12 @@ export default function Dashboard() {
))}
</div>
</>
) : <div className="text-slate-500 text-xs">Backend requis</div>}
) : <div className="text-slate-500 text-xs">Backend required</div>}
</div>
{/* Radar */}
<div className="card col-span-1">
<div className="section-title">Cartographie risques</div>
<div className="section-title">Risk Map</div>
{radarData.length > 0 ? (
<ResponsiveContainer width="100%" height={160}>
<RadarChart data={radarData}>
@@ -273,18 +273,18 @@ export default function Dashboard() {
<Radar dataKey="score" stroke="#3b82f6" fill="#3b82f6" fillOpacity={0.25} />
</RadarChart>
</ResponsiveContainer>
) : <div className="h-40 flex items-center justify-center text-slate-600 text-xs">Chargement...</div>}
) : <div className="h-40 flex items-center justify-center text-slate-600 text-xs">Loading...</div>}
</div>
{/* Patterns — AI scores */}
<div className="card col-span-1 overflow-y-auto max-h-64">
<div className="section-title flex items-center gap-1">
<Brain className="w-3 h-3" /> Scores patterns
<Brain className="w-3 h-3" /> Pattern Scores
{allPatterns.length > 0 && <span className="text-slate-600 text-xs ml-auto font-normal">{allPatterns.length} patterns</span>}
</div>
<div className="space-y-1.5 mt-1">
{allPatterns.length === 0 ? (
<div className="text-slate-600 text-xs">Backend requis</div>
<div className="text-slate-600 text-xs">Backend required</div>
) : (
[...allPatterns]
.sort((a, b) => {
@@ -321,7 +321,7 @@ export default function Dashboard() {
{/* Calendrier */}
<div className="card col-span-1 space-y-3">
<div className="section-title flex items-center gap-1"><Clock className="w-3 h-3" /> Prochains catalyseurs</div>
<div className="section-title flex items-center gap-1"><Clock className="w-3 h-3" /> Upcoming Catalysts</div>
<div className="space-y-1.5">
{calendar?.slice(0, 4).map((ev, i) => (
<div key={i} className="flex items-center gap-2 text-xs">
@@ -397,7 +397,7 @@ export default function Dashboard() {
<div className="grid grid-cols-2 gap-2 flex-shrink-0">
{/* Left: Unrealized */}
<div className="bg-dark-700/40 rounded px-2.5 py-2 border border-slate-700/30">
<div className="text-[9px] text-slate-600 uppercase tracking-wide mb-1">Ouvertes</div>
<div className="text-[9px] text-slate-600 uppercase tracking-wide mb-1">Open</div>
{pnlView === 'simulated' ? (
<>
<div className={clsx('text-xl font-bold font-mono leading-none',
@@ -437,7 +437,7 @@ export default function Dashboard() {
{/* Right: Realized */}
<div className="bg-dark-700/40 rounded px-2.5 py-2 border border-slate-700/30">
<div className="text-[9px] text-slate-600 uppercase tracking-wide mb-1">Réali</div>
<div className="text-[9px] text-slate-600 uppercase tracking-wide mb-1">Realized</div>
{pnlView === 'simulated' ? (
<>
<div className={clsx('text-xl font-bold font-mono leading-none',
@@ -451,10 +451,10 @@ export default function Dashboard() {
closedProfitEur >= 0 ? 'text-emerald-500' : 'text-red-400')}>
{closedCapital > 0
? `${closedProfitEur >= 0 ? '+' : ''}${closedProfitEur.toFixed(0)}`
: closedTrades.length > 0 ? 'sans capital' : ''}
: closedTrades.length > 0 ? 'no capital' : ''}
</div>
<div className="text-[10px] text-slate-500 mt-1">
{closedTrades.length} fermés · <span className="text-emerald-500">{closedWinners}</span>{' '}
{closedTrades.length} closed · <span className="text-emerald-500">{closedWinners}</span>{' '}
<span className="text-red-400">{closedLosers}</span>
</div>
</>
@@ -473,7 +473,7 @@ export default function Dashboard() {
</div>
)}
<div className="text-[10px] text-slate-500 mt-1">
{pf?.closed_positions ?? 0} fermées
{pf?.closed_positions ?? 0} closed
</div>
</>
)}
@@ -483,7 +483,7 @@ export default function Dashboard() {
{/* Total line */}
<div className="mt-1.5 flex items-center justify-between text-[10px] flex-shrink-0">
<span className="text-slate-600">
Investi{isEstimated ? ' ~' : ''} {pnlView === 'simulated'
Invested{isEstimated ? ' ~' : ''} {pnlView === 'simulated'
? (totalCapital > 0 ? `${totalCapital.toFixed(0)}` : '—')
: (pfInvest != null ? `${pfInvest.toFixed(0)}` : '—')}
</span>
@@ -509,7 +509,7 @@ export default function Dashboard() {
const gradId = 'pnlGrad'
return (
<div className="mt-1.5 pt-1.5 border-t border-slate-700/30">
<div className="text-[9px] text-slate-600 mb-0.5">Courbe PnL historique ({snaps.length} pts)</div>
<div className="text-[9px] text-slate-600 mb-0.5">Historical PnL curve ({snaps.length} pts)</div>
<ResponsiveContainer width="100%" height={58}>
<AreaChart data={chartData} margin={{ top: 2, right: 0, left: -38, bottom: 0 }}>
<defs>
@@ -552,14 +552,14 @@ export default function Dashboard() {
return (
<Link to="/risk" className="card flex flex-col h-[288px] overflow-hidden hover:border-slate-600/60 transition-all cursor-pointer">
<div className="flex items-center justify-between mb-1">
<span className="section-title mb-0 text-[10px]">🛡 Risque</span>
<span className="section-title mb-0 text-[10px]">🛡 Risk</span>
<div className="flex items-center gap-1.5">
<ViewToggle value={riskView} onChange={setRiskViewPersist} />
<ArrowUpRight className="w-3 h-3 text-slate-600" />
</div>
</div>
<div className={clsx('text-lg font-bold mt-0.5', alertCount > 0 ? 'text-red-400' : 'text-emerald-400')}>
{alertCount > 0 ? `${alertCount} alerte${alertCount > 1 ? 's' : ''}` : 'OK'}
{alertCount > 0 ? `${alertCount} alert${alertCount > 1 ? 's' : ''}` : 'OK'}
</div>
{sortedClasses.length > 0 && (
<div className="mt-2 space-y-1">
@@ -568,11 +568,11 @@ export default function Dashboard() {
<span className="text-[9px] text-slate-500 w-12 truncate capitalize">{cls}</span>
<div className="flex-1 h-2 bg-dark-700 rounded-full overflow-hidden flex">
{data.bullish > 0 && (
<div className="h-full rounded-l-full" title={`${data.bullish} haussier`}
<div className="h-full rounded-l-full" title={`${data.bullish} bullish`}
style={{ width: `${(data.bullish / openCount) * 100}%`, background: ASSET_COLORS[cls] ?? '#3b82f6', opacity: 0.9 }} />
)}
{data.bearish > 0 && (
<div className="h-full rounded-r-full" title={`${data.bearish} baissier`}
<div className="h-full rounded-r-full" title={`${data.bearish} bearish`}
style={{ width: `${(data.bearish / openCount) * 100}%`, background: '#ef4444', opacity: 0.7 }} />
)}
</div>
@@ -580,15 +580,15 @@ export default function Dashboard() {
</div>
))}
<div className="flex items-center gap-1 text-[9px] text-slate-700 pt-0.5">
<span className="inline-block w-2 h-1.5 rounded-sm bg-blue-500/60"></span>haussier
<span className="inline-block w-2 h-1.5 rounded-sm bg-red-500/60 ml-1"></span>baissier
{conflictCount > 0 && <span className="ml-auto text-red-400">{conflictCount} conflit{conflictCount > 1 ? 's' : ''}</span>}
<span className="inline-block w-2 h-1.5 rounded-sm bg-blue-500/60"></span>bullish
<span className="inline-block w-2 h-1.5 rounded-sm bg-red-500/60 ml-1"></span>bearish
{conflictCount > 0 && <span className="ml-auto text-red-400">{conflictCount} conflict{conflictCount > 1 ? 's' : ''}</span>}
</div>
</div>
)}
{sortedClasses.length === 0 && (
<div className="text-[10px] text-slate-600 mt-1">
{openCount > 0 ? `${openCount} positions` : 'Aucune position'}
{openCount > 0 ? `${openCount} positions` : 'No positions'}
</div>
)}
{/* Latest VaR snapshot */}
@@ -629,7 +629,7 @@ export default function Dashboard() {
return (
<Link to="/journal" className="card flex flex-col h-[288px] overflow-hidden hover:border-slate-600/60 transition-all cursor-pointer">
<div className="flex items-center justify-between mb-2">
<span className="section-title mb-0 text-[10px]">🔄 Derniers Cycles</span>
<span className="section-title mb-0 text-[10px]">🔄 Recent Cycles</span>
<ArrowUpRight className="w-3 h-3 text-slate-600" />
</div>
{/* Column headers */}
@@ -638,7 +638,7 @@ export default function Dashboard() {
<span className="text-[8px] text-slate-700 w-8 shrink-0 text-right">Pat.</span>
<span className="text-[8px] text-slate-700 w-8 shrink-0 text-right">Log.</span>
<span className="text-[8px] text-slate-700 w-8 shrink-0 text-right">Clos</span>
<span className="text-[8px] text-slate-700 flex-1 truncate">Régime</span>
<span className="text-[8px] text-slate-700 flex-1 truncate">Regime</span>
</div>
{runs.length > 0 ? (
<div className="space-y-2.5 flex-1 overflow-hidden">
@@ -668,7 +668,7 @@ export default function Dashboard() {
})}
</div>
) : (
<div className="text-[10px] text-slate-600 mt-1">Aucun cycle enregistré</div>
<div className="text-[10px] text-slate-600 mt-1">No cycles recorded</div>
)}
</Link>
)
@@ -694,38 +694,38 @@ export default function Dashboard() {
key: 'vix', label: 'VIX',
color: (v: number) => v < 18 ? 'text-emerald-400' : v < 25 ? 'text-yellow-400' : 'text-red-400',
fmt: (v: number) => v.toFixed(1),
hint: (v: number) => v < 18 ? '↓ risque bas' : v < 25 ? '↑ alerte' : '↑ crise',
hint: (v: number) => v < 18 ? '↓ low risk' : v < 25 ? '↑ alert' : '↑ crisis',
},
{
key: 'spx_vs_200d', label: 'S&P/200j',
key: 'spx_vs_200d', label: 'S&P/200d',
color: (v: number) => v >= 0 ? 'text-emerald-400' : 'text-red-400',
fmt: (v: number) => `${v >= 0 ? '+' : ''}${v.toFixed(1)}%`,
hint: (v: number) => v >= 5 ? 'bull fort' : v >= 0 ? 'bull mod.' : 'bear',
hint: (v: number) => v >= 5 ? 'strong bull' : v >= 0 ? 'mod. bull' : 'bear',
},
{
key: 'slope_10y3m', label: 'Pente 10Y-3M',
key: 'slope_10y3m', label: 'Slope 10Y-3M',
color: (v: number) => v >= 0.5 ? 'text-emerald-400' : v >= 0 ? 'text-yellow-400' : 'text-red-400',
fmt: (v: number) => `${v >= 0 ? '+' : ''}${v.toFixed(2)}`,
hint: (v: number) => v >= 0.5 ? 'normale' : v >= 0 ? 'plate' : 'inversée',
hint: (v: number) => v >= 0.5 ? 'normal' : v >= 0 ? 'flat' : 'inverted',
},
{
key: 'copper', label: 'Cuivre',
key: 'copper', label: 'Copper',
color: (v: number, chg: number) => chg >= 0 ? 'text-emerald-400' : 'text-red-400',
fmt: (v: number, chg: number) => `${chg >= 0 ? '+' : ''}${chg.toFixed(2)}%`,
hint: (v: number, chg: number) => chg >= 0 ? '↑ croissance' : '↓ ralentis.',
hint: (v: number, chg: number) => chg >= 0 ? '↑ growth' : '↓ slowing',
},
{
key: 'gold', label: 'Or',
key: 'gold', label: 'Gold',
color: (v: number, chg: number) => chg >= 1 ? 'text-amber-400' : chg <= -1 ? 'text-emerald-400' : 'text-slate-400',
fmt: (v: number, chg: number) => `${chg >= 0 ? '+' : ''}${chg.toFixed(2)}%`,
hint: (v: number, chg: number) => chg >= 1 ? '↑ refuge' : chg <= -1 ? '↓ risk-on' : '≈ neutre',
hint: (v: number, chg: number) => chg >= 1 ? '↑ safe haven' : chg <= -1 ? '↓ risk-on' : '≈ neutral',
},
]
return (
<Link to="/macro" className="card flex flex-col h-[288px] overflow-hidden hover:border-slate-600/60 transition-all cursor-pointer">
<div className="flex items-center justify-between mb-1">
<span className="section-title mb-0 text-[10px]">🌐 Régime Macro</span>
<span className="section-title mb-0 text-[10px]">🌐 Macro Regime</span>
<ArrowUpRight className="w-3 h-3 text-slate-600" />
</div>
<div className={clsx('text-base font-bold mt-1', colorClass)}>
@@ -834,7 +834,7 @@ export default function Dashboard() {
})}
</div>
) : (
<div className="text-[10px] text-slate-600 mt-1">Aucun trade loggé</div>
<div className="text-[10px] text-slate-600 mt-1">No trades logged</div>
)}
</Link>
)
@@ -849,7 +849,7 @@ export default function Dashboard() {
return (
<Link to="/journal" className="card block hover:border-slate-600/60 transition-all cursor-pointer">
<div className="flex items-center justify-between mb-1">
<span className="section-title mb-0 text-[10px]">📥 Derniers trades loggés</span>
<span className="section-title mb-0 text-[10px]">📥 Latest Logged Trades</span>
<ArrowUpRight className="w-3 h-3 text-slate-600" />
</div>
{recent.length > 0 ? (
@@ -873,7 +873,7 @@ export default function Dashboard() {
{pnl >= 0 ? '+' : ''}{pnl.toFixed(1)}%
</span>
) : (
<span className="text-slate-700 shrink-0 text-[9px]">en cours</span>
<span className="text-slate-700 shrink-0 text-[9px]">in progress</span>
)}
</div>
{entryDate && (
@@ -884,7 +884,7 @@ export default function Dashboard() {
})}
</div>
) : (
<div className="text-[10px] text-slate-600 mt-1">Aucun trade loggé</div>
<div className="text-[10px] text-slate-600 mt-1">No trades logged</div>
)}
</Link>
)
@@ -899,7 +899,7 @@ export default function Dashboard() {
return (
<Link to="/patterns" className="card block hover:border-slate-600/60 transition-all cursor-pointer">
<div className="flex items-center justify-between mb-1">
<span className="section-title mb-0 text-[10px]"> Derniers Patterns ajoutés</span>
<span className="section-title mb-0 text-[10px]"> Latest Patterns Added</span>
<ArrowUpRight className="w-3 h-3 text-slate-600" />
</div>
{recent.length > 0 ? (
@@ -932,7 +932,7 @@ export default function Dashboard() {
})}
</div>
) : (
<div className="text-[10px] text-slate-600 mt-1">Aucun pattern enregistré</div>
<div className="text-[10px] text-slate-600 mt-1">No patterns recorded</div>
)}
</Link>
)
@@ -969,7 +969,7 @@ export default function Dashboard() {
})}
</div>
) : (
<div className="text-[10px] text-slate-600 mt-1">Chargement des news</div>
<div className="text-[10px] text-slate-600 mt-1">Loading news</div>
)}
</Link>
)
@@ -982,7 +982,7 @@ export default function Dashboard() {
<div key={cls} className="card">
<div className="section-title">{assetEmoji[cls]} {cls}</div>
{allQuotes?.[cls]?.map(q => <QuoteRow key={q.symbol} q={q} />) ?? (
<div className="text-xs text-slate-600">Chargement...</div>
<div className="text-xs text-slate-600">Loading...</div>
)}
</div>
))}