i18n(core): route order-coordinator and token/margin guards through t()

These files bypassed the i18n table entirely, so a user running LANG=en still
got Chinese order logs. Migrating them surfaced structural duplication too: the
five order paths each spelled out their own 'quantity invalid' string, now one
key parameterised by an order-kind label.

Also fixes a type that carried display text as its domain: TrendLabel was
'做多' | '做空' | '无信号', so the engine's snapshot value *was* the Chinese
string and English rendering depended on matching it. Now 'long' | 'short' |
'none', translated at the edge.

Order-coordinator tests asserted the Chinese literals, which is exactly what
made the gap invisible; they now assert the resolved key so they hold in either
language.

39 new translation keys. 271 pass; tsc and oxlint clean.
This commit is contained in:
discountry
2026-07-29 21:26:57 +08:00
parent 448a2f2615
commit b9331c516a
11 changed files with 227 additions and 62 deletions
+2 -1
View File
@@ -1,6 +1,7 @@
import { describe, expect, it, vi } from "vitest";
import { IsolatedMarginGuard } from "../src/strategy/common/isolated-margin-guard";
import type { AccountSnapshot } from "../src/exchanges/types";
import { t } from "../src/i18n";
const SYMBOL = "BTC-USD";
@@ -71,7 +72,7 @@ describe("IsolatedMarginGuard", () => {
});
expect(await guard.ensureIsolated()).toBe(true);
expect(changeMarginMode).toHaveBeenCalledWith({ symbol: SYMBOL, marginMode: "isolated" });
expect(logs.some(([, detail]) => detail.includes("已切换为逐仓模式"))).toBe(true);
expect(logs.some(([, detail]) => detail === t("log.margin.switched"))).toBe(true);
});
it("gives up after the confirm attempts run out", async () => {
+8 -4
View File
@@ -1,6 +1,7 @@
import { describe, expect, it, vi, beforeEach, afterAll } from "vitest";
import type { ExchangeAdapter } from "../src/exchanges/adapter";
import type { Order } from "../src/exchanges/types";
import { t } from "../src/i18n";
import type {
OrderContext,
OrderLockMap,
@@ -84,7 +85,7 @@ describe("order-coordinator", () => {
];
await deduplicateOrders(ctx, openOrders, "LIMIT", "BUY");
expect(adapter.cancelOrders).toHaveBeenCalledWith({ symbol: "BTCUSDT", orderIdList: [2] });
expect(log).toHaveBeenCalledWith("order", expect.stringContaining("去重撤销重复"));
expect(log).toHaveBeenCalledWith("order", t("log.order.dedupeCancelled", { type: "LIMIT", ids: "2" }));
});
it("places limit orders and records pending id", async () => {
@@ -112,7 +113,7 @@ describe("order-coordinator", () => {
lastPrice: 100,
});
expect(adapter.createOrder).toHaveBeenCalled();
expect(log).toHaveBeenCalledWith("stop", expect.stringContaining("STOP_MARKET"));
expect(log).toHaveBeenCalledWith("stop", t("log.order.stopPlaced", { side: "SELL", stopPrice: 99 }));
});
it("places trailing stop order", async () => {
@@ -125,7 +126,10 @@ describe("order-coordinator", () => {
callbackRate: 0.2,
});
expect(adapter.createOrder).toHaveBeenCalled();
expect(log).toHaveBeenCalledWith("order", expect.stringContaining("挂动态止盈单"));
expect(log).toHaveBeenCalledWith(
"order",
t("log.order.trailingPlaced", { side: "SELL", activation: 101, callbackRate: 0.2 })
);
});
it("market close cancels open orders before placing close order", async () => {
@@ -136,7 +140,7 @@ describe("order-coordinator", () => {
quantity: 1,
});
expect(adapter.createOrder).toHaveBeenCalled();
expect(log).toHaveBeenCalledWith("close", expect.stringContaining("市价平仓"));
expect(log).toHaveBeenCalledWith("close", t("log.order.closePlaced", { side: "SELL" }));
});
it("unlockOperating clears timers and pending", () => {
+2 -1
View File
@@ -1,5 +1,6 @@
import { describe, expect, it, vi, afterEach } from "vitest";
import { standxTokenConfig } from "../src/config";
import { t } from "../src/i18n";
import { TokenExpiryGuard } from "../src/strategy/common/token-expiry-guard";
const HOUR_MS = 3_600_000;
@@ -83,7 +84,7 @@ describe("TokenExpiryGuard", () => {
await guard.evaluate({ positionAmt: 0, openOrderCount: 0 });
await guard.evaluate({ positionAmt: 0, openOrderCount: 0 });
const entryLogs = logs.filter(
([type, detail]) => type === "info" && detail.includes("静默数据接收模式")
([type, detail]) => type === "info" && detail === t("log.token.silentEntered")
);
expect(entryLogs).toHaveLength(1);
});