# 需求文档：UI文案国际化（i18n）

## 引言

《坦克探险》微信小游戏需要支持中英文双语UI。根据用户所在区域自动展示对应语言的文案，中文地区显示中文，其他地区显示英文。

---

## 技术方案

### 1. i18n 模块结构

在 `js/i18n/` 目录下创建以下文件：

- **`I18n.js`** — 核心管理器，负责语言检测和文案获取
- **`zh.js`** — 中文语言包
- **`en.js`** — 英文语言包

### 2. 语言检测

通过微信 `wx.getSystemInfoSync().language` 自动检测：
- `zh_CN`、`zh_TW`、`zh_HK` 等以 `zh` 开头 → 使用中文
- 其他 → 使用英文（默认 fallback）

### 3. 使用方式

各场景文件通过 `const { t } = require('../i18n/I18n');` 引入翻译函数：
- 简单文案：`t('menu.title')` → `'坦克探险'` / `'Tank Adventure'`
- 带参数模板：`t('pvp.hp', { count: 3 })` → `'生命 x3'` / `'HP x3'`

### 4. Key 命名规范

按场景分组，使用点号分隔：
- `menu.*` — 主菜单
- `room.*` — 双人对战房间
- `teamRoom.*` — 3v3团队房间
- `pvp.*` — 双人对战游戏
- `team.*` — 3v3团队游戏
- `pvpResult.*` — 双人对战结算
- `teamResult.*` — 3v3团队结算
- `game.*` — 经典模式
- `common.*` — 通用文案

---

## 需求

### 需求 1：创建 i18n 核心模块

**用户故事：** 作为开发者，我需要一个 i18n 模块来管理多语言文案，支持自动语言检测和带参数的文案模板。

#### 验收标准

1. 创建 `js/i18n/I18n.js`，提供 `t(key, params)` 函数
2. 创建 `js/i18n/zh.js`，包含所有中文文案
3. 创建 `js/i18n/en.js`，包含所有英文文案
4. 通过 `wx.getSystemInfoSync().language` 自动检测语言
5. 支持 `{variable}` 占位符插值
6. 缺失 key 时 fallback 到英文，仍缺失则返回 key 本身

---

### 需求 2：主菜单场景（MenuScene）i18n 化

#### 验收标准

| Key | 中文 | 英文 |
|-----|------|------|
| `menu.title` | 坦克探险 | Tank Adventure |
| `menu.subtitle` | 经典坦克对战 | TANK WAR |
| `menu.classic` | 经典模式 | Classic |
| `menu.endless` | 无尽模式 | Endless |
| `menu.pvp` | 双人对战 | PVP |
| `menu.team3v3` | 3v3 对战 | 3v3 Battle |
| `menu.ranking` | 排行榜 | Ranking |
| `menu.settings` | 设置 | Settings |

---

### 需求 3：双人对战房间场景（RoomScene）i18n 化

#### 验收标准

| Key | 中文 | 英文 |
|-----|------|------|
| `room.title` | 双人对战 | PVP Battle |
| `room.idleHint` | 创建房间或输入房间号加入 | Create a room or join with a code |
| `room.create` | 创建房间 | Create Room |
| `room.join` | 加入房间 | Join Room |
| `room.connecting` | 连接中{dots} | Connecting{dots} |
| `room.roomCode` | 房间号： | Room Code: |
| `room.waiting` | 等待对手加入{dots} | Waiting for opponent{dots} |
| `room.shareHint` | 将房间号分享给好友 | Share the room code with your friend |
| `room.inputCode` | 输入房间号： | Enter Room Code: |
| `room.opponentFound` | 对手已找到！ | Opponent found! |
| `room.starting` | 即将开始... | Game starting... |
| `room.tapBack` | 点击任意位置返回 | Tap anywhere to go back |
| `common.back` | ← 返回 | ← Back |
| `common.joinBtn` | 加入 | Join |
| `common.cannotConnect` | 无法连接服务器 | Cannot connect to server |
| `common.connectFailed` | 连接失败 | Connection failed |
| `common.disconnected` | 与服务器断开连接 | Disconnected from server |

---

### 需求 4：3v3 团队房间场景（TeamRoomScene）i18n 化

