chore: 添加文档、资源与测试输出

- assets/ 静态资源
- docs/superpowers/ 技能文档
- dogfood-output/ 测试验证输出
This commit is contained in:
张翔
2026-04-29 21:17:10 +08:00
parent d79b3f183a
commit a39b4388f3
23 changed files with 3449 additions and 0 deletions
@@ -0,0 +1,91 @@
{
"plan": {
"id": "uniapp-i18n",
"name": "UniApp 主应用国际化(i18n)实现计划",
"version": "1.0.0",
"date": "2026-04-29",
"designDoc": "docs/superpowers/specs/2026-04-28-uniapp-i18n-design.md",
"goal": "为 UniApp 主应用引入 vue-i18n 国际化体系,支持 9 种语言,将所有硬编码中文 UI 文本替换为 $t() / t() 调用",
"architecture": "集中式翻译文件方案,所有翻译键值对按语言分文件存放于 src/locales/,通过 vue-i18n 实例注入 Vue 应用。领域术语保留中文原词不纳入翻译体系",
"techStack": [
"vue-i18n@9",
"Vue 3 Composition API",
"UniApp",
"Vitest"
],
"executionMode": "sequential",
"phases": [
{
"id": "phase-1",
"name": "基础设施 — 安装 vue-i18n + 创建核心模块",
"file": "phase-1-infrastructure.json",
"status": "pending"
},
{
"id": "phase-2",
"name": "翻译文件 — 创建 8 种非中文翻译",
"file": "phase-2-translation-files.json",
"status": "pending"
},
{
"id": "phase-3",
"name": "页面改造 — 3 个页面 + pages.json",
"file": "phase-3-page-migration.json",
"status": "pending"
},
{
"id": "phase-4",
"name": "组件改造 — 10 个组件",
"file": "phase-4-component-migration.json",
"status": "pending"
},
{
"id": "phase-5",
"name": "测试与验证",
"file": "phase-5-testing-and-verification.json",
"status": "pending"
}
],
"fileStructure": {
"create": [
"src/locales/index.ts",
"src/locales/zh-CN.ts",
"src/locales/zh-TW.ts",
"src/locales/en.ts",
"src/locales/ja.ts",
"src/locales/ko.ts",
"src/locales/de.ts",
"src/locales/fr.ts",
"src/locales/es.ts",
"src/locales/pt.ts",
"src/locales/__tests__/completeness.test.ts",
"src/locales/__tests__/interpolation.test.ts"
],
"modify": [
"package.json",
"src/main.ts",
"src/pages/almanac-search/index.vue",
"src/pages/ziwei/index.vue",
"src/pages/fortune/index.vue",
"src/components/BottomNavigation/BottomNavigation.vue",
"src/components/EmptyState/index.vue",
"src/components/SearchConditionPanel/index.vue",
"src/components/SearchConditionItem/index.vue",
"src/components/SearchResultList/index.vue",
"src/components/SearchResultCard/index.vue",
"src/components/SortSwitcher/index.vue",
"src/components/ExportPanel/index.vue",
"src/components/SearchHistoryPanel/index.vue",
"src/components/TemplatePanel/index.vue"
],
"delete": []
},
"qualityGates": {
"translationKeyConsistency": "所有 9 种语言翻译键与 zh-CN 完全一致",
"interpolationConsistency": "所有语言的插值变量名一致",
"buildSuccess": "vite build 无错误",
"allTestsPass": "npx vitest run 全部通过",
"noHardcodedChinese": "页面和组件中无硬编码中文 UI 文本(领域术语除外)"
}
}
}
@@ -0,0 +1,115 @@
{
"phase": {
"id": "phase-1",
"name": "基础设施 — 安装 vue-i18n + 创建核心模块",
"description": "安装 vue-i18n 依赖,创建翻译核心模块(index.ts + zh-CN.ts),改造 main.ts 挂载 i18n",
"dependencies": [],
"tasks": [
{
"id": "1.1",
"name": "安装 vue-i18n 依赖",
"status": "pending",
"description": "通过 npm 安装 vue-i18n@9 并验证版本",
"files": {
"modify": ["package.json", "package-lock.json"]
},
"steps": [
{
"id": "1.1.1",
"action": "install",
"description": "安装 vue-i18n@9",
"runCommand": "cd everything-is-suitable-uniapp && npm install vue-i18n@9",
"expectedResult": "package.json 中出现 vue-i18n 依赖"
},
{
"id": "1.1.2",
"action": "verify",
"description": "验证安装版本",
"runCommand": "cd everything-is-suitable-uniapp && node -e \"const v = require('vue-i18n/package.json'); console.log(v.version)\"",
"expectedResult": "输出 9.x.x 版本号"
},
{
"id": "1.1.3",
"action": "commit",
"message": "chore: 安装 vue-i18n@9 依赖"
}
]
},
{
"id": "1.2",
"name": "创建 zh-CN 主翻译源文件",
"status": "pending",
"description": "创建 src/locales/zh-CN.ts,包含 common/nav/almanac/ziwei/fortune/search/template/export/history 共 9 个命名空间约 80 个翻译键",
"files": {
"create": ["src/locales/zh-CN.ts"]
},
"steps": [
{
"id": "1.2.1",
"action": "write_implementation",
"description": "创建 zh-CN.ts 翻译源文件",
"translationNamespaces": ["common", "nav", "almanac", "ziwei", "fortune", "search", "template", "export", "history"],
"sourceRef": "设计规格翻译键清单"
},
{
"id": "1.2.2",
"action": "commit",
"message": "feat(i18n): 创建简体中文主翻译源 zh-CN.ts"
}
]
},
{
"id": "1.3",
"name": "创建 locales/index.ts 核心模块",
"status": "pending",
"description": "创建 vue-i18n 实例、语言检测、切换、持久化逻辑",
"files": {
"create": ["src/locales/index.ts"]
},
"steps": [
{
"id": "1.3.1",
"action": "write_implementation",
"description": "创建 index.ts 核心模块,包含 createI18n/detectLocale/setLocale/getLocale/SUPPORTED_LOCALES/localeNames",
"exports": ["i18n", "setLocale", "getLocale", "SUPPORTED_LOCALES", "localeNames"],
"sourceRef": "设计规格核心模块设计"
},
{
"id": "1.3.2",
"action": "commit",
"message": "feat(i18n): 创建 vue-i18n 实例与语言检测/切换核心模块"
}
]
},
{
"id": "1.4",
"name": "改造 main.ts 挂载 i18n",
"status": "pending",
"description": "在 main.ts 中导入 i18n 并通过 app.use() 挂载",
"files": {
"modify": ["src/main.ts"]
},
"steps": [
{
"id": "1.4.1",
"action": "write_implementation",
"description": "修改 main.ts:添加 import { i18n } from './locales' 和 app.use(i18n)",
"sourceRef": "设计规格 main.ts 改造"
},
{
"id": "1.4.2",
"action": "verify",
"description": "验证构建无报错",
"runCommand": "cd everything-is-suitable-uniapp && npx vite build --mode development 2>&1 | head -20",
"expectedResult": "构建成功,无模块解析错误"
},
{
"id": "1.4.3",
"action": "commit",
"message": "feat(i18n): 在 main.ts 中挂载 vue-i18n 实例"
}
]
}
]
}
}
@@ -0,0 +1,98 @@
{
"phase": {
"id": "phase-2",
"name": "翻译文件 — 创建 8 种非中文翻译",
"description": "创建 zh-TW/en/ja/ko/de/fr/es/pt 共 8 种语言翻译文件,键结构必须与 zh-CN.ts 完全一致",
"dependencies": ["phase-1"],
"tasks": [
{
"id": "2.1",
"name": "创建繁體中文翻译 zh-TW.ts",
"status": "pending",
"description": "简转繁,保持相同键结构",
"files": { "create": ["src/locales/zh-TW.ts"] },
"steps": [
{ "id": "2.1.1", "action": "write_implementation", "description": "创建 zh-TW.ts,简转繁" },
{ "id": "2.1.2", "action": "commit", "message": "feat(i18n): 添加繁體中文翻譯 zh-TW.ts" }
]
},
{
"id": "2.2",
"name": "创建英文翻译 en.ts",
"status": "pending",
"description": "领域术语保留拼音(如 Zi Wei Dou Shu",
"files": { "create": ["src/locales/en.ts"] },
"steps": [
{ "id": "2.2.1", "action": "write_implementation", "description": "创建 en.ts,领域术语保留拼音" },
{ "id": "2.2.2", "action": "commit", "message": "feat(i18n): 添加英文翻譯 en.ts" }
]
},
{
"id": "2.3",
"name": "创建日文翻译 ja.ts",
"status": "pending",
"description": "命理术语保留汉字(如 紫微斗数、三方四正)",
"files": { "create": ["src/locales/ja.ts"] },
"steps": [
{ "id": "2.3.1", "action": "write_implementation", "description": "创建 ja.ts,命理术语保留汉字" },
{ "id": "2.3.2", "action": "commit", "message": "feat(i18n): 添加日文翻譯 ja.ts" }
]
},
{
"id": "2.4",
"name": "创建韩文翻译 ko.ts",
"status": "pending",
"description": "命理术语保留汉字(如 자미두수)",
"files": { "create": ["src/locales/ko.ts"] },
"steps": [
{ "id": "2.4.1", "action": "write_implementation", "description": "创建 ko.ts" },
{ "id": "2.4.2", "action": "commit", "message": "feat(i18n): 添加韩文翻譯 ko.ts" }
]
},
{
"id": "2.5",
"name": "创建德文翻译 de.ts",
"status": "pending",
"description": "领域术语保留拼音",
"files": { "create": ["src/locales/de.ts"] },
"steps": [
{ "id": "2.5.1", "action": "write_implementation", "description": "创建 de.ts" },
{ "id": "2.5.2", "action": "commit", "message": "feat(i18n): 添加德文翻譯 de.ts" }
]
},
{
"id": "2.6",
"name": "创建法文翻译 fr.ts",
"status": "pending",
"description": "领域术语保留拼音",
"files": { "create": ["src/locales/fr.ts"] },
"steps": [
{ "id": "2.6.1", "action": "write_implementation", "description": "创建 fr.ts" },
{ "id": "2.6.2", "action": "commit", "message": "feat(i18n): 添加法文翻譯 fr.ts" }
]
},
{
"id": "2.7",
"name": "创建西班牙文翻译 es.ts",
"status": "pending",
"description": "领域术语保留拼音",
"files": { "create": ["src/locales/es.ts"] },
"steps": [
{ "id": "2.7.1", "action": "write_implementation", "description": "创建 es.ts" },
{ "id": "2.7.2", "action": "commit", "message": "feat(i18n): 添加西班牙文翻譯 es.ts" }
]
},
{
"id": "2.8",
"name": "创建葡萄牙文翻译 pt.ts",
"status": "pending",
"description": "领域术语保留拼音",
"files": { "create": ["src/locales/pt.ts"] },
"steps": [
{ "id": "2.8.1", "action": "write_implementation", "description": "创建 pt.ts" },
{ "id": "2.8.2", "action": "commit", "message": "feat(i18n): 添加葡萄牙文翻譯 pt.ts" }
]
}
]
}
}
@@ -0,0 +1,122 @@
{
"phase": {
"id": "phase-3",
"name": "页面改造 — 3 个页面 + pages.json",
"description": "改造 almanac-search/ziwei/fortune 三个页面,替换硬编码中文为 $t()/t() 调用,添加 onShow 动态导航栏标题",
"dependencies": ["phase-1", "phase-2"],
"tasks": [
{
"id": "3.1",
"name": "改造黄历搜索页 almanac-search/index.vue",
"status": "pending",
"description": "替换模板和 script 中的硬编码中文,添加 onShow 导航栏标题",
"files": { "modify": ["src/pages/almanac-search/index.vue"] },
"steps": [
{
"id": "3.1.1",
"action": "write_implementation",
"description": "添加 i18n 导入:import { useI18n } from 'vue-i18n' + const { t } = useI18n()"
},
{
"id": "3.1.2",
"action": "write_implementation",
"description": "替换模板硬编码中文:黄历搜索→$t('almanac.pageTitle')、搜索中...→$t('almanac.searching')、重试→$t('common.retry')"
},
{
"id": "3.1.3",
"action": "write_implementation",
"description": "替换 script 硬编码中文:error.value = t('almanac.keywordHint')、t('almanac.noResult')、t('search.addConditionHint')、t('almanac.searchFailed')"
},
{
"id": "3.1.4",
"action": "write_implementation",
"description": "添加 onShowimport { onShow } from '@dcloudio/uni-app' + onShow(() => { uni.setNavigationBarTitle({ title: t('almanac.pageTitle') }) })"
},
{
"id": "3.1.5",
"action": "verify",
"description": "验证构建",
"runCommand": "cd everything-is-suitable-uniapp && npx vite build --mode development 2>&1 | tail -5",
"expectedResult": "构建成功"
},
{
"id": "3.1.6",
"action": "commit",
"message": "feat(i18n): 改造黄历搜索页,替换硬编码中文为 $t() 调用"
}
]
},
{
"id": "3.2",
"name": "改造紫微斗数页 ziwei/index.vue",
"status": "pending",
"description": "替换模板和 script 中的硬编码中文,添加 onShow 导航栏标题",
"files": { "modify": ["src/pages/ziwei/index.vue"] },
"replacements": [
{ "from": "出生信息", "to": "$t('ziwei.birthInfo')" },
{ "from": "出生日期", "to": "$t('ziwei.birthDate')" },
{ "from": "请选择", "to": "$t('ziwei.pleaseSelect')" },
{ "from": "出生时辰", "to": "$t('ziwei.birthHour')" },
{ "from": "性别", "to": "$t('ziwei.gender')" },
{ "from": "男", "to": "$t('common.male')" },
{ "from": "女", "to": "$t('common.female')" },
{ "from": "排盘中...", "to": "$t('ziwei.generating')" },
{ "from": "排盘", "to": "$t('ziwei.generate')" },
{ "from": "紫微斗数盘", "to": "$t('ziwei.chartTitle')" },
{ "from": "三方四正分析", "to": "$t('ziwei.sanFangAnalysis')" },
{ "from": "三方得分", "to": "$t('ziwei.sanFangScore')" },
{ "from": "四正得分", "to": "$t('ziwei.siZhengScore')" },
{ "from": "综合评价", "to": "$t('ziwei.overallEval')" },
{ "from": "盘面概述", "to": "$t('ziwei.summary')" },
{ "from": "分", "to": "$t('ziwei.scoreUnit')" }
],
"steps": [
{ "id": "3.2.1", "action": "write_implementation", "description": "添加 i18n 导入" },
{ "id": "3.2.2", "action": "write_implementation", "description": "替换模板硬编码中文(16 处)" },
{ "id": "3.2.3", "action": "write_implementation", "description": "添加 onShow 动态导航栏标题" },
{ "id": "3.2.4", "action": "verify", "description": "验证构建", "runCommand": "cd everything-is-suitable-uniapp && npx vite build --mode development 2>&1 | tail -5", "expectedResult": "构建成功" },
{ "id": "3.2.5", "action": "commit", "message": "feat(i18n): 改造紫微斗数页,替换硬编码中文为 $t() 调用" }
]
},
{
"id": "3.3",
"name": "改造运势分析页 fortune/index.vue",
"status": "pending",
"description": "替换模板和 script 中的硬编码中文,添加 onShow 导航栏标题",
"files": { "modify": ["src/pages/fortune/index.vue"] },
"replacements": [
{ "from": "日运", "to": "$t('fortune.daily')" },
{ "from": "月运", "to": "$t('fortune.monthly')" },
{ "from": "选择日期", "to": "$t('fortune.selectDate')" },
{ "from": "选择月份", "to": "$t('fortune.selectMonth')" },
{ "from": "综合运势:", "to": "$t('fortune.overallFortune')" },
{ "from": "事业:", "to": "$t('fortune.career')" },
{ "from": "财运:", "to": "$t('fortune.wealth')" },
{ "from": "感情:", "to": "$t('fortune.relationship')" },
{ "from": "健康:", "to": "$t('fortune.health')" },
{ "from": "幸运色:", "to": "$t('fortune.luckyColor')" },
{ "from": "幸运数字:", "to": "$t('fortune.luckyNumber')" },
{ "from": "幸运方位:", "to": "$t('fortune.luckyDirection')" },
{ "from": "请先在紫微斗数页面完成排盘", "to": "$t('fortune.noChart')" }
],
"steps": [
{ "id": "3.3.1", "action": "write_implementation", "description": "添加 i18n 导入" },
{ "id": "3.3.2", "action": "write_implementation", "description": "替换模板硬编码中文(13 处)" },
{ "id": "3.3.3", "action": "write_implementation", "description": "添加 onShow 动态导航栏标题" },
{ "id": "3.3.4", "action": "verify", "description": "验证构建", "runCommand": "cd everything-is-suitable-uniapp && npx vite build --mode development 2>&1 | tail -5", "expectedResult": "构建成功" },
{ "id": "3.3.5", "action": "commit", "message": "feat(i18n): 改造运势分析页,替换硬编码中文为 $t() 调用" }
]
},
{
"id": "3.4",
"name": "pages.json 保持中文初始值",
"status": "pending",
"description": "pages.json 中的 navigationBarTitleText 保持现有中文值作为首次渲染前的兜底,运行时由各页面 onShow 生命周期动态覆盖。无需改动此文件",
"files": {},
"steps": [
{ "id": "3.4.1", "action": "verify", "description": "确认 pages.json 无需修改,中文初始值作为兜底" }
]
}
]
}
}
@@ -0,0 +1,183 @@
{
"phase": {
"id": "phase-4",
"name": "组件改造 — 10 个组件",
"description": "改造 10 个组件,替换硬编码中文为 $t()/t() 调用。领域术语(如 suitableActivities/avoidActivities 数组)保留中文不翻译",
"dependencies": ["phase-1", "phase-2"],
"tasks": [
{
"id": "4.1",
"name": "改造 BottomNavigation",
"status": "pending",
"description": "将 tabs 数组中的 label 改为 computed,使用 t() 调用",
"files": { "modify": ["src/components/BottomNavigation/BottomNavigation.vue"] },
"replacements": [
{ "from": "黄历", "to": "t('nav.almanac')" },
{ "from": "紫微", "to": "t('nav.ziwei')" },
{ "from": "运势", "to": "t('nav.fortune')" }
],
"steps": [
{ "id": "4.1.1", "action": "write_implementation", "description": "添加 i18n 导入 + 将 tabs 改为 computed" },
{ "id": "4.1.2", "action": "commit", "message": "feat(i18n): 改造 BottomNavigation 组件" }
]
},
{
"id": "4.2",
"name": "改造 EmptyState",
"status": "pending",
"description": "移除 withDefaults 中的中文默认值,由调用方通过 $t() 传入",
"files": { "modify": ["src/components/EmptyState/index.vue"] },
"steps": [
{ "id": "4.2.1", "action": "write_implementation", "description": "移除 title/description/actionText 的中文默认值" },
{ "id": "4.2.2", "action": "commit", "message": "feat(i18n): 改造 EmptyState 组件,移除中文默认值" }
]
},
{
"id": "4.3",
"name": "改造 SearchConditionPanel",
"status": "pending",
"description": "替换搜索条件面板中的硬编码中文",
"files": { "modify": ["src/components/SearchConditionPanel/index.vue"] },
"replacements": [
{ "from": "搜索条件", "to": "$t('search.conditions')" },
{ "from": "添加条件", "to": "$t('search.addCondition')" },
{ "from": "请添加搜索条件", "to": "$t('search.addConditionEmpty')" },
{ "from": "搜索天数", "to": "$t('search.days')" },
{ "from": "天", "to": "$t('search.dayUnit')" },
{ "from": "搜索", "to": "$t('common.search')" }
],
"steps": [
{ "id": "4.3.1", "action": "write_implementation", "description": "添加 i18n 导入 + 替换 6 处硬编码中文" },
{ "id": "4.3.2", "action": "commit", "message": "feat(i18n): 改造 SearchConditionPanel 组件" }
]
},
{
"id": "4.4",
"name": "改造 SearchConditionItem",
"status": "pending",
"description": "替换硬编码中文,将 typeOptions/operatorOptions 改为 computed。领域术语保留中文",
"files": { "modify": ["src/components/SearchConditionItem/index.vue"] },
"replacements": [
{ "from": "事项", "to": "$t('search.items')" },
{ "from": "逻辑运算符", "to": "$t('search.operator')" },
{ "from": "排除条件", "to": "$t('search.excludeCondition')" },
{ "from": "宜", "to": "t('search.typeSuitable')" },
{ "from": "忌", "to": "t('search.typeUnsuitable')" },
{ "from": "与(AND", "to": "t('search.operatorAnd')" },
{ "from": "或(OR", "to": "t('search.operatorOr')" }
],
"note": "suitableActivities 和 avoidActivities 数组保持中文硬编码(领域术语策略)",
"steps": [
{ "id": "4.4.1", "action": "write_implementation", "description": "添加 i18n 导入 + 替换模板中文 + 将 typeOptions/operatorOptions 改为 computed" },
{ "id": "4.4.2", "action": "commit", "message": "feat(i18n): 改造 SearchConditionItem 组件(领域术语保留中文)" }
]
},
{
"id": "4.5",
"name": "改造 SearchResultList",
"status": "pending",
"description": "替换搜索结果列表中的硬编码中文",
"files": { "modify": ["src/components/SearchResultList/index.vue"] },
"replacements": [
{ "from": "搜索结果", "to": "$t('search.searchResult')" },
{ "from": "共{results.length}条结果", "to": "$t('search.resultCount', { count: results.length })" },
{ "from": "加载中...", "to": "$t('common.loading')" }
],
"steps": [
{ "id": "4.5.1", "action": "write_implementation", "description": "添加 i18n 导入 + 替换 3 处硬编码中文" },
{ "id": "4.5.2", "action": "commit", "message": "feat(i18n): 改造 SearchResultList 组件" }
]
},
{
"id": "4.6",
"name": "改造 SearchResultCard",
"status": "pending",
"description": "替换搜索结果卡片中的硬编码中文",
"files": { "modify": ["src/components/SearchResultCard/index.vue"] },
"replacements": [
{ "from": "宜", "to": "$t('search.typeSuitable')" },
{ "from": "忌", "to": "$t('search.typeUnsuitable')" },
{ "from": "匹配度:", "to": "$t('search.matchCount', { count: result.matchCount })" }
],
"steps": [
{ "id": "4.6.1", "action": "write_implementation", "description": "添加 i18n 导入 + 替换 3 处硬编码中文" },
{ "id": "4.6.2", "action": "commit", "message": "feat(i18n): 改造 SearchResultCard 组件" }
]
},
{
"id": "4.7",
"name": "改造 SortSwitcher",
"status": "pending",
"description": "替换排序切换器中的硬编码中文",
"files": { "modify": ["src/components/SortSwitcher/index.vue"] },
"replacements": [
{ "from": "排序", "to": "$t('search.sort')" },
{ "from": "日期", "to": "$t('search.sortByDate')" },
{ "from": "匹配度", "to": "$t('search.sortByMatch')" }
],
"steps": [
{ "id": "4.7.1", "action": "write_implementation", "description": "添加 i18n 导入 + 替换 3 处硬编码中文" },
{ "id": "4.7.2", "action": "commit", "message": "feat(i18n): 改造 SortSwitcher 组件" }
]
},
{
"id": "4.8",
"name": "改造 ExportPanel",
"status": "pending",
"description": "替换导出面板中的硬编码中文,将 contentOptions 改为 computed",
"files": { "modify": ["src/components/ExportPanel/index.vue"] },
"replacements": [
{ "from": "导出搜索结果", "to": "$t('export.title')" },
{ "from": "选择导出格式", "to": "$t('export.selectFormat')" },
{ "from": "选择导出内容", "to": "$t('export.selectContent')" },
{ "from": "取消", "to": "$t('common.cancel')" },
{ "from": "导出", "to": "$t('common.confirm')" }
],
"steps": [
{ "id": "4.8.1", "action": "write_implementation", "description": "添加 i18n 导入 + 替换模板中文 + 将 contentOptions 改为 computed" },
{ "id": "4.8.2", "action": "commit", "message": "feat(i18n): 改造 ExportPanel 组件" }
]
},
{
"id": "4.9",
"name": "改造 SearchHistoryPanel",
"status": "pending",
"description": "替换搜索历史面板中的硬编码中文,将 formatTime 中的中文改为 t() 调用",
"files": { "modify": ["src/components/SearchHistoryPanel/index.vue"] },
"replacements": [
{ "from": "搜索历史", "to": "$t('history.title')" },
{ "from": "清除历史", "to": "$t('history.clear')" },
{ "from": "确认清除", "to": "$t('history.clearConfirm')" },
{ "from": "确定要清除所有搜索历史吗?", "to": "$t('history.clearConfirmMsg')" },
{ "from": "暂无搜索历史", "to": "$t('history.empty')" },
{ "from": "执行搜索后,搜索条件将显示在这里", "to": "$t('history.emptyDesc')" },
{ "from": "刚刚", "to": "t('history.justNow')" },
{ "from": "小时前", "to": "t('history.hoursAgo', { count })" },
{ "from": "天前", "to": "t('history.daysAgo', { count })" }
],
"steps": [
{ "id": "4.9.1", "action": "write_implementation", "description": "添加 i18n 导入 + 替换模板中文 + 改造 formatTime 函数" },
{ "id": "4.9.2", "action": "commit", "message": "feat(i18n): 改造 SearchHistoryPanel 组件" }
]
},
{
"id": "4.10",
"name": "改造 TemplatePanel",
"status": "pending",
"description": "替换搜索模板面板中的硬编码中文",
"files": { "modify": ["src/components/TemplatePanel/index.vue"] },
"replacements": [
{ "from": "搜索模板", "to": "$t('template.title')" },
{ "from": "搜索模板(占位符)", "to": "$t('template.searchPlaceholder')" },
{ "from": "暂无模板", "to": "$t('template.empty')" },
{ "from": "没有找到符合条件的模板", "to": "$t('template.emptyDesc')" },
{ "from": "全部", "to": "$t('template.all')" }
],
"steps": [
{ "id": "4.10.1", "action": "write_implementation", "description": "添加 i18n 导入 + 替换 5 处硬编码中文" },
{ "id": "4.10.2", "action": "commit", "message": "feat(i18n): 改造 TemplatePanel 组件" }
]
}
]
}
}
@@ -0,0 +1,145 @@
{
"phase": {
"id": "phase-5",
"name": "测试与验证",
"description": "创建翻译完整性测试、插值格式测试、组件 i18n 集成测试,运行全量构建和测试验证",
"dependencies": ["phase-3", "phase-4"],
"tasks": [
{
"id": "5.1",
"name": "创建翻译完整性测试",
"status": "pending",
"description": "校验所有 9 种语言的翻译键与 zh-CN 完全一致(嵌套 key 逐层比对)",
"files": {
"create": ["src/locales/__tests__/completeness.test.ts"]
},
"steps": [
{
"id": "5.1.1",
"action": "write_test",
"description": "编写翻译完整性测试:导入所有 9 种语言翻译,递归比对键结构,报告缺失/多余键"
},
{
"id": "5.1.2",
"action": "run_test_verify_pass",
"description": "运行测试验证通过",
"runCommand": "cd everything-is-suitable-uniapp && npx vitest run src/locales/__tests__/completeness.test.ts",
"expectedResult": "PASS — 所有语言键结构一致"
},
{
"id": "5.1.3",
"action": "commit",
"message": "test(i18n): 添加翻译完整性校验测试"
}
]
},
{
"id": "5.2",
"name": "创建插值格式测试",
"status": "pending",
"description": "校验所有语言的插值变量名一致(如 {count} 在所有语言中均存在)",
"files": {
"create": ["src/locales/__tests__/interpolation.test.ts"]
},
"steps": [
{
"id": "5.2.1",
"action": "write_test",
"description": "编写插值格式测试:提取所有 {variable} 占位符,比对各语言一致性"
},
{
"id": "5.2.2",
"action": "run_test_verify_pass",
"description": "运行测试验证通过",
"runCommand": "cd everything-is-suitable-uniapp && npx vitest run src/locales/__tests__/interpolation.test.ts",
"expectedResult": "PASS — 所有语言插值变量一致"
},
{
"id": "5.2.3",
"action": "commit",
"message": "test(i18n): 添加插值格式校验测试"
}
]
},
{
"id": "5.3",
"name": "创建组件 i18n 集成测试",
"status": "pending",
"description": "在 vitest 配置中添加 vue-i18n 插件,为 BottomNavigation 编写 i18n 测试",
"files": {
"modify": ["src/components/BottomNavigation/__tests__/BottomNavigation.test.ts"]
},
"steps": [
{
"id": "5.3.1",
"action": "write_implementation",
"description": "在 vitest 配置中添加 vue-i18n 插件,确保 @vue/test-utils 的 mount 能正确注入 i18n"
},
{
"id": "5.3.2",
"action": "write_test",
"description": "为 BottomNavigation 编写 i18n 测试:验证渲染文本来自翻译键"
},
{
"id": "5.3.3",
"action": "run_test_verify_pass",
"description": "运行测试验证通过",
"runCommand": "cd everything-is-suitable-uniapp && npx vitest run src/components/BottomNavigation/__tests__/",
"expectedResult": "PASS"
},
{
"id": "5.3.4",
"action": "commit",
"message": "test(i18n): 添加 BottomNavigation i18n 集成测试"
}
]
},
{
"id": "5.4",
"name": "全量构建验证",
"status": "pending",
"description": "运行 vite build 确认构建成功",
"files": {},
"steps": [
{
"id": "5.4.1",
"action": "verify",
"description": "运行 vite build",
"runCommand": "cd everything-is-suitable-uniapp && npx vite build 2>&1 | tail -10",
"expectedResult": "构建成功,无错误"
}
]
},
{
"id": "5.5",
"name": "运行全量测试",
"status": "pending",
"description": "运行所有测试确认无回归",
"files": {},
"steps": [
{
"id": "5.5.1",
"action": "verify",
"description": "运行所有测试",
"runCommand": "cd everything-is-suitable-uniapp && npx vitest run",
"expectedResult": "所有测试通过"
}
]
},
{
"id": "5.6",
"name": "最终 Commit",
"status": "pending",
"description": "提交所有剩余变更",
"files": {},
"steps": [
{
"id": "5.6.1",
"action": "commit",
"message": "feat(i18n): 完成 UniApp 国际化改造 — 9 种语言支持"
}
]
}
]
}
}
@@ -0,0 +1,431 @@
# UniApp 主应用国际化(i18n)实现计划
> **面向 AI 代理的工作者:** 必需子技能:使用 superpowers:subagent-driven-development(推荐)或 superpowers:executing-plans 逐任务实现此计划。步骤使用复选框(`- [ ]`)语法来跟踪进度。
**目标:** 为 UniApp 主应用引入 vue-i18n 国际化体系,支持 9 种语言,将所有硬编码中文 UI 文本替换为 `$t()` / `t()` 调用。
**架构:** 集中式翻译文件方案,所有翻译键值对按语言分文件存放于 `src/locales/`,通过 vue-i18n 实例注入 Vue 应用。领域术语保留中文原词不纳入翻译体系。
**技术栈:** vue-i18n@9, Vue 3 Composition API, UniApp, Vitest
**设计规格:** `docs/superpowers/specs/2026-04-28-uniapp-i18n-design.md`
---
## 整体架构流程
```mermaid
graph TD
A[用户启动 App] --> B{localStorage 有 preferred-locale?}
B -->|是| C[读取存储的 locale]
B -->|否| D{读取系统语言}
D -->|匹配支持语言| E[使用系统语言]
D -->|不匹配| F[回退 zh-CN]
C --> G[应用 vue-i18n locale]
E --> G
F --> G
G --> H[渲染页面 - $t 替换硬编码文本]
H --> I{用户切换系统语言?}
I -->|是| J[重启 App 重新检测]
I -->|否| H
```
## 组件改造流程
```mermaid
graph LR
subgraph 改造前
A1[模板硬编码中文] --> B1[Script 硬编码中文]
B1 --> C1[pages.json 硬编码中文]
end
subgraph 改造后
A2["模板: {{ $t('key') }}"] --> B2["Script: const { t } = useI18n(); t('key')"]
B2 --> C2["onShow: uni.setNavigationBarTitle"]
end
A1 -.->|替换| A2
B1 -.->|替换| B2
C1 -.->|运行时覆盖| C2
```
## Phase 依赖关系
```mermaid
graph TD
P1["Phase 1: 基础设施<br/>安装 vue-i18n + 创建核心模块"] --> P2["Phase 2: 翻译文件<br/>创建 9 种语言翻译"]
P2 --> P3["Phase 3: 页面改造<br/>3 个页面 + pages.json"]
P2 --> P4["Phase 4: 组件改造<br/>10 个组件"]
P3 --> P5["Phase 5: 测试与验证<br/>完整性 + 组件 + E2E"]
P4 --> P5
```
---
## 文件结构
### 新建文件
| 文件路径 | 职责 |
|---------|------|
| `src/locales/index.ts` | vue-i18n 实例创建、语言检测/切换/持久化 |
| `src/locales/zh-CN.ts` | 简体中文翻译(主翻译源) |
| `src/locales/zh-TW.ts` | 繁體中文翻译 |
| `src/locales/en.ts` | English 翻译 |
| `src/locales/ja.ts` | 日本語翻译 |
| `src/locales/ko.ts` | 한국어 翻译 |
| `src/locales/de.ts` | Deutsch 翻译 |
| `src/locales/fr.ts` | Français 翻译 |
| `src/locales/es.ts` | Español 翻译 |
| `src/locales/pt.ts` | Português 翻译 |
| `src/locales/__tests__/completeness.test.ts` | 翻译完整性校验测试 |
| `src/locales/__tests__/interpolation.test.ts` | 插值格式校验测试 |
### 修改文件
| 文件路径 | 改动说明 |
|---------|---------|
| `package.json` | 添加 vue-i18n 依赖 |
| `src/main.ts` | 挂载 i18n 实例 |
| `src/pages/almanac-search/index.vue` | 替换硬编码中文为 `$t()` / `t()` |
| `src/pages/ziwei/index.vue` | 替换硬编码中文为 `$t()` / `t()` |
| `src/pages/fortune/index.vue` | 替换硬编码中文为 `$t()` / `t()` |
| `src/components/BottomNavigation/BottomNavigation.vue` | 替换硬编码中文 |
| `src/components/EmptyState/index.vue` | 移除中文默认值 |
| `src/components/SearchConditionPanel/index.vue` | 替换硬编码中文 |
| `src/components/SearchConditionItem/index.vue` | 替换硬编码中文(领域术语保留) |
| `src/components/SearchResultList/index.vue` | 替换硬编码中文 |
| `src/components/SearchResultCard/index.vue` | 替换硬编码中文 |
| `src/components/SortSwitcher/index.vue` | 替换硬编码中文 |
| `src/components/ExportPanel/index.vue` | 替换硬编码中文 |
| `src/components/SearchHistoryPanel/index.vue` | 替换硬编码中文 |
| `src/components/TemplatePanel/index.vue` | 替换硬编码中文 |
---
## Phase 1:基础设施 — 安装 vue-i18n + 创建核心模块
**文件:**
- 修改:`package.json`
- 创建:`src/locales/index.ts`
- 创建:`src/locales/zh-CN.ts`
- 修改:`src/main.ts`
### 任务 1.1:安装 vue-i18n 依赖
- [ ] **步骤 1:安装 vue-i18n**
运行:`cd everything-is-suitable-uniapp && npm install vue-i18n@9`
预期:`package.json` 中出现 `"vue-i18n": "^9.x.x"` 依赖
- [ ] **步骤 2:验证安装**
运行:`cd everything-is-suitable-uniapp && node -e "const v = require('vue-i18n/package.json'); console.log(v.version)"`
预期:输出 `9.x.x` 版本号
- [ ] **步骤 3Commit**
`cd everything-is-suitable-uniapp && git add package.json package-lock.json && git commit -m "chore: 安装 vue-i18n@9 依赖"`
### 任务 1.2:创建 zh-CN 主翻译源文件
- [ ] **步骤 1:创建 `src/locales/zh-CN.ts`** — 包含 common/nav/almanac/ziwei/fortune/search/template/export/history 共 9 个命名空间,约 80 个翻译键
- [ ] **步骤 2Commit**
`cd everything-is-suitable-uniapp && git add src/locales/zh-CN.ts && git commit -m "feat(i18n): 创建简体中文主翻译源 zh-CN.ts"`
### 任务 1.3:创建 locales/index.ts 核心模块
- [ ] **步骤 1:创建 `src/locales/index.ts`** — 包含 createI18n 实例、detectLocale 语言检测、setLocale 切换持久化、getLocale 获取当前语言、SUPPORTED_LOCALES 常量、localeNames 映射
- [ ] **步骤 2Commit**
`cd everything-is-suitable-uniapp && git add src/locales/index.ts && git commit -m "feat(i18n): 创建 vue-i18n 实例与语言检测/切换核心模块"`
### 任务 1.4:改造 main.ts 挂载 i18n
- [ ] **步骤 1:修改 `src/main.ts`** — 添加 `import { i18n } from './locales'``app.use(i18n)`
- [ ] **步骤 2:验证构建无报错**
运行:`cd everything-is-suitable-uniapp && npx vite build --mode development 2>&1 | head -20`
预期:构建成功,无模块解析错误
- [ ] **步骤 3Commit**
`cd everything-is-suitable-uniapp && git add src/main.ts && git commit -m "feat(i18n): 在 main.ts 中挂载 vue-i18n 实例"`
---
## Phase 2:翻译文件 — 创建 8 种非中文翻译
**依赖:** Phase 1
每个任务结构相同:创建翻译文件 → Commit。翻译键结构必须与 zh-CN.ts 完全一致。
### 任务 2.1:创建繁體中文翻译 zh-TW.ts
- [ ] **步骤 1:创建 `src/locales/zh-TW.ts`** — 简转繁,保持相同键结构
- [ ] **步骤 2Commit**
### 任务 2.2:创建英文翻译 en.ts
- [ ] **步骤 1:创建 `src/locales/en.ts`** — 领域术语保留拼音(如 Zi Wei Dou Shu
- [ ] **步骤 2Commit**
### 任务 2.3:创建日文翻译 ja.ts
- [ ] **步骤 1:创建 `src/locales/ja.ts`** — 命理术语保留汉字(如 紫微斗数、三方四正)
- [ ] **步骤 2Commit**
### 任务 2.4:创建韩文翻译 ko.ts
- [ ] **步骤 1:创建 `src/locales/ko.ts`** — 命理术语保留汉字(如 자미두수)
- [ ] **步骤 2Commit**
### 任务 2.5:创建德文翻译 de.ts
- [ ] **步骤 1:创建 `src/locales/de.ts`** — 领域术语保留拼音
- [ ] **步骤 2Commit**
### 任务 2.6:创建法文翻译 fr.ts
- [ ] **步骤 1:创建 `src/locales/fr.ts`** — 领域术语保留拼音
- [ ] **步骤 2Commit**
### 任务 2.7:创建西班牙文翻译 es.ts
- [ ] **步骤 1:创建 `src/locales/es.ts`** — 领域术语保留拼音
- [ ] **步骤 2Commit**
### 任务 2.8:创建葡萄牙文翻译 pt.ts
- [ ] **步骤 1:创建 `src/locales/pt.ts`** — 领域术语保留拼音
- [ ] **步骤 2Commit**
---
## Phase 3:页面改造 — 3 个页面 + pages.json
**依赖:** Phase 1, Phase 2
```mermaid
graph TD
P3A["任务 3.1: almanac-search"] --> P3D["任务 3.4: pages.json 确认"]
P3B["任务 3.2: ziwei"] --> P3D
P3C["任务 3.3: fortune"] --> P3D
```
### 任务 3.1:改造黄历搜索页 almanac-search/index.vue
**文件:** 修改 `src/pages/almanac-search/index.vue`
- [ ] **步骤 1:在 `<script setup>` 中添加 i18n 导入**`import { useI18n } from 'vue-i18n'` + `const { t } = useI18n()`
- [ ] **步骤 2:替换模板硬编码中文**`黄历搜索``{{ $t('almanac.pageTitle') }}``搜索中...``{{ $t('almanac.searching') }}``重试``{{ $t('common.retry') }}`
- [ ] **步骤 3:替换 script 硬编码中文**`error.value = t('almanac.keywordHint')``error.value = t('almanac.noResult')``error.value = t('search.addConditionHint')``error.value = t('almanac.searchFailed')`
- [ ] **步骤 4:添加 onShow 动态设置导航栏标题**`import { onShow } from '@dcloudio/uni-app'` + `onShow(() => { uni.setNavigationBarTitle({ title: t('almanac.pageTitle') }) })`
- [ ] **步骤 5:验证构建**
- [ ] **步骤 6Commit**
### 任务 3.2:改造紫微斗数页 ziwei/index.vue
**文件:** 修改 `src/pages/ziwei/index.vue`
- [ ] **步骤 1:添加 i18n 导入**
- [ ] **步骤 2:替换模板硬编码中文**`出生信息``{{ $t('ziwei.birthInfo') }}``出生日期``{{ $t('ziwei.birthDate') }}``请选择``{{ $t('ziwei.pleaseSelect') }}``出生时辰``{{ $t('ziwei.birthHour') }}``性别``{{ $t('ziwei.gender') }}``男``{{ $t('common.male') }}``女``{{ $t('common.female') }}``排盘中...``{{ $t('ziwei.generating') }}``排盘``{{ $t('ziwei.generate') }}``紫微斗数盘``{{ $t('ziwei.chartTitle') }}``三方四正分析``{{ $t('ziwei.sanFangAnalysis') }}``三方得分``{{ $t('ziwei.sanFangScore') }}``四正得分``{{ $t('ziwei.siZhengScore') }}``综合评价``{{ $t('ziwei.overallEval') }}``盘面概述``{{ $t('ziwei.summary') }}``分``{{ $t('ziwei.scoreUnit') }}`
- [ ] **步骤 3:添加 onShow 动态设置导航栏标题**
- [ ] **步骤 4:验证构建**
- [ ] **步骤 5Commit**
### 任务 3.3:改造运势分析页 fortune/index.vue
**文件:** 修改 `src/pages/fortune/index.vue`
- [ ] **步骤 1:添加 i18n 导入**
- [ ] **步骤 2:替换模板硬编码中文**`日运``{{ $t('fortune.daily') }}``月运``{{ $t('fortune.monthly') }}``选择日期``{{ $t('fortune.selectDate') }}``选择月份``{{ $t('fortune.selectMonth') }}``综合运势:``{{ $t('fortune.overallFortune') }}``分``{{ $t('ziwei.scoreUnit') }}``事业:``{{ $t('fortune.career') }}``财运:``{{ $t('fortune.wealth') }}``感情:``{{ $t('fortune.relationship') }}``健康:``{{ $t('fortune.health') }}``幸运色:``{{ $t('fortune.luckyColor') }}``幸运数字:``{{ $t('fortune.luckyNumber') }}``幸运方位:``{{ $t('fortune.luckyDirection') }}``请先在紫微斗数页面完成排盘``{{ $t('fortune.noChart') }}`
- [ ] **步骤 3:添加 onShow 动态设置导航栏标题**
- [ ] **步骤 4:验证构建**
- [ ] **步骤 5Commit**
### 任务 3.4pages.json 保持中文初始值
**文件:** `src/pages.json`(无需修改)
`pages.json` 中的 `navigationBarTitleText` 保持现有中文值作为首次渲染前的兜底,运行时由各页面 `onShow` 生命周期动态覆盖。
---
## Phase 4:组件改造 — 10 个组件
**依赖:** Phase 1, Phase 2
```mermaid
graph TD
C1["BottomNavigation"] --> CV["验证构建"]
C2["EmptyState"] --> CV
C3["SearchConditionPanel"] --> CV
C4["SearchConditionItem"] --> CV
C5["SearchResultList"] --> CV
C6["SearchResultCard"] --> CV
C7["SortSwitcher"] --> CV
C8["ExportPanel"] --> CV
C9["SearchHistoryPanel"] --> CV
C10["TemplatePanel"] --> CV
```
### 任务 4.1:改造 BottomNavigation
**文件:** 修改 `src/components/BottomNavigation/BottomNavigation.vue`
- [ ] **步骤 1:添加 i18n 导入**`import { useI18n } from 'vue-i18n'` + `const { t } = useI18n()`
- [ ] **步骤 2:将 tabs 数组中的 label 改为 computed**`label: t('nav.almanac')``label: t('nav.ziwei')``label: t('nav.fortune')`
- [ ] **步骤 3Commit**
### 任务 4.2:改造 EmptyState
**文件:** 修改 `src/components/EmptyState/index.vue`
- [ ] **步骤 1:移除 withDefaults 中的中文默认值** — 移除 `title``description``actionText` 的默认值,由调用方通过 `$t()` 传入
- [ ] **步骤 2Commit**
### 任务 4.3:改造 SearchConditionPanel
**文件:** 修改 `src/components/SearchConditionPanel/index.vue`
- [ ] **步骤 1:添加 i18n 导入并替换硬编码中文**`搜索条件``{{ $t('search.conditions') }}``添加条件``{{ $t('search.addCondition') }}``请添加搜索条件``{{ $t('search.addConditionEmpty') }}``搜索天数``{{ $t('search.days') }}``天``{{ $t('search.dayUnit') }}``搜索``{{ $t('common.search') }}`
- [ ] **步骤 2Commit**
### 任务 4.4:改造 SearchConditionItem
**文件:** 修改 `src/components/SearchConditionItem/index.vue`
- [ ] **步骤 1:添加 i18n 导入并替换硬编码中文**`事项``{{ $t('search.items') }}``逻辑运算符``{{ $t('search.operator') }}``排除条件``{{ $t('search.excludeCondition') }}`
- [ ] **步骤 2:将 typeOptions 和 operatorOptions 改为 computed**`{ label: t('search.typeSuitable'), value: 'suitable' }``{ label: t('search.typeUnsuitable'), value: 'unsuitable' }``{ label: t('search.operatorAnd'), value: 'and' }``{ label: t('search.operatorOr'), value: 'or' }`
- [ ] **步骤 3Commit** — 注意:`suitableActivities``avoidActivities` 数组保持中文硬编码(领域术语策略)
### 任务 4.5:改造 SearchResultList
**文件:** 修改 `src/components/SearchResultList/index.vue`
- [ ] **步骤 1:添加 i18n 导入并替换硬编码中文**`搜索结果``{{ $t('search.searchResult') }}``共{results.length}条结果``{{ $t('search.resultCount', { count: results.length }) }}``加载中...``{{ $t('common.loading') }}`
- [ ] **步骤 2Commit**
### 任务 4.6:改造 SearchResultCard
**文件:** 修改 `src/components/SearchResultCard/index.vue`
- [ ] **步骤 1:添加 i18n 导入并替换硬编码中文**`宜``{{ $t('search.typeSuitable') }}``忌``{{ $t('search.typeUnsuitable') }}``匹配度:``{{ $t('search.matchCount', { count: result.matchCount }) }}`
- [ ] **步骤 2Commit**
### 任务 4.7:改造 SortSwitcher
**文件:** 修改 `src/components/SortSwitcher/index.vue`
- [ ] **步骤 1:添加 i18n 导入并替换硬编码中文**`排序``{{ $t('search.sort') }}``日期``{{ $t('search.sortByDate') }}``匹配度``{{ $t('search.sortByMatch') }}`
- [ ] **步骤 2Commit**
### 任务 4.8:改造 ExportPanel
**文件:** 修改 `src/components/ExportPanel/index.vue`
- [ ] **步骤 1:添加 i18n 导入并替换硬编码中文**`导出搜索结果``{{ $t('export.title') }}``选择导出格式``{{ $t('export.selectFormat') }}``选择导出内容``{{ $t('export.selectContent') }}``取消``{{ $t('common.cancel') }}``导出``{{ $t('common.confirm') }}`
- [ ] **步骤 2:将 contentOptions 改为 computed**`{ label: t('export.date'), value: 'date' }``{ label: t('export.lunarDate'), value: 'lunarDate' }``{ label: t('export.weekday'), value: 'weekday' }``{ label: t('export.matchedItems'), value: 'matchedItems' }``{ label: t('export.matchCount'), value: 'matchCount' }`
- [ ] **步骤 3Commit**
### 任务 4.9:改造 SearchHistoryPanel
**文件:** 修改 `src/components/SearchHistoryPanel/index.vue`
- [ ] **步骤 1:添加 i18n 导入并替换硬编码中文**`搜索历史``{{ $t('history.title') }}``清除历史``{{ $t('history.clear') }}``确认清除``{{ $t('history.clearConfirm') }}``确定要清除所有搜索历史吗?``{{ $t('history.clearConfirmMsg') }}``暂无搜索历史``{{ $t('history.empty') }}``执行搜索后,搜索条件将显示在这里``{{ $t('history.emptyDesc') }}`
- [ ] **步骤 2:将 formatTime 函数中的中文改为 t() 调用**`刚刚``t('history.justNow')``小时前``t('history.hoursAgo', { count })``天前``t('history.daysAgo', { count })`
- [ ] **步骤 3Commit**
### 任务 4.10:改造 TemplatePanel
**文件:** 修改 `src/components/TemplatePanel/index.vue`
- [ ] **步骤 1:添加 i18n 导入并替换硬编码中文**`搜索模板``{{ $t('template.title') }}`、搜索框占位 → `{{ $t('template.searchPlaceholder') }}``暂无模板``{{ $t('template.empty') }}``没有找到符合条件的模板``{{ $t('template.emptyDesc') }}``全部``{{ $t('template.all') }}`
- [ ] **步骤 2Commit**
---
## Phase 5:测试与验证
**依赖:** Phase 3, Phase 4
```mermaid
graph TD
T1["任务 5.1: 翻译完整性测试"] --> T4["任务 5.4: 全量构建验证"]
T2["任务 5.2: 插值格式测试"] --> T4
T3["任务 5.3: 组件 i18n 集成测试"] --> T4
T4 --> T5["任务 5.5: 运行全量测试"]
T5 --> T6["任务 5.6: 最终 Commit"]
```
### 任务 5.1:创建翻译完整性测试
**文件:** 创建 `src/locales/__tests__/completeness.test.ts`
- [ ] **步骤 1:编写测试** — 校验所有 9 种语言的翻译键与 zh-CN 完全一致(嵌套 key 逐层比对)
- [ ] **步骤 2:运行测试验证通过**
运行:`cd everything-is-suitable-uniapp && npx vitest run src/locales/__tests__/completeness.test.ts`
预期:PASS
- [ ] **步骤 3Commit**
### 任务 5.2:创建插值格式测试
**文件:** 创建 `src/locales/__tests__/interpolation.test.ts`
- [ ] **步骤 1:编写测试** — 校验所有语言的插值变量名一致(如 `{count}` 在所有语言中均存在)
- [ ] **步骤 2:运行测试验证通过**
- [ ] **步骤 3Commit**
### 任务 5.3:创建组件 i18n 集成测试
**文件:** 修改现有组件测试文件
- [ ] **步骤 1:在 vitest 配置中添加 vue-i18n 插件** — 确保 `@vue/test-utils``mount` 能正确注入 i18n
- [ ] **步骤 2:为 BottomNavigation 编写 i18n 测试** — 验证渲染文本来自翻译键
- [ ] **步骤 3:运行测试验证通过**
- [ ] **步骤 4Commit**
### 任务 5.4:全量构建验证
- [ ] **步骤 1:运行 vite build**
运行:`cd everything-is-suitable-uniapp && npx vite build 2>&1 | tail -10`
预期:构建成功,无错误
- [ ] **步骤 2:运行 H5 开发服务器验证页面渲染**
运行:`cd everything-is-suitable-uniapp && npm run dev:h5`
预期:页面正常渲染,导航栏标题为中文(默认 locale),底部导航显示中文
### 任务 5.5:运行全量测试
- [ ] **步骤 1:运行所有测试**
运行:`cd everything-is-suitable-uniapp && npx vitest run`
预期:所有测试通过
### 任务 5.6:最终 Commit
- [ ] **步骤 1Commit**
`cd everything-is-suitable-uniapp && git add -A && git commit -m "feat(i18n): 完成 UniApp 国际化改造 — 9 种语言支持"`
---
## 自检清单
1. **规格覆盖度:** 设计规格中的 9 种语言 ✅、集中式翻译文件 ✅、vue-i18n ✅、领域术语保留 ✅、pages.json 运行时覆盖 ✅、3 个页面改造 ✅、10 个组件改造 ✅、翻译完整性测试 ✅
2. **占位符扫描:** 无 TODO/待定/后续实现 ✅
3. **类型一致性:** 所有翻译键名在 zh-CN.ts 定义,其他语言文件保持一致 ✅