【i18n】i18n的使用方式


前言

i18n的简单定义

i18n是internationalization的简称。指让产品无需做出大的改变就能适应不同语言和地区的需要。


一、vue-i18n的使用

只专注于Vue3的vue-18n,即v9版本
vue-i18n是为vue.js而开发的国际化插件,使得开发者在Vue应用内插i18n更加便捷。

1. 安装i18n插件

// npm
npm install vue-i18n@9
// yarn
yarn add vue-i18n@9

2. 配置i18n插件

新建i18n文件夹后,新建index.ts
在i18n/index.ts做i18n的配置工作。

createI18n(options)方法的参数options

options指定了语言包、语言(名称)、是否使用传统API、是否全局注入、是否抑制回退警告、是否抑制本地化警告、日期时间格式等等
语言包messages和语言locale是最重要的配置,也是通常需要用户手动注入的动态参数

const i18n = createI18n({
	legacy: false, // 是否使用传统API模式,设为false指定使用Composition API模式(vue3设为false)
	globalInjection: true, // 为每个组件注入全局属性和函数
	silentTranslationWarn: true, // 抑制本地化失败警告
	silentFallbackWarn: true, // 抑制回退失败警告
	locale: 'en', // 语言
	fallbackLocale: 'en', // 回退语言
	messages, // 语言包(配置语言包的教程在下方)
})
export default i18n;

配置语言包

在i18n文件夹下建立语言包文件夹lang,中新建zn-cn.ts, en.ts等
语言包文件实际上是一个可嵌套的键值对对象,不同语言的key值相同,value便是本区语言翻译。
示例如下:

// zh-cn.ts
export default {
	common: {
		add: '新建',
		edit: '编辑‘
	}
}

// en.ts
export default {
	common: {
		add: 'Add',
		edit: 'Edit'
	}
}

在i18n/index.ts中引入语言包
最简单示例

import en from './lang/en.ts'
import zhCn from './lang/zh-cn.ts'
const messages = {
	en: { name: 'en', ...en},
	'zh-cn': {	name: 'zh-cn', ...zhCn}
}

引入语言包后的操作就是上方的createI18n()了


语言包文件分包

不只是在i18n/lang下建立语言包文件,可以在组件文件夹或任意地方建立语言包文件,避免lang下文件包体积太大,开发时频繁切换
可以在开发的任意组件文件夹下设立语言包,such as views/admin/user(用户列表文件夹)下建立i18n文件夹,新建zh-cn.ts和en.ts等,这些语言包是如何让i18n知道呢?

// i18n/index.ts

