国际化 (i18n) 使用指南
概述
本项目使用 Vue i18n 进行国际化处理,配合 i18n ally 插件提供完整的开发体验。支持中文(zh_CN)和英文(en_US)两种语言。
目录结构
src/locales/
├── lang/ # 语言文件目录
│ ├── zh_CN/ # 中文语言包
│ │ ├── index.ts # 主入口文件
│ │ ├── components.ts # 组件相关翻译
│ │ ├── layout.ts # 布局相关翻译
│ │ └── pages/ # 页面相关翻译
│ │ ├── index.ts # 页面翻译入口
│ │ ├── login.ts # 登录页翻译
│ │ ├── dashboard-base.ts
│ │ └── ...
│ └── en_US/ # 英文语言包
│ ├── index.ts
│ ├── components.ts
│ ├── layout.ts
│ └── pages/
├── index.ts # i18n 配置入口
├── useLocale.ts # 语言切换 Hook
└── README.md # 使用说明
基本使用
在 Vue 组件中使用
<template>
<div>
<!-- 使用全局 $t 方法 -->
<h1>{{ $t('pages.login.loginTitle') }}</h1>
<!-- 使用 Composition API -->
<p>{{ t('layout.header.user') }}</p>
<!-- 条件渲染 -->
<div v-if="$te('layout.header.help')">
{{ $t('layout.header.help') }}
</div>
</div>
</template>
<script setup lang="ts">
import { useI18n } from 'vue-i18n';
// 解构出需要的方法
const { t, te, locale } = useI18n();
// 或者直接导入
import { t } from '@/locales';
</script>
在 TypeScript 中使用
import { t } from '@/locales';
// 基本翻译
const message = t('layout.header.logout');
// 在函数中使用
function showMessage() {
console.log(t('components.commonTable.contractName'));
}
// 动态翻译
const getStatusText = (status: string) => {
return t(`components.commonTable.contractStatusEnum.${status}`);
};
语言切换
使用 useLocale Hook
<script setup lang="ts">
import { useLocale } from '@/locales/useLocale';
const { changeLocale, locale } = useLocale();
// 切换到英文
const switchToEnglish = () => {
changeLocale('en_US');
};
// 切换到中文
const switchToChinese = () => {
changeLocale('zh_CN');
};
</script>
使用语言选择器
<template>
<t-select v-model="currentLang" @change="handleLangChange">
<t-option
v-for="lang in langList"
:key="lang.value"
:value="lang.value"
>
{{ lang.content }}
</t-option>
</t-select>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { langList } from '@/locales';
import { useLocale } from '@/locales/useLocale';
const { changeLocale, locale } = useLocale();
const currentLang = ref(locale.value);
const handleLangChange = (value: string) => {
changeLocale(value);
currentLang.value = value;
};
</script>
添加新的翻译
1. 在语言文件中添加翻译
// src/locales/lang/zh_CN/components.ts
export default {
button: {
confirm: '确认',
cancel: '取消',
save: '保存',
delete: '删除',
},
message: {
success: '操作成功',
error: '操作失败',
warning: '警告信息',
},
form: {
required: '此字段为必填项',
invalid: '格式不正确',
},
};
2. 对应的英文翻译
// src/locales/lang/en_US/components.ts
export default {
button: {
confirm: 'Confirm',
cancel: 'Cancel',
save: 'Save',
delete: 'Delete',
},
message: {
success: 'Operation successful',
error: 'Operation failed',
warning: 'Warning message',
},
form: {
required: 'This field is required',
invalid: 'Invalid format',
},
};
3. 在组件中使用
<template>
<div>
<t-button @click="handleSave">
{{ $t('components.button.save') }}
</t-button>
<t-button @click="handleDelete" theme="danger">
{{ $t('components.button.delete') }}
</t-button>
</div>
</template>
<script setup lang="ts">
import { MessagePlugin } from 'tdesign-vue-next';
import { t } from '@/locales';
const handleSave = () => {
// 使用翻译的消息
MessagePlugin.success(t('components.message.success'));
};
const handleDelete = () => {
MessagePlugin.error(t('components.message.error'));
};
</script>
i18n ally 插件配置
VSCode 设置
项目已配置 .vscode/settings.json
:
{
"i18n-ally.localesPaths": ["src/locales/lang"],
"i18n-ally.enabledParsers": ["ts"],
"i18n-ally.enabledFrameworks": ["vue"],
"i18n-ally.displayLanguage": "zh-CN",
"i18n-ally.sourceLanguage": "zh-CN",
"i18n-ally.keystyle": "nested"
}
插件功能
1. 自动提取翻译
- 选中需要翻译的文本
- 右键选择 "i18n ally: Extract text"
- 插件会自动生成 key 并添加到语言文件
2. 内联显示
- 在代码中显示当前语言的翻译内容
- 支持快速切换预览语言
- 鼠标悬停显示翻译详情
3. 翻译管理
- 侧边栏查看所有翻译 key
- 快速跳转到翻译定义
- 检测缺失的翻译
- 批量编辑翻译
4. 实时验证
- 检测未使用的翻译 key
- 检测缺失的翻译
- 语法错误提示
最佳实践
1. Key 命名规范
// ✅ 推荐:使用嵌套结构,语义化命名
{
pages: {
login: {
title: '登录',
form: {
username: '用户名',
password: '密码',
submit: '登录'
}
}
}
}
// ❌ 不推荐:扁平结构,难以维护
{
'login-title': '登录',
'login-username': '用户名',
'login-password': '密码'
}
2. 文件组织
- 按功能模块分组:页面、组件、布局分别管理
- 保持结构一致:中英文文件结构完全对应
- 及时同步更新:添加新翻译时同时更新所有语言
3. 翻译内容规范
// ✅ 推荐:简洁明了,符合语言习惯
{
button: {
save: '保存',
cancel: '取消'
},
message: {
saveSuccess: '保存成功'
}
}
// ❌ 不推荐:过于冗长或不符合习惯
{
button: {
save: '点击此按钮进行保存操作',
cancel: '点击取消按钮'
}
}
4. 动态翻译
<script setup lang="ts">
import { computed } from 'vue';
import { t } from '@/locales';
// 动态状态翻译
const statusText = computed(() => {
return t(`components.commonTable.contractStatusEnum.${status.value}`);
});
// 带参数的翻译(如果需要)
const welcomeMessage = computed(() => {
return t('pages.user.welcome', { name: userName.value });
});
</script>
类型安全
全局类型定义
项目已配置 src/types/vue-i18n.d.ts
:
import type { VueI18n } from 'vue-i18n';
declare module '@vue/runtime-core' {
interface ComponentCustomProperties {
/**
* 翻译函数
* @param key 翻译键
* @param values 插值参数
*/
$t: (key: string, values?: Record<string, any>) => string;
/**
* 检查翻译键是否存在
* @param key 翻译键
*/
$te: (key: string) => boolean;
/**
* 复数翻译函数
* @param key 翻译键
* @param choice 数量
* @param values 插值参数
*/
$tc: (key: string, choice?: number, values?: Record<string, any>) => string;
/**
* 日期格式化函数
* @param value 日期值
* @param key 格式化键
* @param locale 语言环境
*/
$d: (value: number | Date, key?: string, locale?: string) => string;
/**
* 数字格式化函数
* @param value 数字值
* @param key 格式化键
* @param locale 语言环境
*/
$n: (value: number, key?: string, locale?: string) => string;
/**
* 当前语言环境
*/
$i18n: VueI18n;
}
}
export {};
使用 TypeScript
// 类型安全的翻译函数
const getTranslation = (key: string): string => {
return t(key);
};
// 枚举翻译
enum ContractStatus {
FAIL = 'fail',
AUDIT = 'audit',
EXECUTING = 'executing'
}
const getStatusText = (status: ContractStatus): string => {
return t(`components.commonTable.contractStatusEnum.${status}`);
};
常见问题
1. 翻译不生效
问题:添加了翻译但页面不显示
解决方案:
- 检查 key 是否正确
- 确认语言文件已正确导入
- 检查浏览器控制台错误
- 重启开发服务器
2. i18n ally 无法识别
问题:插件不显示翻译内容
解决方案:
- 确保安装了 i18n ally 插件
- 检查
.vscode/settings.json
配置 - 重启 VSCode
- 确认文件路径正确
3. i18n ally 加载错误:Cannot find module 'tdesign-vue-next/es/locale/'
问题:i18n ally 插件报错无法找到 TDesign 语言包模块
解决方案:
方法一:移除 TDesign 语言包导入(推荐)
项目已经移除了语言文件中的 TDesign 语言包导入,改为动态加载:
// 不再在语言文件中直接导入
// import componentsLocale from 'tdesign-vue-next/es/locale/zh_CN';
// 而是在 useLocale.ts 中动态导入
const getComponentsLocale = computed(() => {
try {
if (locale.value === 'zh_CN') {
return import('tdesign-vue-next/es/locale/zh_CN');
} else if (locale.value === 'en_US') {
return import('tdesign-vue-next/es/locale/en_US');
}
return {};
} catch (error) {
console.warn('Failed to load TDesign locale:', error);
return {};
}
});
方法二:配置 i18n ally 忽略特定文件
在 .vscode/settings.json
中添加:
{
"i18n-ally.ignoreFiles": [
"**/node_modules/**",
"**/dist/**",
"**/.git/**",
"**/index.ts"
]
}
4. 文件只读模式错误
问题:提示文件处于只读模式,无法编辑
解决方案:
方法一:检查文件权限
# 检查文件权限
ls -la src/locales/
# 修改文件权限(如果需要)
chmod 644 src/locales/lang/*/index.ts
方法二:关闭可能占用文件的进程
- 关闭所有 VSCode 窗口
- 停止开发服务器 (
Ctrl+C
) - 重启 VSCode
- 重新启动开发服务器
方法三:检查 Git 状态
# 检查文件是否被 Git 锁定
git status
# 如果有冲突,解决冲突
git add .
git commit -m "fix: resolve i18n configuration conflicts"
方法四:重启 TypeScript 服务
- 在 VSCode 中按
Ctrl+Shift+P
(Mac:Cmd+Shift+P
) - 输入
TypeScript: Restart TS Server
- 选择并执行
5. 类型错误:$t
、$te
等方法不存在
问题:TypeScript 报错 Property '$te' does not exist on type...
解决方案:
方法一:确保类型定义文件存在
- 确保
src/types/vue-i18n.d.ts
文件存在且内容正确 - 重启 TypeScript 服务(VSCode:
Ctrl+Shift+P
→TypeScript: Restart TS Server
) - 重启开发服务器
方法二:使用 Composition API 替代
<template>
<div>
<!-- 使用 Composition API 方法 -->
<div v-if="te('layout.header.help')">
{{ t('layout.header.help') }}
</div>
</div>
</template>
<script setup lang="ts">
import { useI18n } from 'vue-i18n';
// 解构出所有需要的方法
const { t, te, d, n, locale } = useI18n();
</script>
方法三:临时类型断言(不推荐)
<template>
<div>
<!-- 临时解决方案,不推荐长期使用 -->
<div v-if="($te as any)('layout.header.help')">
{{ $t('layout.header.help') }}
</div>
</div>
</template>
方法四:检查 tsconfig.json 配置
确保 tsconfig.json
包含了类型文件:
{
"include": [
"src/**/*.d.ts",
"src/types/**/*.d.ts"
]
}
6. 动态导入问题
问题:语言文件动态导入失败
解决方案:
- 检查文件路径是否正确
- 确认
import.meta.glob
语法正确 - 检查文件导出格式
7. 全局注入不生效
问题:globalInjection: true
设置了但仍然无法使用 $t
解决方案:
- 确保在
main.ts
中正确安装了 i18n 插件:
import { createApp } from 'vue';
import i18n from './locales';
const app = createApp(App);
app.use(i18n);
- 确保 i18n 配置中设置了
globalInjection: true
:
export const i18n = createI18n({
legacy: false,
globalInjection: true, // 确保这个设置为 true
// ... 其他配置
});
开发工作流
1. 添加新页面翻译
# 1. 创建页面翻译文件
touch src/locales/lang/zh_CN/pages/new-page.ts
touch src/locales/lang/en_US/pages/new-page.ts
# 2. 在 pages/index.ts 中导入
# 3. 在组件中使用
2. 批量翻译管理
使用 i18n ally 插件:
- 打开侧边栏的 i18n ally 面板
- 查看所有翻译 key
- 批量编辑或导出翻译
- 检查缺失的翻译
3. 翻译验证
# 检查未使用的翻译 key
# 使用 i18n ally 插件的 "Unused keys" 功能
# 检查缺失的翻译
# 使用 i18n ally 插件的 "Missing keys" 功能
性能优化
1. 懒加载语言包
// 如果需要懒加载(当前项目使用 eager: true)
const langModules = import.meta.glob('./lang/*/index.ts', {
eager: false // 改为懒加载
});
2. 缓存翻译结果
// 在组件中缓存翻译结果
const cachedTranslations = computed(() => ({
title: t('pages.dashboard.title'),
subtitle: t('pages.dashboard.subtitle'),
}));
扩展功能
1. 添加新语言
// 1. 创建新语言目录
mkdir src/locales/lang/ja_JP
// 2. 复制现有语言文件结构
// 3. 翻译内容
// 4. 在 langList 中添加新语言选项
2. 日期和数字格式化
<template>
<!-- 如果需要格式化功能,可以添加配置 -->
<p>{{ $d(new Date(), 'short') }}</p>
<p>{{ $n(1234.56, 'currency') }}</p>
</template>
故障排除清单
当遇到国际化相关问题时,请按以下顺序检查:
类型错误排查
- 检查文件是否存在:
src/types/vue-i18n.d.ts
- 重启 TypeScript 服务:VSCode 中按
Ctrl+Shift+P
,输入TypeScript: Restart TS Server
- 重启开发服务器:停止并重新启动
npm run dev
- 检查 tsconfig.json:确保包含了
src/types/**/*.d.ts
- 检查 i18n 配置:确保
globalInjection: true
- 检查插件安装:确保在
main.ts
中使用了app.use(i18n)
- 使用 Composition API:作为备选方案使用
useI18n()
解构方法
i18n ally 插件问题排查
- 检查插件是否安装:确保安装了 i18n ally 插件
- 检查 VSCode 设置:确认
.vscode/settings.json
配置正确 - 重启 VSCode:完全关闭并重新打开 VSCode
- 清除插件缓存:禁用并重新启用 i18n ally 插件
- 检查语言文件:确保语言文件没有导入错误
- 查看插件日志:打开 VSCode 输出面板,选择 i18n ally 查看错误日志
文件只读问题排查
- 检查文件权限:
ls -la src/locales/
- 关闭占用进程:停止开发服务器,关闭 VSCode
- 检查 Git 状态:
git status
查看是否有冲突 - 重启系统服务:重启 TypeScript 服务和开发服务器
- 修改权限:
chmod 644 src/locales/lang/*/index.ts
快速修复命令
# 1. 停止开发服务器
# Ctrl+C
# 2. 检查文件权限
ls -la src/locales/lang/
# 3. 修复权限(如果需要)
chmod -R 644 src/locales/lang/
# 4. 检查 Git 状态
git status
# 5. 重启开发服务器
npm run dev
应急解决方案
如果以上方法都无效,可以使用以下应急方案:
- 使用 Composition API:
<script setup lang="ts">
import { useI18n } from 'vue-i18n';
const { t, te } = useI18n();
</script>
-
临时禁用 i18n ally:在 VSCode 扩展中禁用 i18n ally 插件
-
使用类型断言:
<template>
<div v-if="($te as any)('key')">{{ ($t as any)('key') }}</div>
</template>
这个文档涵盖了项目中国际化的完整使用方法,包括基本用法、最佳实践、问题排查等,可以作为团队开发的参考指南。