国际化(i18n)方案实现
国际化(i18n)方案实现
国际化(i18n)是现代前端应用开发中不可或缺的功能,Vite.js作为新一代构建工具,提供了多种实现国际化的方式。从简单的JSON文件到复杂的动态加载,开发者可以根据项目需求选择最适合的方案。
基础JSON文件方案
最简单的国际化实现方式是使用JSON文件存储不同语言的翻译内容。在Vite项目中创建一个locales
文件夹,存放各种语言的翻译文件:
// locales/en.json
{
"welcome": "Welcome",
"about": "About Us",
"contact": "Contact"
}
// locales/zh.json
{
"welcome": "欢迎",
"about": "关于我们",
"contact": "联系我们"
}
然后在组件中通过导入JSON文件并使用:
import en from './locales/en.json'
import zh from './locales/zh.json'
const messages = {
en,
zh
}
const currentLocale = ref('en')
function t(key) {
return messages[currentLocale.value][key]
}
使用vue-i18n插件
对于Vue项目,vue-i18n
是最流行的国际化解决方案。首先安装依赖:
npm install vue-i18n@9
然后在Vite项目中配置:
// src/i18n.js
import { createI18n } from 'vue-i18n'
import en from './locales/en.json'
import zh from './locales/zh.json'
const messages = {
en,
zh
}
export default createI18n({
legacy: false,
locale: 'en',
fallbackLocale: 'en',
messages
})
在main.js中引入:
import { createApp } from 'vue'
import App from './App.vue'
import i18n from './i18n'
const app = createApp(App)
app.use(i18n)
app.mount('#app')
在组件中使用:
<template>
<p>{{ $t('welcome') }}</p>
</template>
动态加载语言包
对于大型应用,可以按需加载语言包以减少初始加载体积:
// src/i18n.js
export async function loadLocaleMessages(i18n, locale) {
const messages = await import(`./locales/${locale}.json`)
i18n.global.setLocaleMessage(locale, messages.default)
return nextTick()
}
切换语言时调用:
async function changeLocale(locale) {
if (!i18n.global.availableLocales.includes(locale)) {
await loadLocaleMessages(i18n, locale)
}
i18n.global.locale.value = locale
}
处理复数形式和插值
国际化不仅仅是简单的文本替换,还需要处理复数形式和变量插值:
// locales/en.json
{
"cart": {
"items": "{count} item | {count} items"
}
}
在组件中使用:
<template>
<p>{{ $tc('cart.items', itemCount, { count: itemCount }) }}</p>
</template>
日期和数字格式化
国际化还包括日期和数字的本地化显示:
const i18n = createI18n({
// ...其他配置
datetimeFormats: {
en: {
short: {
year: 'numeric',
month: 'short',
day: 'numeric'
}
},
zh: {
short: {
year: 'numeric',
month: 'short',
day: 'numeric'
}
}
},
numberFormats: {
en: {
currency: {
style: 'currency',
currency: 'USD'
}
},
zh: {
currency: {
style: 'currency',
currency: 'CNY'
}
}
}
})
使用方式:
<template>
<p>{{ $d(new Date(), 'short') }}</p>
<p>{{ $n(1000, 'currency') }}</p>
</template>
路由国际化
对于多语言网站,路由也需要国际化:
// router.js
const routes = [
{
path: '/:locale',
component: Layout,
children: [
{
path: 'about',
component: About,
name: 'about'
}
]
},
{
path: '/',
redirect: '/en'
}
]
在导航守卫中处理语言切换:
router.beforeEach((to) => {
const locale = to.params.locale || 'en'
if (!supportedLocales.includes(locale)) {
return '/en' + to.fullPath
}
i18n.global.locale.value = locale
})
SEO优化
多语言网站的SEO需要考虑hreflang标签:
<template>
<head>
<link
v-for="locale in availableLocales"
:key="locale"
rel="alternate"
:hreflang="locale"
:href="`https://example.com/${locale}${$route.path}`"
>
</head>
</template>
测试国际化应用
编写测试时需要考虑多语言场景:
import { mount } from '@vue/test-utils'
import Component from './Component.vue'
import { createI18n } from 'vue-i18n'
const i18n = createI18n({
locale: 'en',
messages: {
en: {
welcome: 'Welcome'
}
}
})
const wrapper = mount(Component, {
global: {
plugins: [i18n]
}
})
expect(wrapper.text()).toContain('Welcome')
性能优化技巧
- 按需加载语言包
- 使用CDN托管语言文件
- 实现语言缓存
- 预加载用户可能使用的语言
// 预加载相邻语言
function preloadAdjacentLocales(locale) {
const localesToPreload = getAdjacentLocales(locale)
localesToPreload.forEach(l => {
import(`./locales/${l}.json`)
})
}
常见问题解决方案
问题1:动态键名翻译
// 使用计算属性
const translatedKey = computed(() => $t(`page.${currentPage}.title`))
问题2:混合内容翻译
// 使用数组和v-for
const mixedContent = computed(() => [
{ text: $t('part1'), isHtml: false },
{ html: '<strong>HTML内容</strong>', isHtml: true }
])
问题3:处理缺失的翻译
// 自定义缺失处理器
const i18n = createI18n({
// ...其他配置
missing: (locale, key) => {
console.warn(`Missing translation for ${key} in ${locale}`)
return key
}
})
高级自定义需求
对于需要高度自定义的项目,可以扩展i18n功能:
// 自定义指令
app.directive('t', {
mounted(el, binding) {
el.textContent = i18n.global.t(binding.value)
},
updated(el, binding) {
el.textContent = i18n.global.t(binding.value)
}
})
// 使用方式
<p v-t="'welcome'"></p>
与其他工具集成
与Pinia状态管理集成:
// stores/i18n.js
export const useI18nStore = defineStore('i18n', {
state: () => ({
locale: 'en'
}),
actions: {
setLocale(locale) {
this.locale = locale
i18n.global.locale.value = locale
}
}
})
与TailwindCSS集成实现RTL支持:
// 根据语言切换HTML方向
watch(() => i18n.global.locale.value, (locale) => {
document.documentElement.dir = locale === 'ar' ? 'rtl' : 'ltr'
})
构建优化
在Vite配置中优化国际化资源:
// vite.config.js
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks(id) {
if (id.includes('locales')) {
return 'locales'
}
}
}
}
}
})
实际应用场景
电商网站的产品多语言展示:
<template>
<div v-for="product in products" :key="product.id">
<h3>{{ $t(`products.${product.id}.name`) }}</h3>
<p>{{ $t(`products.${product.id}.description`) }}</p>
<p>{{ $n(product.price, 'currency') }}</p>
</div>
</template>
多语言表单验证:
// 验证规则
const rules = {
required: value => !!value || i18n.global.t('validation.required'),
email: value => /.+@.+\..+/.test(value) || i18n.global.t('validation.email')
}
持续维护策略
- 建立翻译键名规范
- 自动化提取未翻译文本
- 集成翻译管理系统
- 定期审查翻译质量
# 提取未翻译文本的示例脚本
grep -r "\$t(" src/ | awk -F"\$t\\('" '{print $2}' | awk -F"'" '{print $1}' | sort | uniq > keys.txt
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn