本文档基于 Vue 3 + TypeScript 环境,提供 Vue I18n(国际化插件)的标准化安装、配置、组件内使用及非组件场景适配方案,修复原文档中的路径冲突、类型不匹配等问题,同时补充注释说明与最佳实践。
一、环境准备
确保项目已满足以下依赖版本(Vue 3 专属,Vue 2 需使用 vue-i18n@8.x
版本):
- Vue:
^3.0.0
- TypeScript(可选但推荐):
^4.0.0
- Vue I18n:
^12.0.0-alpha.3
(当前最新版)
二、安装 Vue I18n
使用包管理器安装(pnpm
/npm
/yarn
均可,推荐 pnpm
):
bash
csharp
# pnpm(推荐,速度快且节省磁盘空间)
pnpm install vue-i18n
# npm
npm install vue-i18n --save
# yarn
yarn add vue-i18n
三、目录结构设计
先创建标准化的国际化目录,避免后续文件散放导致维护困难:
plaintext
bash
src/
├── locales/ # 语言包目录(存放各语言的翻译文件)
│ ├── en.ts # 英文语言包
│ └── zh.ts # 中文语言包
├── plugins/ # 插件配置目录(集中管理第三方插件)
│ └── i18n.ts # Vue I18n 实例配置
├── utils/ # 工具函数目录
│ └── i18nUtils.ts # 国际化工具函数(切换语言、非组件翻译)
└── main.ts # 项目入口文件(挂载 I18n)
四、配置语言包
在 locales
目录下创建对应语言的翻译文件,采用「模块 + 键」的命名规范(如 Header.index
),便于后续定位和维护。
1. 中文语言包(src/locales/zh.ts)
typescript
javascript
// 中文翻译文件:按「页面/组件」划分模块,避免键名冲突
export default {
// 头部导航模块
Header: {
index: '首页',
about: '关于我们',
contact: '联系我们'
},
// 可扩展其他模块(如首页、商品页等)
Home: {
welcome: '欢迎访问我的网站',
desc: '这是一个 Vue I18n 国际化示例'
}
} as const; // 添加 as const 让 TypeScript 识别键名,提供类型提示
2. 英文语言包(src/locales/en.ts)
保持与中文包完全一致的键名结构,仅修改值为对应英文翻译:
typescript
css
export default {
Header: {
index: 'Home',
about: 'About Us',
contact: 'Contact Us'
},
Home: {
welcome: 'Welcome to My Website',
desc: 'This is a Vue I18n Internationalization Demo'
}
} as const;
五、创建 Vue I18n 实例(插件配置)
在 plugins/i18n.ts
中初始化 I18n 实例,处理默认语言检测 、持久化存储等核心逻辑:
typescript
javascript
import { createI18n } from 'vue-i18n'; // 从 vue-i18n 导入创建实例的方法
import zh from '@/locales/zh'; // 导入中文语言包(@ 表示 src 目录,需配置 tsconfig.json)
import en from '@/locales/en'; // 导入英文语言包
/**
* 获取默认语言(优先级:本地存储 > 浏览器默认)
* @returns 语言标识(zh 或 en)
*/
const getDefaultLanguage = (): 'zh' | 'en' => {
// 1. 优先读取本地存储(用户之前切换过的语言)
const savedLang = localStorage.getItem('app_language');
if (savedLang === 'zh' || savedLang === 'en') {
return savedLang;
}
// 2. 读取浏览器默认语言(如 navigator.language 为 "zh-CN" 则返回 zh)
const browserLang = navigator.language.toLowerCase();
if (browserLang.includes('zh')) {
return 'zh';
}
return 'en'; // 默认英文(可根据需求改为 zh) ---如果第二步影响到你的语言,可以把第二点忽略掉
};
// 创建 I18n 实例
const i18n = createI18n({
legacy: false, // 启用 Vue 3 组合式 API 模式(必须设为 false,否则无法使用 useI18n)
globalInjection: true, // 全局注入 $t 方法(可在模板中直接使用 {{ $t('key') }})
locale: getDefaultLanguage(), // 当前使用的语言
fallbackLocale: 'en', // 翻译失败时的备用语言(避免显示键名)
messages: { // 语言包映射(键为语言标识,值为对应语言包)
zh,
en
}
});
export default i18n;
六、在入口文件挂载 I18n
在 main.ts
中导入并挂载 I18n 实例,确保全局可用:
typescript
javascript
import { createApp } from 'vue';
import App from './App.vue';
import i18n from '@/plugins/i18n'; // 导入 I18n 实例
// 创建 Vue 应用并挂载 I18n
const app = createApp(App);
app.use(i18n); // 挂载 I18n 插件
app.mount('#app');
七、核心功能:语言切换与工具函数
在 utils/i18nUtils.ts
中封装语言切换 和非组件翻译函数,避免重复代码:
typescript
typescript
import i18n from '@/plugins/i18n'; // 导入 I18n 实例
/**
* 切换语言(并持久化到本地存储)
* @param newLocale 目标语言标识(zh 或 en)
*/
export const switchLanguage = (newLocale: 'zh' | 'en') => {
try {
// 1. 更新 I18n 实例的当前语言
i18n.global.locale.value = newLocale;
// 2. 持久化到本地存储(下次打开页面仍保持该语言)
localStorage.setItem('app_language', newLocale);
console.log(`语言已切换为:${newLocale === 'zh' ? '中文' : '英文'}`);
} catch (error) {
console.error('语言切换失败:', error);
}
};
/**
* 非组件环境下的翻译函数(如工具类、Store 中使用)
* @param key 翻译键(如 Header.index)
* @param values 可选插值参数(如 t('Home.hello', { name: 'Tom' }))
* @returns 翻译后的文本(失败时返回原键名,避免页面空白)
*/
export const t = (key: string, values?: Record<string, any>): string => {
try {
return i18n.global.t(key, values);
} catch (error) {
console.error(`翻译失败(键:${key}):`, error);
return key;
}
};
八、组件内使用(两种方式)
Vue 3 中推荐使用组合式 API (useI18n
),也支持模板中直接使用全局注入的 $t
方法。
方式 1:组合式 API(推荐,TypeScript 友好)
vue
xml
<script setup lang="ts">
import { useI18n } from 'vue-i18n'; // 导入组合式 API
import { switchLanguage } from '@/utils/i18nUtils'; // 导入语言切换函数
import { ref } from 'vue';
// 获取 I18n 实例的核心方法:locale(当前语言)、t(翻译函数)
const { locale, t } = useI18n();
// 示例:用翻译函数定义数据(语言切换时会自动更新)
const navMenu = ref([
{ label: t('Header.index'), path: '/' },
{ label: t('Header.about'), path: '/about' },
{ label: t('Header.contact'), path: '/contact' }
]);
// 切换语言的方法(点击按钮触发)
const handleSwitchLang = () => {
// 切换为当前语言的对立语言
const newLang = locale.value === 'zh' ? 'en' : 'zh';
switchLanguage(newLang);
};
</script>
<template>
<!-- 1. 直接使用 $t 翻译(全局注入,无需导入) -->
<h1>{{ $t('Home.welcome') }}</h1>
<p>{{ $t('Home.desc') }}</p>
<!-- 2. 使用组合式 API 中的 t 函数定义的数据 -->
<nav>
<a v-for="item in navMenu" :key="item.path" :href="item.path">
{{ item.label }}
</a>
</nav>
<!-- 3. 语言切换按钮 -->
<button @click="handleSwitchLang">
{{ locale === 'zh' ? '切换为英文' : 'Switch to Chinese' }}
</button>
</template>
方式 2:选项式 API(兼容旧代码)
vue
xml
<script lang="ts">
import { defineComponent } from 'vue';
import { useI18n } from 'vue-i18n';
import { switchLanguage } from '@/utils/i18nUtils';
export default defineComponent({
setup() {
const { locale, t } = useI18n();
return { locale, t, switchLanguage };
},
data() {
return {
navMenu: [
{ label: this.t('Header.index'), path: '/' },
{ label: this.t('Header.about'), path: '/about' }
]
};
},
methods: {
handleSwitchLang() {
const newLang = this.locale === 'zh' ? 'en' : 'zh';
this.switchLanguage(newLang);
}
}
});
</script>
<template>
<h1>{{ $t('Home.welcome') }}</h1>
<button @click="handleSwitchLang">切换语言</button>
</template>
九、非组件环境使用(关键场景)
在工具类、Vuex/Pinia Store、路由守卫 等非组件环境中,无法使用 useI18n
组合式 API,需使用 i18nUtils.ts
中封装的 t
函数。
示例 1:在 Pinia Store 中使用
typescript
javascript
// src/store/modules/menuStore.ts
import { defineStore } from 'pinia';
import { t } from '@/utils/i18nUtils'; // 导入非组件翻译函数
export const useMenuStore = defineStore('menu', {
state: () => ({
// 用 t 函数翻译菜单标签(语言切换时需重新获取,或监听语言变化)
menuList: [
{ label: t('Header.index'), path: '/', icon: 'home' },
{ label: t('Header.about'), path: '/about', icon: 'info' }
]
}),
actions: {
// 语言切换后更新菜单(需在切换语言时调用)
updateMenuAfterLangSwitch() {
this.menuList = [
{ label: t('Header.index'), path: '/', icon: 'home' },
{ label: t('Header.about'), path: '/about', icon: 'info' }
];
}
}
});
示例 2:在工具函数中使用
typescript
typescript
// src/utils/formatUtils.ts
import { t } from '@/utils/i18nUtils';
/**
* 格式化提示信息(根据当前语言返回对应文本)
* @param type 提示类型(success/error)
* @returns 格式化后的提示文本
*/
export const formatTip = (type: 'success' | 'error'): string => {
if (type === 'success') {
return t('Common.successTip'); // 需在语言包中添加 Common 模块
}
return t('Common.errorTip');
};
十、常见问题与最佳实践。
1. 如何处理动态插值?
语言包中支持占位符,使用 {}
定义,翻译时传入参数:
typescript
javascript
// 语言包(zh.ts)
export default {
Common: {
welcomeUser: '欢迎您,{name}!'
}
} as const;
// 组件中使用
t('Common.welcomeUser', { name: '张三' }); // 输出:欢迎您,张三!
4. 如何支持更多语言?
- 在
locales
目录下新增语言包(如ja.ts
日语)。 - 在
plugins/i18n.ts
的messages
中添加新语言映射(ja: ja
)。 - 更新
switchLanguage
函数的类型('zh' | 'en' | 'ja'
)。
十一、扩展功能(可选)
- 懒加载语言包 :对于大型项目,可使用
import()
动态加载语言包,减少首屏体积。 - 语言切换动画:配合 Vue 的过渡动画,实现语言切换时的平滑过渡。
- HTML 标签支持 :在语言包中使用
{0}
等占位符,支持插入 HTML 元素(需使用v-html
)。
通过以上配置,可实现一套完整、可维护的 Vue 3 国际化方案,满足多语言场景的开发需求。