vue项目国际化i18n的实现及思考
国际化是什么?
国际化对应的英文单词为 Internationalization,又称“i18n”:
- i 为单词的【第一个】字母
- 18 为【i 和 n 之间】字母的个数
- n 为单词的【最后一个】字母
如果你的项目是vue,那么相信你在实现国际化功能时,也必不可少的会使用到“vue-i18n”这个库,接下来本文也是通过这个库搭配vue实现最基本的国际化功能。
一、集成vue-i18n
1、安装依赖
npm i vue-i18n -S
2、配置vue-i18n
- 在src目录下创建i18n目录用于保存和语言切换相关的内容
- 在i18n目录下创建lang目录用于保存不同语言的映射关系,如中文对应zh.js、英文对应en.js等
- 在i18n目录下创建index.js作为默认导出,并在其中创建i18n对象
// index.js import { createI18n } from 'vue-i18n' import A_zh from './lang/A/zh' import A_en from './lang/A/en' const i18n = createI18n({ legacy: false, locale: 'zh', // 初始化配置语言 messages:{ zh: { aFile: A_zh }, en: { aFile: A_en } }, silentTranslationWarn: true // 隐藏警告 }) export default i18n
3、主文件main.js注册i18n
import { createApp } from 'vue' import i18n from './i18n' import store from './store' import App from './App.vue' createApp(App) .use(store) .use(i18n) .mount('#app')
实际上在通过use(i18n)时,会调用i18n.install()方法,大概内容如下:
- 通过app.provide(app.__VUE_I18N_SYMBOL__, i18n)将i18n对象提供给应用中的所有后代组件可通过inject注入
- 通过app.config.globalProperties.xxx = xxx的方式为应用添加全局属性/方法,实际上是对Vue2中Vue.prototype使用方式的一种替代
- 常见全局属性,如$i18n通过app.config.globalProperties.$i18n=i18n添加到全局
- 常见全局方法,如$t,$rt,$d,$n,$tm通过Object.defineProperty(app.config.globalProperties, `$${method}`, desc)添加到全局
- 通过apply(...)方法注册常用的全局指令和全局组件i18n等
- 在根组件卸载时移除/释放i18n相关内容
const unmountApp = app.unmount app.unmount = () => { i18n.dispose() unmountApp() }
- 注册vue-devtools的相关插件
二、根据数据信息填充国际化内容
1、填充lang目录下文件映射关系
在lang/A/zh.js文件中:
export default { name:'年龄' }
在lang/A/en.js文件中:
export default { name: 'name' }
2、页面渲染
翻译处理可通过如下方式处理:
- 使用$t(...)方法
- 使用v-t指令
- 使用<i18n-t></i18n-t>组件
这里选择第一种,因为它更灵活,能使用到的范围也更广,指令和组件形式限定在了template中:
<template> <div>{{$t('aFile.name')}}</div> </template>
基于以上简单的例子,已经能够实现了国际化切换功能。
三、思考
1、转换翻译文件类型(.js转.json)
上述的翻译文件是.js文件,因此,为了能够让其能够被其他文件导入,我们不得不在文件中使用export default或export将对应文件内容向外导出,但其实我们可以将.js文件转换为.json文件直接使用,避免新增一个国际化语言文件就需要手动导入一次翻译文件的问题。
在src/index.js文件中:
// index.js import { createI18n } from 'vue-i18n' // 动态读取.json文件中定义的国际化语言字段 const getMessage = () => { const messages = {} const modules = import.meta.globaEager('./lang/*') for(let key in modules){ let langKey = key.replace(/\.\/lang\/(.+).json$/, (match, p1) => p1) messages[langKey] = modules[key].default } return messages } const i18n = createI18n({ legacy: false, locale: 'zh', // 初始化配置语言 messages: getMessage() }) export default i18n
2、考虑不同语言的样式
由于不同语言的表现形式不同,内容长度也不一致,因此在前端进行展示时,就必须要考虑到最终的显示问题,否则一旦切换语言环境那么一定会导致页面的布局展示出现问题,处理方式无非几种:
- 允许文字内容换行展示,在文字发生换行时,要通过css设置按完整词换行;不允许换行的,就要控制固定宽度,超出部分打点展示,鼠标移入展示全部内容等
- 单独为不同语言环境设置样式,具体还是得看展示需求,如需要考虑不同语言环境下文字的对齐方式、文字间距等
- 针对难以处理的翻译内容,可以通过和业务沟通是否可以替换翻译内容、缩减文字长度等等
3、考虑后端接口语言环境变更
一个项目的国际化不可能都是前端来实现的,一些接口动态返回的内容也是需要后端去处理的,通常接口的请求头中会存储一个用于标识当前页面语言环境的字段,然后再决定返回给前端页面的具体内容。
基于前面处理的国际化切换功能,本身是会基于vue的响应式来切换翻译内容的,即不会刷新页面,因此在切换对应翻译内容后,同样需要修改后续接口请求头中的语言环境。
但这样还是有问题的,已经通过接口返回的数据内容,此时没有办法切换成对应的翻译内容,因此当前的国际化切换是基于页面的变动,但基于接口变动的部分还没重新请求获取新的内容,那怎么处理呢?
- 前端切换语言环境后,重新刷新页面,再次调用接口获取新的内容
- 后端在返回数据时,将对应的不同语言环境的翻译内容一起返回,由前端根据语言环境决定如何渲染
- 将所有的翻译内容全部交由前端管理,一开始就初始化好各个语言环境对应的翻译内容
以上内容是基于国际化功能的一点思考,记录的只是自己在项目中遇到的点,并不一定适用所有项目。