#### 验收标准

| Key | 中文 | 英文 |
|-----|------|------|
| `teamRoom.title` | 3v3 团队对战 | 3v3 Team Battle |
| `teamRoom.chooseMode` | 选择游戏方式 | Choose how to play |
| `teamRoom.createTeam` | 🎮 组队开黑 | 🎮 Create Team |
| `teamRoom.soloMatch` | ⚡ 快速匹配 | ⚡ Quick Match |
| `teamRoom.teamId` | 队伍：{id} | Team: {id} |
| `teamRoom.leader` | 队长 | Leader |
| `teamRoom.ready` | ✓ 已准备 | ✓ Ready |
| `teamRoom.notReady` | 未准备 | Not Ready |
| `teamRoom.emptySlot` | 空位 | Empty |
| `teamRoom.invite` | 📨 邀请好友 | 📨 Invite |
| `teamRoom.startMatch` | 🔍 开始匹配 | 🔍 Start Match |
| `teamRoom.disband` | 解散队伍 | Disband |
| `teamRoom.readyBtn` | ✓ 准备 | ✓ Ready |
| `teamRoom.cancelReady` | 取消准备 | Cancel Ready |
| `teamRoom.leaveTeam` | 退出队伍 | Leave Team |
| `teamRoom.matching` | 匹配中{dots} | Matching{dots} |
| `teamRoom.waitTime` | 已等待 {seconds} 秒 | Waited {seconds}s |
| `teamRoom.cancelMatch` | 取消匹配 | Cancel Match |
| `teamRoom.matchFound` | 对手已找到！ | Match found! |
| `teamRoom.enterBattle` | 即将进入战斗... | Entering battle... |
| `teamRoom.tapBack` | 点击任意位置返回 | Tap anywhere to go back |
| `teamRoom.shareTitle` | 坦克3v3，速来开黑！ | Tank 3v3, join the battle! |
| `common.kicked` | 你已被踢出队伍 | You have been kicked from the team |

---

### 需求 5：双人对战游戏场景（PvpGameScene）i18n 化

#### 验收标准

| Key | 中文 | 英文 |
|-----|------|------|
| `pvp.playerLabel` | P{slot} (我方) | P{slot} (You) |
| `pvp.hp` | 生命 x{count} | HP x{count} |
| `pvp.kills` | 击杀：{count} | Kills: {count} |
| `common.paused` | 暂停 | PAUSED |
| `common.tapContinue` | 点击继续 | Tap to continue |
| `pvp.youWin` | 你赢了！ | YOU WIN! |
| `pvp.draw` | 平局 | DRAW |
| `pvp.youLose` | 你输了 | YOU LOSE |

---

### 需求 6：3v3 团队对战游戏场景（TeamGameScene）i18n 化

#### 验收标准

| Key | 中文 | 英文 |
|-----|------|------|
| `team.teamA` | A队 | Team A |
| `team.teamB` | B队 | Team B |
| `team.myTeam` | 我方：{team}队 | You: {team} Team |
| `team.killDeath` | 杀:{kills} 亡:{deaths} | K:{kills} D:{deaths} |
| `team.respawn` | {seconds}秒后重生 | Respawning in {seconds}s |
| `team.victory` | 胜利！ | VICTORY! |
| `team.defeat` | 失败 | DEFEAT |
| `team.baseHpSummary` | A队：{hpA} 生命  \|  B队：{hpB} 生命 | Team A: {hpA} HP  \|  Team B: {hpB} HP |
| `team.disconnectTitle` | ⚠ 连接断开 | ⚠ Connection Lost |
| `team.reconnecting` | 重连中{dots} ({attempts}/{max}) | Reconnecting{dots} ({attempts}/{max}) |
| `team.reconnectHint` | 请稍候，您的坦克将由AI代管 | Please wait, your tank will be controlled by AI |

---

### 需求 7：双人对战结算场景（PvpResultScene）i18n 化

#### 验收标准

