feat: i18n system, lang switch component, english subtitles, UI improvements, roadmap update

This commit is contained in:
2026-06-09 15:40:51 +08:00
parent 6b4114af43
commit 59aed77199
16 changed files with 410 additions and 33 deletions

View File

@@ -568,11 +568,63 @@ GainNode 的 ramp 目标值 = `Math.min(bgmVolume, bgmDuckLevel × bgmVolume)`
### P10b 手柄导航(远期 P10b— 见 FUTURE.md
### P11 多语言字幕(待实现)
### P11 完整 i18n — 字幕 + UI 国际化,自制 useI18n ✅ 已完成 2026-06-09
目标:支持多语言字幕切换UI 文本国际化,同一个场景可有中/英/日等多个字幕文件。
目标:字幕和完整 UI 文本(选项、按钮、标签)支持多语言切换。使用自制 `useI18n()` 组合式函数,
零依赖,通过静态 import JSON 翻译文件实现。语言切换入口在主菜单和游戏内顶部栏两处。
**数据结构设计:**
**技术选型:自制 useI18n~25 行 TS不用 vue-i18n。**
无需 npm 包,`t(key)` 从静态 import 的 JSON 中按路径查找翻译文本,
`currentLang` 持久化到 localStorage跨会话保持。
**架构分层:**
```
UI 层 (Vue)
├── useI18n.ts t(key), currentLang, setLang(lang)
├── LangSwitch.vue "中文 / English" 按钮组
├── locales/zh.json 中文翻译UI + scene 文本 ~50行
├── locales/en.json 英文翻译UI + scene 文本 ~50行
├── 各组件 t('key') 按钮/提示/标签翻译
└── Subtitles.vue 按 currentLang 加载 subtitles[lang]
数据层 (Engine / Scene JSON)
├── Choice.textKey 可选 i18n key缺省 fallback 到 text
├── SceneNode.subtitles Record<lang, url> 字幕多语言 map
└── 引擎不感知 i18n 纯数据传递,翻译在 composable 层完成
```
**核心 composable 设计:**
```typescript
// src/composables/useI18n.ts
const messages = { zh, en }
const currentLang = ref(localStorage.getItem('lang') || 'zh')
export function useI18n() {
function t(key: string): string { /* key = "ui.start" → messages[lang].ui.start */ }
function setLang(lang: string) { /* localStorage + currentLang */ }
return { t, currentLang, setLang }
}
```
**Choice 翻译策略:**
composable 在 `choiceRequest` 事件中调用 `t(textKey)` 翻译选项文字后存入 store。
`textKey` 未设置时 fallback 到 `text`(向后兼容,不要求每个 Choice 都加 key
```typescript
engine.on('choiceRequest', (choiceList) => {
const translated = choiceList.map(c => ({
...c,
text: c.textKey ? i18n.t(c.textKey) : c.text,
}))
store.setChoices(translated)
})
```
**场景数据变更:**
```json
{
@@ -580,22 +632,71 @@ GainNode 的 ramp 目标值 = `Math.min(bgmVolume, bgmDuckLevel × bgmVolume)`
"videoUrl": "/videos/intro.mp4",
"subtitles": {
"zh": "/subtitles/intro_zh.vtt",
"en": "/subtitles/intro_en.vtt",
"ja": "/subtitles/intro_ja.vtt"
"en": "/subtitles/intro_en.vtt"
},
"choices": [...]
"choices": [
{
"text": "走向左边那扇发光的门",
"textKey": "scene.intro.choice.left_door",
"targetScene": "left_door"
}
]
}
```
**翻译文件结构:**
```json
// src/locales/zh.json
{
"ui": {
"start": "开始",
"resume": "继续上次进度",
"chapters": "章节选择",
"menu": "菜单",
"save": "保存",
"load": "读取",
"close": "关闭",
"skip": "跳过",
"fullscreen": "全屏",
"exitFullscreen": "退出全屏",
"gameEnd": "结束",
"choose": "做出你的选择",
"back": "返回",
"autoSave": "自动存档",
"empty": "空",
"loading": "加载中..."
},
"scene": {
"intro": {
"choice": {
"left_door": "走向左边那扇发光的门",
"right_door": "走向右边那扇普通的门",
"search": "搜索房间",
"stay": "留在原地,什么也不做"
}
}
}
}
```
**实现清单:**
- [ ] `engine/types.ts``SceneNode.subtitles` 改为 `Record<lang, url>``Choice.text` 支持多语言 key
- [ ] `engine/systems/I18nSystem.ts` — 语言切换、当前语言持久化到 localStorage
- [ ] `src/components/Subtitles.vue` — 监听语言切换,动态加载对应 VTT
- [ ] `src/components/ChoicePanel.vue` — 选项文字支持 i18n 映射
- [ ] `src/components/LanguageSwitch.vue` — 语言选择下拉菜单(顶部或菜单中)
- [ ] `public/scenes/demo.json` — 中英双语字幕示例
- [ ] 验证:语言切换后字幕/UI 即时更新、刷新保持语言偏好
- [x] `src/composables/useI18n.ts``t(key)`, `currentLang`, `setLang(lang)`, localStorage 持久化
- [x] `src/locales/zh.json` — 中文翻译UI ~20 项 + scene 选项文字 ~30 项)
- [x] `src/locales/en.json` — 英文翻译(同结构)
- [x] `engine/types.ts``Choice.textKey?: string``SceneNode.subtitles?: Record<string, string>`
- [x] `src/composables/useGameEngine.ts``choiceRequest``t(textKey)` 翻译后存入 store
- [x] `src/components/LangSwitch.vue` — "中文 / English" 切换按钮组,调用 `setLang`
- [x] `src/components/Subtitles.vue``effectiveUrl` computed 优先 `subtitles[lang]`fallback `subtitleUrl`
- [x] `src/App.vue` — 主菜单 LangSwitch + 顶部栏按钮 `t()` 翻译
- [x] `src/components/ChoicePanel.vue``t('ui.choose')` 替代硬编码提示文字
- [x] `src/components/SaveLoadMenu.vue` — 8 处文本用 `t()` 翻译
- [x] `src/components/ChapterSelect.vue` — 标题 + 返回按钮用 `t()` 翻译
- [x] `src/components/PlaybackBar.vue` — 跳过按钮用 `t('ui.skip')`
- [x] `public/subtitles/*_en.vtt` — 3 个英文版字幕文件intro/left_door/stay
- [x] `public/scenes/demo.json` — intro 场景配置 `subtitles` map + 4 个 choice 添加 `textKey`
- [x] 验证TypeScript + Vite build 通过
### P12 场景过渡特效(待实现)