本项目当前采用 “多端同构、统一语言包” 的国际化实现方式,H5 与小程序共享同一套 vue-i18n 初始化与语言包加载逻辑,业务层统一使用 $t / useI18n,无需端侧分支。以下内容以现有工程为准,整理清晰的落地方案与使用规范。
一、现状与目标
- 多端一致:H5 与小程序均加载
vue-i18n,无需条件编译分支。 - 统一入口:
src/i18n/index.ts负责语言包聚合与 i18n 实例创建。 - 统一使用方式:模板使用
$t,脚本使用useI18n()获取t。 - 语言状态持久化:Pinia
localestore 持久化保存,App 启动时初始化语言。
二、目录结构与语言包组织
src/
├── i18n/
│ ├── index.ts # i18n 初始化入口
│ ├── zh/ # 中文模块
│ │ ├── demo.ts
│ │ ├── login.ts
│ │ └── ...
│ └── en/ # 英文模块
│ ├── demo.ts
│ ├── login.ts
│ └── ...
├── store/
│ └── locale.ts # 语言状态持久化
├── components/
│ └── ChangeLocale/ # 切换语言组件
└── main.js # app.use(i18n)
语言包按模块文件拆分,模块名与文件名保持一致,例如:
src/i18n/zh/demo.ts→ 使用demo.xxx作为 keysrc/i18n/en/login.ts→ 使用login.xxx作为 key
三、i18n 初始化机制
项目使用 import.meta.glob 聚合语言包,构建时自动注入:
// src/i18n/index.ts
const messages = {
zh: {},
en: {},
}
const zhModules = import.meta.glob('./zh/*.ts', { eager: true })
const enModules = import.meta.glob('./en/*.ts', { eager: true })
最终通过 createI18n 创建实例并全局注入 $t:
const i18n = createI18n({
legacy: false,
locale: getInitialLocale(),
fallbackLocale: 'zh',
messages: loadModuleMessages(),
globalInjection: true,
})
四、全局挂载与语言初始化
- 全局挂载:
src/main.js中app.use(i18n)对所有端生效。 - 语言初始化:
src/App.vue在onLaunch阶段读取本地locale或系统语言设置,并同步到i18n与localestore。 - 持久化:
src/store/locale.ts使用 Pinia 持久化,存储 key 为locale。
五、业务层使用方式
5.1 模板中使用
<text>{{ $t('demo.iconPreviewTitle') }}</text>
5.2 脚本中使用
import { useI18n } from 'vue-i18n'
const { t, locale } = useI18n()
const title = t('login.login')
5.3 切换语言
项目已有切换组件 src/components/ChangeLocale/ChangeLocale.vue:
locale.value = 'en'
useLocaleStore().setLocale('en')
六、编写规范(与项目规则一致)
- 禁止硬编码中文:所有展示文本必须走
t('xx.yy')。 - 模块化 key 结构:
module.section.item风格,模块名与语言包文件名一致。 - 插值规范:小程序端使用列表插值
{0}/{1},参数传数组,避免具名插值。 - 新增模块:新增页面同时补充
src/i18n/zh/*.ts与src/i18n/en/*.ts。
七、可选优化方向(当前未落地)
如需进一步缩减小程序包体,可考虑:
- 条件编译隔离:将
vue-i18n初始化与语言包加载包裹到#ifdef H5。 - 小程序降级策略:小程序端返回默认中文或只加载
zh。 - 端侧依赖裁剪:配置
mp-weixinexternals 排除vue-i18n。
当前代码未实现上述优化,如需启用请单独评审与改造。
参考: 官方国际化:uniapp.dcloud.net.cn/tutorial/i1…