refactor: 完成全站国际化改造,统一多语言支持

此提交完成了全项目的国际化适配:
1. 新增多语言翻译文件与基础配置
2. 替换所有硬编码文本为i18n调用
3. 优化语言切换与文档语言同步逻辑
4. 重构部分业务逻辑以支持动态翻译
5. 移除过时代码与硬编码配置
This commit is contained in:
2026-05-15 10:41:14 +08:00
parent ac612cb32c
commit f2c7f5e4f1
53 changed files with 2179 additions and 767 deletions

View File

@@ -12,7 +12,7 @@ import {
} from "lucide-react";
import Image from "next/image";
import { useRouter, useSearchParams } from "next/navigation";
import { useCallback, useEffect, useState } from "react";
import { useCallback, useEffect, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import { getPlayerMe, getPlayerPing } from "@/api/player";
@@ -97,7 +97,6 @@ export function EntryGate() {
usePlayerSessionStore();
const [phase, setPhase] = useState<Phase>("loading");
const [progress, setProgress] = useState(0);
const [failureDetails, setFailureDetails] = useState<FailureRow[]>([]);
const [steps, setSteps] = useState<EntryStep[]>(initialSteps());
@@ -107,15 +106,11 @@ export function EntryGate() {
setSteps((prev) => prev.map((s) => (s.id === stepId ? { ...s, status } : s)));
}, []);
const calculateProgress = useCallback((currentSteps: EntryStep[]) => {
const doneCount = currentSteps.filter((s) => s.status === "done").length;
const inProgressCount = currentSteps.filter((s) => s.status === "in-progress").length;
return Math.round(((doneCount + inProgressCount * 0.5) / currentSteps.length) * 100);
}, []);
useEffect(() => {
setProgress(calculateProgress(steps));
}, [steps, calculateProgress]);
const progress = useMemo(() => {
const doneCount = steps.filter((s) => s.status === "done").length;
const inProgressCount = steps.filter((s) => s.status === "in-progress").length;
return Math.round(((doneCount + inProgressCount * 0.5) / steps.length) * 100);
}, [steps]);
const handleRetry = useCallback(() => {
setPhase("loading");

View File

@@ -1,6 +1,7 @@
"use client";
import { UserRound } from "lucide-react";
import { useTranslation } from "react-i18next";
import { cn } from "@/lib/utils";
import { usePlayerSessionStore } from "@/stores/player-session-store";
@@ -10,11 +11,12 @@ import { usePlayerSessionStore } from "@/stores/player-session-store";
*/
export function PlayerSessionBar({ className }: { className?: string }) {
const profile = usePlayerSessionStore((s) => s.profile);
const { t } = useTranslation("player");
const label =
profile?.nickname?.trim() ||
profile?.username?.trim() ||
(profile?.id != null ? `玩家 #${profile.id}` : null);
(profile?.id != null ? t("player.fallback", { id: profile.id }) : null);
return (
<div