| Key | 中文 | 英文 |
|-----|------|------|
| `pvpResult.title` | 对战结果 | MATCH RESULT |
| `pvpResult.victory` | 🏆 胜利！ | 🏆 VICTORY! |
| `pvpResult.draw` | ⚔️ 平局 | ⚔️ DRAW |
| `pvpResult.defeat` | 💀 失败 | 💀 DEFEAT |
| `pvpResult.kills` | 击杀 | Kills |
| `pvpResult.lives` | 生命 | Lives |
| `pvpResult.timeRemaining` | 剩余时间：{time} | Time remaining: {time} |
| `pvpResult.rematch` | 再来一局 | Rematch |
| `pvpResult.backMenu` | 返回菜单 | Back to Menu |

---

### 需求 8：3v3 团队结算场景（TeamResultScene）i18n 化

#### 验收标准

| Key | 中文 | 英文 |
|-----|------|------|
| `teamResult.title` | 3v3 对战结果 | 3v3 MATCH RESULT |
| `teamResult.victory` | 🏆 胜利！ | 🏆 VICTORY! |
| `teamResult.defeat` | 💀 失败 | 💀 DEFEAT |
| `teamResult.teamAHp` | A队：{hp} 生命 | Team A: {hp} HP |
| `teamResult.teamBHp` | B队：{hp} 生命 | Team B: {hp} HP |
| `teamResult.baseDestroyed` | 基地被摧毁 | Base Destroyed |
| `teamResult.disconnectedReason` | 断线 | Disconnected |
| `teamResult.teamAHeader` | A队 | Team A |
| `teamResult.teamBHeader` | B队 | Team B |
| `teamResult.myTeamSuffix` |  (我方) |  (You) |
| `teamResult.player` | 玩家 | Player |
| `teamResult.k` | 杀 | K |
| `teamResult.d` | 亡 | D |
| `teamResult.a` | 助 | A |
| `teamResult.dmg` | 伤害 | DMG |
| `teamResult.bot` | 🤖 机器人 | 🤖 Bot |
| `teamResult.duration` | 对战时长：{time} | Match duration: {time} |
| `teamResult.mvp` | ⭐ MVP：{name}（{kills} 击杀） | ⭐ MVP: {name} ({kills} kills) |
| `teamResult.rankUp` | 📈 积分 +{points} | 📈 Rank +{points} |
| `teamResult.mvpBonus` | （MVP加成 +5） | (MVP bonus +5) |
| `teamResult.rankDown` | 📉 积分 -{points} | 📉 Rank -{points} |
| `teamResult.rematch` | 再来一局 | Rematch |
| `teamResult.backMenu` | 返回菜单 | Back to Menu |

---

### 需求 9：经典模式游戏场景（GameScene）i18n 化

#### 验收标准

| Key | 中文 | 英文 |
|-----|------|------|
| `game.level` | 第 {level} 关 | Level {level} |
| `game.hp` | 生命 x{count} | HP x{count} |
| `game.fireLevel` | LV{level} | LV{level} |
| `game.enemies` | 敌人: {count} | Enemies: {count} |
| `game.score` | {score}分 | {score}pts |
| `game.gameOver` | 游戏结束 | GAME OVER |
| `game.stageClear` | 关卡通过！ | STAGE CLEAR! |

---

## 边界情况与技术约束

### 边界情况

1. **中文字体渲染**：Canvas 中使用 `'Arial'` 字体渲染中文时，微信小游戏环境下系统会自动 fallback 到系统中文字体，无需额外处理。
2. **文案长度变化**：中英文文案长度不同，替换后需确认UI布局不会溢出或错位。
3. **`GameScene` 中的字符串比较**：`text === '游戏结束'` 改为 `text === t('game.gameOver')`，确保逻辑不受语言影响。
4. **错误消息来源**：部分错误消息可能来自服务端（如 `data.message`），本次仅替换客户端硬编码的文案。

### 技术约束

1. 所有文案替换涉及 `js/scenes/` 目录下的场景文件和新建的 `js/i18n/` 模块。
2. 替换操作不应影响游戏逻辑，仅修改展示层的字符串。
3. 不需要在设置页面增加语言切换选项，完全依赖微信系统语言自动检测。

### 成功标准

1. 中文区域用户看到全中文UI，非中文区域用户看到全英文UI。
2. 替换后游戏功能正常，无因文案修改导致的逻辑错误。
3. 文案在各场景中布局合理，无溢出或错位现象。
