diff --git a/src/features/hall/hall-betting-grid.tsx b/src/features/hall/hall-betting-grid.tsx
index d75469c..21973b8 100644
--- a/src/features/hall/hall-betting-grid.tsx
+++ b/src/features/hall/hall-betting-grid.tsx
@@ -1628,14 +1628,15 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot }
) : null}
-
+
+
@@ -1727,8 +1728,12 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot }
...current,
[column.key]: checked === true,
}))}
- aria-label={`${playColumnHeaderLabel(column.play, playCategory(column.play.play_code), column.digitSlot, t)} 同步本列金额`}
- title="同步本列金额"
+ aria-label={t("hall.table.syncColumnAmount", {
+ column: playColumnHeaderLabel(column.play, playCategory(column.play.play_code), column.digitSlot, t),
+ })}
+ title={t("hall.table.syncColumnAmount", {
+ column: playColumnHeaderLabel(column.play, playCategory(column.play.play_code), column.digitSlot, t),
+ })}
className="mx-auto mt-1 size-3.5"
/>
@@ -1745,15 +1750,15 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot }
event.currentTarget.value = "";
}}
className="mx-auto mt-1 h-5 w-full rounded border border-[#d7e1f3] bg-white px-0.5 text-[10px] font-semibold text-[#304f86]"
- aria-label="全选种类"
- title="统一设置所有行的种类"
+ aria-label={t("hall.table.selectAllTypes")}
+ title={t("hall.table.setAllTypes")}
>
-
-
-
-
-
- {activeCategory === "D4" ? : null}
+
+
+
+
+
+ {activeCategory === "D4" ? : null}
) : null}
@@ -1770,7 +1775,7 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot }
checked={checked}
disabled={tableDisabled}
onCheckedChange={(next) => toggleProviderColumn(provider.code, next === true)}
- aria-label={`全选 ${provider.name}`}
+ aria-label={t("hall.table.selectAllProvider", { provider: provider.name })}
className="mx-auto mt-1 border-slate-500 bg-white data-checked:border-slate-800 data-checked:bg-slate-800"
/>
@@ -1816,74 +1821,63 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot }
))}
- {!isMobile ? (
-
- ) : null}
+
+
+ {!isMobile ? (
+
+ ) : null}
- {!isMobile ? (
-
-
- {t("hall.table.draftTotal")}
-
-
- {formatMinorAsCurrency(debouncedSummary.actual, currencyCode)}
-
-
- ) : null}
-
{sealedBetUi ? (
{t("hall.table.sealedHint")}
) : null}
- {!isMobile ? (
-
- ) : null}
{isMobile ? (
@@ -2140,13 +2134,13 @@ const DraftRowItem = memo(function DraftRowItem({
disabled={tableDisabled}
onChange={(event) => updateRowSelectionType(row.id, event.target.value as DraftRow["selectionType"])}
className="h-8 w-full rounded-md border border-[#e1e8f3] bg-white px-1 text-[10px] font-semibold text-[#304f86]"
- aria-label={`第 ${index + 1} 行种类`}
+ aria-label={t("hall.table.selectionTypeForRow", { row: index + 1 })}
>
-
-
-
-
- {activeCategory === "D4" ?
: null}
+
+
+
+
+ {activeCategory === "D4" ?
: null}
) : null}
diff --git a/src/hooks/use-token-refresh.ts b/src/hooks/use-token-refresh.ts
index 81cbb58..81c779a 100644
--- a/src/hooks/use-token-refresh.ts
+++ b/src/hooks/use-token-refresh.ts
@@ -1,4 +1,5 @@
import { useCallback, useEffect, useRef } from "react";
+import { useTranslation } from "react-i18next";
import { parseJwtExp } from "@/lib/jwt-payload";
import { usePlayerSessionStore } from "@/stores/player-session-store";
@@ -39,6 +40,7 @@ export function useTokenRefresh(): {
const setBearerToken = usePlayerSessionStore((state) => state.setBearerToken);
const setServerError = useErrorStore((state) => state.setServerError);
const clearServerError = useErrorStore((state) => state.clearServerError);
+ const { t } = useTranslation("player");
const refreshTimerRef = useRef
(null);
const retryCountRef = useRef(0);
@@ -89,7 +91,7 @@ export function useTokenRefresh(): {
*/
const refreshToken = useCallback(async (): Promise => {
if (retryCountRef.current >= MAX_RETRY) {
- setServerError(true, "Token 刷新失败,请返回主站重新进入");
+ setServerError(true, t("token.refreshFailed"));
return;
}
@@ -102,10 +104,10 @@ export function useTokenRefresh(): {
if (pendingRefreshRef.current !== attemptId) return;
retryCountRef.current += 1;
if (retryCountRef.current >= MAX_RETRY) {
- setServerError(true, "Token 刷新失败,请返回主站重新进入");
+ setServerError(true, t("token.refreshFailed"));
}
}, REFRESH_RESPONSE_TIMEOUT_MS);
- }, [clearServerError, requestParentRefresh, setServerError]);
+ }, [clearServerError, requestParentRefresh, setServerError, t]);
/**
* 监听主站 postMessage 发送的新 Token
diff --git a/src/i18n/locales/en/player.json b/src/i18n/locales/en/player.json
index e9b4666..29882da 100644
--- a/src/i18n/locales/en/player.json
+++ b/src/i18n/locales/en/player.json
@@ -68,7 +68,8 @@
"warning": "Login expiring soon",
"remaining": "Remaining {{time}}",
"autoRenewing": "Auto-renewing...",
- "renewNow": "Renew now"
+ "renewNow": "Renew now",
+ "refreshFailed": "Token refresh failed. Please return to the main site and enter again."
},
"serverError": {
"title": "Server error",
@@ -235,6 +236,19 @@
"number": "Number",
"selectionType": "Type",
"rowTotal": "Row Total",
+ "selectAllTypes": "Select all types",
+ "setAllTypes": "Set the type for every row",
+ "selectAll": "Select all",
+ "syncColumnAmount": "Sync amount for {{column}}",
+ "selectAllProvider": "Select all {{provider}}",
+ "selectionTypeForRow": "Type for row {{row}}",
+ "selectionTypes": {
+ "straight": "Straight",
+ "reverse": "Reverse",
+ "full_cover": "Full cover",
+ "full_play": "Full play",
+ "half_play": "Half play"
+ },
"stake": "Stake Amount",
"rebate": "Commission / Rebate",
"actual": "Actual Deduction",
@@ -456,6 +470,7 @@
"wallet": "Main-site wallet player (transfers & wallet ledger)"
},
"creditAvailableAfter": "Available credit after",
+ "creditReleasedAmount": "Released credit {{amount}}",
"creditPaymentRecordOnly": "Billing payment record only; not counted in available credit",
"totalRecords": "{{total}} records",
"emptyLogs": "No wallet logs",
diff --git a/src/i18n/locales/ne/player.json b/src/i18n/locales/ne/player.json
index 96aaf2d..577e979 100644
--- a/src/i18n/locales/ne/player.json
+++ b/src/i18n/locales/ne/player.json
@@ -68,7 +68,8 @@
"warning": "लगइन समाप्त हुन लागेको छ",
"remaining": "बाँकी {{time}}",
"autoRenewing": "स्वचालित नवीकरण हुँदैछ...",
- "renewNow": "अहिले नवीकरण"
+ "renewNow": "अहिले नवीकरण",
+ "refreshFailed": "टोकन नवीकरण असफल भयो। कृपया मुख्य साइटमा फर्केर फेरि प्रवेश गर्नुहोस्।"
},
"serverError": {
"title": "सर्भर त्रुटि",
@@ -233,6 +234,21 @@
"table": {
"no": "नं.",
"number": "नम्बर",
+ "selectionType": "प्रकार",
+ "rowTotal": "पंक्ति जम्मा",
+ "selectAllTypes": "सबै प्रकार छान्नुहोस्",
+ "setAllTypes": "सबै पंक्तिको प्रकार सेट गर्नुहोस्",
+ "selectAll": "सबै छान्नुहोस्",
+ "syncColumnAmount": "{{column}} को रकम मिलाउनुहोस्",
+ "selectAllProvider": "सबै {{provider}} छान्नुहोस्",
+ "selectionTypeForRow": "पंक्ति {{row}} को प्रकार",
+ "selectionTypes": {
+ "straight": "स्ट्रेट",
+ "reverse": "रिभर्स",
+ "full_cover": "पूर्ण कभर",
+ "full_play": "पूर्ण खेल",
+ "half_play": "आधा खेल"
+ },
"stake": "बेट रकम",
"rebate": "कमिशन / रिबेट",
"actual": "वास्तविक कट्टा",
@@ -242,6 +258,9 @@
"totalBet": "जम्मा",
"totalRebate": "रिबेट",
"actualTotal": "अनुमानित कट्टा",
+ "orderSummary": "अर्डर सारांश",
+ "lineCount": "लाइनहरू",
+ "providerCount": "खुलाउने कम्पनीहरू",
"sealedHint": "बन्द: यो तालिका लक छ। कृपया अर्को इश्यू पर्खनुहोस्।",
"previewing": "पूर्वावलोकन...",
"submitBet": "बेट पेश गर्नुहोस्",
@@ -451,6 +470,7 @@
"wallet": "मुख्य साइट वालेट खेलाडी"
},
"creditAvailableAfter": "पछि उपलब्ध क्रेडिट",
+ "creditReleasedAmount": "मुक्त क्रेडिट {{amount}}",
"creditPaymentRecordOnly": "बिलिङ भुक्तानी रेकर्ड मात्र; उपलब्ध क्रेडिटमा गणना हुँदैन",
"emptyCreditLogs": "क्रेडिट लेनदेन छैन",
"creditFlow": {
diff --git a/src/i18n/locales/zh/player.json b/src/i18n/locales/zh/player.json
index ed4247c..9a03def 100644
--- a/src/i18n/locales/zh/player.json
+++ b/src/i18n/locales/zh/player.json
@@ -68,7 +68,8 @@
"warning": "登录即将过期",
"remaining": "剩余 {{time}}",
"autoRenewing": "正在自动续签...",
- "renewNow": "立即续签"
+ "renewNow": "立即续签",
+ "refreshFailed": "Token 刷新失败,请返回主站重新进入"
},
"serverError": {
"title": "服务器错误",
@@ -234,6 +235,19 @@
"number": "号码",
"selectionType": "种类",
"rowTotal": "本行合计",
+ "selectAllTypes": "全选种类",
+ "setAllTypes": "统一设置所有行的种类",
+ "selectAll": "全选",
+ "syncColumnAmount": "同步 {{column}} 本列金额",
+ "selectAllProvider": "全选 {{provider}}",
+ "selectionTypeForRow": "第 {{row}} 行种类",
+ "selectionTypes": {
+ "straight": "正字",
+ "reverse": "来回",
+ "full_cover": "全保",
+ "full_play": "全打",
+ "half_play": "半打"
+ },
"stake": "下注金额",
"rebate": "佣金 / 回水",
"actual": "实扣金额",
diff --git a/src/stores/error-store.ts b/src/stores/error-store.ts
index fab27dc..47aae5d 100644
--- a/src/stores/error-store.ts
+++ b/src/stores/error-store.ts
@@ -31,7 +31,7 @@ export const useErrorStore = create((set, get) => ({
// 首屏与 SSR 一致为 false;真实离线状态由 useNetworkStatus 在 mount 后同步
isOffline: false,
isServerError: false,
- serverErrorMessage: "服务器暂时不可用,请稍后重试",
+ serverErrorMessage: "",
currentErrorType: null,
// Actions
@@ -51,7 +51,7 @@ export const useErrorStore = create((set, get) => ({
setServerError: (isError: boolean, message?: string) => {
set({
isServerError: isError,
- serverErrorMessage: message || "服务器暂时不可用,请稍后重试",
+ serverErrorMessage: message ?? "",
});
// 更新当前错误类型 - 服务器错误优先级更高
if (isError) {