const itemilizes = { 'zh-cn': [], 'en': []}
const modules = import.meta.global('./**/*.ts', {eager: true})
const pages = import.meta.global('./../../**/**/**/i18n/*.ts', {eager: true})
const regexp = /(\S+)\/(\S+).ts/
for(const path in modules) {
	const key = path.match(regexp)
	if (itemilizes[key[2]]) itemilizes[key[2].push(modules[path].default
	else itemilizes[key[2]] = [modules[path.default]
}
for (const path in pages){
	const key = path.match(regexp)
	if (itemilizes[key[2]]) itemilizes[key[2].push(pages[path].default
	else itemilizes[key[2]] = [pages[path.default]
}

for(const [name, el] of Object.entries(itemilizes) {
	messages[name] = {
		name,
		...mergeArrObj(el)
	}
}
const mergeArrObj = (arr) => {
	const obj = {}
	arr.forEach(item => Object.assign({}, obj, item))
	return obj
}

引入element-plus语言包

若应用使用了element-plus组件库,需要引入组件库的语言包

import enLocale from 'element-plus/lib/locale/lang/en'
import zhCnLocale  from 'element-plus/lib/locale/lang/zh-cn'

Object.assign(messages, {
	'en': enLocale,
	'zh-cn': zhCnLocale
}

上面只是引入了element-plus的语言包
但是如何让element-plus知道正在应用的是那个语言呢?

element-plus是支持通过 Config Provider 来配置多语言的
所以,为了更改element-plus的语言
在App.vue中包裹el-config-provider

// App.vue
<template>
	<el-config-provider :locale="getGlobalI18n">
	</el-config-provider>
</template>

<script lang="ts" setup>
import { useI18n } from 'vue-i18n'

const { message, locale } = useI18n()

// 实际传入的是某个语言包,而不是语言(名称)
const getGlobalI18n = computed(() => message.value[locale.value])
</script>

获取用户配置的语言包

若应用支持用户手动配置语言包,则语言包需要交给后台管理,但是每次应用刷新时都需要请求来获取后台管理的语言包
同样,这段代码写在i18n/index.ts中即可

fetchI18n()
const fetchI18n = async () => {
	// 假设后端返回的数据格式是:{ zh-cn: [{'delete: '删除'}, 'search': '查询'], en: [{'delete': 'Delete', 'search': 'Search'}]
	const infoI18n = await info(); //info是向后端请求的接口
	const messagesLocal = {}
	for(const [name, data] of Object.entries(infoI18n.data) {
		messagesLocal[name] = {
			name,
			...mergeArrObj(data)
		}
		// 需要注意的是,后台保存的语言包合并到已经创建的i18n对象的语言包上,而不是注入到messages(注入到messages就需要等待后台返回后才可以创建i18n对象)
		i18n.global.mergeLocaleMessage(name, messagesLocal[name])
	}
}


配置语言

语言状态是持久化存储数据
应用到底使用哪种语言应该交给用户来决定,而语言的状态应该交给存储库(pinai/vuex)来管理(它不是单一组件的状态,起码i18n/index和语言切换组件不在同一个地方),为了每次刷新都不会更改用户的语言选择结果,而应该将语言持久化保存

// 主题设置Store,除了i18n,还可以设置其他内容,比如主题色、主题模式等

import {defineStore} from 'pinia'

export const useThemeConfig = defineStore('themeConfig', {
	state: () => {
		themeConfig: {
			globalI18n: 'zh-cn'
		}
	},
	persist: true
})

// i18n/index.ts

import { useThemeConfig } from '/@/stores/themeConfig'

const { themeConfig } = storeToRefs(useThemeConfig(pinia))

// 修改createI18n
createI18n({
	// balabala --- 看上文
	locale: themeConfig.value.globalI18n
})

// 如果有动态语言包,则修改fetchI18n ()
const fetchI18n = async () => {
	// balabala...
	i18n.global.locale.value = themeConfig.value.globalI18n
}
// 上文中的pinia是什么呢?
import { createPinia } from 'pinia';
import piniaPluginPersist from 'pinia-plugin-persist';

// 创建
const pinia = createPinia();
pinia.use(piniaPluginPersist);

// 导出
export default pinia;

语言切换组件就不说了,无非就是更改themeConfig中的globalI18n状态而已

3. 引用i18n插件

需要将i18n挂载到vue实例

// main.js
import i18n from '/@/i18n'

const app = createApp(App);
app.use(i18n)

4. 使用i18n

单文件组件

可以在<template>或<script>中使用i18n

template中可以直接使用$t()来使用i18n

<div class="content">{{ $t('common.add') }}</div>

// 最终在浏览器上显示i18n的翻译结果

script中需要引入t

import { useI18n } from 'vue-i18n'

const { t } = useI18n()

任意ts/js文件

useI18n只可在组件中使用,否则会抛出错误
如果非要使用useI18n(), 那么t的声明就必须放在某个函数中,且此函数会由组件触发
下面的使用方式会报错✖️:

import { useI18n } from 'vue-i18n'
const { t } = useI18n()

可以用以下方案替代✔️:

import i18n from '/@/i18n'

const { t } = i18n.global

二、element-plus中i18n的使用

element-plus中并没有引入vue-i18n,useI18n是自己编写的函数

通过eleement-plususeI18n的编写,也许会对i18n的t有一个更深入的了解

1.语言包

element-plus也是有自己的语言包的,并且不只是中英文,一共支持55种语言
位于packages/locale/lang文件夹中

element-plus语言包位置图
语言包格式如下:
element-plus英文语言包

2.useLocale

useLocale放在hooks/use-locale文件夹下的index.ts中

// hooks/use-locale/index.ts
// 就是语言包下的英文语言包,也是element-plus的默认语言包
import English from '@element-plus/locale/lang/en'
import { get } from 'lodash-unified'

// 真正的t方法,可接收两个参数(locale来自于父函数)第二个参数是一个对象,用于替换翻译文本中{}中的内容
const translate = (path, option, locale) => {
	get(locale,path,path).replace(/\{(\w+)\}/g, (_, key) => `${option?.[key] ?? `{${key}}`}`)
}

// locale一定是一个响应式语言包, 一个闭包函数,目的是避免用户传入当前使用的语言包
export const buildTranslator = (locale) => (path,option) => translate(path,option, unref(locale))

// locale可能是一个响应式语言包, lang确保是响应式数据,开发者可以在任意地方引用而不担心数值变更
// 必须确保传入t方法的语言包是当前应用的语言包,所以传入
export const buildLocaleContext = (locale) => {
	const lang = computed(() => unref(locale).name)
	const localeRef = isRef(locale) ? locale : ref(locale)
	return {
		lang,
		locale: localeRef,
		t: buildTranslator(locale)
	}
}

// localeOverrides是可选参数,响应式Ref数据,指向某个具体的语言包
// 可通过inject接收祖辈组件传入的语言包,且响应其变化
// 需要把locale must to be 响应式数据,因为用户在使用过程中可能会修改语言类型,为了能够实时切换lang和t方法的语言包,所以传入buildLocaleContext方法的也必须是响应式语言包
export const useLocale = (localeOverrides) => {
	const locale = localOverrides || inject(localContextKey, ref())
	return buildLocaleContext(computed(() => locale.value || English))
}

特别巧妙的点在于生成t函数的闭包函数b

3.element-plus组件使用国际化

import { useLocale } from '@element-plus/hooks'
const { t } = useLocale()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值