Implement internationalization support by adding translation functionality and updating UI components to use translated strings. Add language configuration in .env.example and integrate translations across various strategy and UI components.

This commit is contained in:
discountry
2025-12-09 00:35:28 +08:00
parent 03df1006cc
commit 85e7f245c0
17 changed files with 1166 additions and 295 deletions
+69 -27
View File
@@ -6,6 +6,7 @@ import { buildAdapterFromEnv } from "../exchanges/resolve-from-env";
import { GridEngine, type GridEngineSnapshot } from "../strategy/grid-engine";
import { DataTable, type TableColumn } from "./components/DataTable";
import { formatNumber } from "../utils/format";
import { t } from "../i18n";
interface GridAppProps {
onExit: () => void;
@@ -59,8 +60,8 @@ export function GridApp({ onExit }: GridAppProps) {
if (error) {
return (
<Box flexDirection="column" padding={1}>
<Text color="red">: {error.message}</Text>
<Text color="gray"></Text>
<Text color="red">{t("common.startFailed", { message: error.message })}</Text>
<Text color="gray">{t("common.checkEnv")}</Text>
</Box>
);
}
@@ -68,17 +69,17 @@ export function GridApp({ onExit }: GridAppProps) {
if (!snapshot) {
return (
<Box padding={1}>
<Text></Text>
<Text>{t("grid.initializing")}</Text>
</Box>
);
}
const feedStatus = snapshot.feedStatus;
const feedEntries: Array<{ key: keyof typeof feedStatus; label: string }> = [
{ key: "account", label: "账户" },
{ key: "orders", label: "订单" },
{ key: "depth", label: "深度" },
{ key: "ticker", label: "行情" },
{ key: "account", label: t("maker.feed.account") },
{ key: "orders", label: t("maker.feed.orders") },
{ key: "depth", label: t("maker.feed.depth") },
{ key: "ticker", label: t("maker.feed.ticker") },
];
const stopReason = snapshot.running ? null : snapshot.stopReason;
const lastLogs = snapshot.tradeLog.slice(-5);
@@ -112,71 +113,112 @@ export function GridApp({ onExit }: GridAppProps) {
price: order.price,
amount: formatNumber(order.amount, 4),
}));
const statusLabel = snapshot.running ? t("status.running") : t("status.paused");
const directionLabel =
snapshot.direction === "both"
? t("grid.direction.both")
: snapshot.direction === "long"
? t("grid.direction.long")
: t("grid.direction.short");
return (
<Box flexDirection="column" paddingX={1}>
<Box flexDirection="column" marginBottom={1}>
<Text color="cyanBright">Grid Strategy Dashboard</Text>
<Text color="cyanBright">{t("grid.title")}</Text>
<Text>
: {exchangeName} : {snapshot.symbol} : {snapshot.running ? "运行中" : "暂停"} : {snapshot.direction}
{t("grid.headerLine", {
exchange: exchangeName,
symbol: snapshot.symbol,
status: statusLabel,
direction: directionLabel,
})}
</Text>
<Text>
: {formatNumber(snapshot.lastPrice, 4)} : {formatNumber(snapshot.lowerPrice, 4)} : {formatNumber(snapshot.upperPrice, 4)} : {snapshot.gridLines.length}
{t("grid.priceLine", {
lastPrice: formatNumber(snapshot.lastPrice, 4),
lower: formatNumber(snapshot.lowerPrice, 4),
upper: formatNumber(snapshot.upperPrice, 4),
count: snapshot.gridLines.length,
})}
</Text>
<Text color="gray">:
<Text color="gray">
{t("grid.dataStatus")}
{feedEntries.map((entry, index) => (
<Text key={entry.key} color={feedStatus[entry.key] ? "green" : "red"}>
{index === 0 ? " " : " "}
{entry.label}
</Text>
))}
Esc
{" | "}
{t("common.backHint")}
</Text>
{stopReason ? <Text color="yellow">: {stopReason}</Text> : null}
{stopReason ? <Text color="yellow">{t("grid.stopReason", { reason: stopReason })}</Text> : null}
</Box>
<Box flexDirection="row" marginBottom={1}>
<Box flexDirection="column" marginRight={4}>
<Text color="greenBright"></Text>
<Text color="greenBright">{t("grid.configTitle")}</Text>
<Text>
: {formatNumber(gridConfig.orderSize, 6)} : {formatNumber(gridConfig.maxPositionSize, 6)}
{t("grid.configSize", {
orderSize: formatNumber(gridConfig.orderSize, 6),
maxPosition: formatNumber(gridConfig.maxPositionSize, 6),
})}
</Text>
<Text>
: {(gridConfig.stopLossPct * 100).toFixed(2)}% : {(gridConfig.restartTriggerPct * 100).toFixed(2)}% : {gridConfig.autoRestart ? "启用" : "关闭"}
{t("grid.configRisk", {
stopLoss: (gridConfig.stopLossPct * 100).toFixed(2),
restart: (gridConfig.restartTriggerPct * 100).toFixed(2),
autoRestart: gridConfig.autoRestart ? t("common.enabled") : t("common.disabled"),
})}
</Text>
<Text>
: {gridConfig.refreshIntervalMs} ms
{t("grid.refreshInterval", { interval: gridConfig.refreshIntervalMs })}
</Text>
</Box>
<Box flexDirection="column">
<Text color="greenBright"></Text>
<Text color="greenBright">{t("common.section.position")}</Text>
{hasPosition ? (
<>
<Text>
: {position.positionAmt > 0 ? "多" : "空"} : {formatNumber(Math.abs(position.positionAmt), 6)} : {formatNumber(position.entryPrice, 4)}
{t("grid.positionLine", {
direction: position.positionAmt > 0 ? t("common.direction.long") : t("common.direction.short"),
qty: formatNumber(Math.abs(position.positionAmt), 6),
avgPrice: formatNumber(position.entryPrice, 4),
})}
</Text>
<Text>
: {formatNumber(position.unrealizedProfit, 4)} : {formatNumber(position.markPrice, 4)}
{t("grid.unrealizedLine", {
pnl: formatNumber(position.unrealizedProfit, 4),
mark: formatNumber(position.markPrice, 4),
})}
</Text>
</>
) : (
<Text color="gray"></Text>
<Text color="gray">{t("common.noPosition")}</Text>
)}
</Box>
</Box>
<Box flexDirection="column" marginBottom={1}>
<Text color="yellow">线</Text>
{gridRows.length > 0 ? <DataTable columns={gridColumns} rows={gridRows} /> : <Text color="gray">线</Text>}
<Text color="yellow">{t("grid.linesTitle")}</Text>
{gridRows.length > 0 ? (
<DataTable columns={gridColumns} rows={gridRows} />
) : (
<Text color="gray">{t("grid.noLines")}</Text>
)}
</Box>
<Box flexDirection="column" marginBottom={1}>
<Text color="yellow"></Text>
{desiredRows.length > 0 ? <DataTable columns={desiredColumns} rows={desiredRows} /> : <Text color="gray"></Text>}
<Text color="yellow">{t("maker.targetOrders")}</Text>
{desiredRows.length > 0 ? (
<DataTable columns={desiredColumns} rows={desiredRows} />
) : (
<Text color="gray">{t("maker.noTargetOrders")}</Text>
)}
</Box>
<Box flexDirection="column">
<Text color="yellow"></Text>
<Text color="yellow">{t("common.section.recent")}</Text>
{lastLogs.length > 0 ? (
lastLogs.map((item, index) => (
<Text key={`${item.time}-${index}`}>
@@ -184,7 +226,7 @@ export function GridApp({ onExit }: GridAppProps) {
</Text>
))
) : (
<Text color="gray"></Text>
<Text color="gray">{t("common.noLogs")}</Text>
)}
</Box>
</Box>