# 需求文档: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. 文案在各场景中布局合理,无溢出或错位现象。