最近一个优先级高的任务——将之前的vue项目全局国际化,时间紧急虽然付出了较多的努力,但因为不够细心,导致出现单词拼错、翻译缺漏等错误,还有翻译过后因中英文字符长度差异产生的样式问题,上线效果并不理想。这里记录一下vue-i18n国际化语言包处理插件的使用。
vue-i18n官网文档
安装
npm install vue-i18n --save
配置及使用
在src目录新建i18n目录,en.js为英文语言包,zh.js为中文语言包
en.js文件内容
export default {common: {username: '用户名',password: '密码',save: '保存',edit: '编辑',update: '更新',delete: '删除',forever: '永久',expired: '过期'}}
zh.js 文件内容
export default {common: {username: 'username',password: 'password',save: 'Save',edit: 'Edit',update: 'Update',delete: 'Delete',forever: 'Forever',expired: 'Expired'}}
i18n 下index.js文件
import Vue from 'vue'import VueI18n from 'vue-i18n'import Cookies from 'js-cookie'import elementEnLocale from 'element-ui/lib/locale/lang/en' // element-ui lang导入Element的语言包 英文import elementZhLocale from 'element-ui/lib/locale/lang/zh-CN'// element-ui lang g导入Element的语言包 中文import enLocale from './en' // 导入项目中用到的英文语言包import zhLocale from './zh'// 导入项目中用到的中文语言包Vue.use(VueI18n)const messages = {en: {...enLocale,...elementEnLocale},zh: {...zhLocale,...elementZhLocale,},}const i18n = new VueI18n({locale: localStorage.getItem('language') || 'zh', // 设置语种messages, // 设置全局当地语言包,fallbackLocale: 'zh',numberFormats:{ //设置 数字本地化'en': {currency: { //添加 $style: 'currency', currency: 'USD'}},'zh': {currency: { //添加 ¥style: 'currency', currency: 'JPY', currencyDisplay: 'symbol'}}},dateTimeFormats:{//设置 日期时间本地化'en': {short: { year: 'numeric', month: 'short', day: 'numeric'},long: {year: 'numeric', month: 'short', day: 'numeric',weekday: 'short', hour: 'numeric', minute: 'numeric'}},'zh': {short: {year: 'numeric', month: 'short', day: 'numeric'},long: {year: 'numeric', month: 'short', day: 'numeric',weekday: 'short', hour: 'numeric', minute: 'numeric'}}}})export default i18n
在主入口文件main.js
中
import Vue from 'vue'import Element from 'element-ui'import 'element-ui/lib/theme-chalk/index.css'//导入配置好的国际化语言包import i18n from './i18n' // InternationalizationVue.use(Element, {size: 'medium', // set element-ui default size设置元素默认大小i18n: (key, value) => i18n.t(key, value)// 在注册Element时设置i18n的处理方法})Vue.config.productionTip = false// 生产提示new Vue({el: '#app',i18n, // 注入 配置好的国际化语言配置})
在vue中注册i18n之后,我们便可以在组件中通过this.$i18n
获取到我们配置好的这个i18n对象
将导入的VueI18n
实例化,在new VueI18n()中传入配置信息:
- locale 当前语种
- fallbackLocale 如果当前语种不存在时,默认设置当前语种
- messages 本地语言包(’en’,’zh’…)
- numberFormats 设置 数字格式化
- dateTimeFormats 设置 时间日期格式化 接下来一一介绍:
locale主要是用来设置语种包 如何切换locale?
//在组件中import i18n from "@/i18n" //根据项目对应真实路径i18n.locale="en" // 改变为中文localStorage.setItem('language',"en")//在localStorage中存入设置
messages
一般会将项目中的静态语言分成产品所需要的各个语种,包括中文,英文,法文等…比如:
message:{zh: {common: {username: '用户名',password: '密码',save: '保存',edit: '编辑',update: '更新',delete: '删除',forever: '永久',expired: '过期'}},en: {common: {username: 'username',password: 'password',save: 'Save',edit: 'Edit',update: 'Update',delete: 'Delete',forever: 'Forever',expired: 'Expired'}}}
zh
就代表静态语言中文版,en
代表静态语言英文版,一般就将en
,zh
单独分成两个模块——en.js
,zh.js
.这样项目不仅结构变得更简洁明了,在维护开发阶段也节省很多时间。
$t 的使用(格式化)
简单来说就是拿到messages
中的某个数据,参数是字符串,代表着所要获取的某一条数据
注意:
放在data中的静态数据 国际化需要放到computed中,切换时才会有响应式;
当翻译定义在consts 里的某些常量时,用 i18n.t 翻译 再用函数返回, 在页面使用时在computed 中调用函数,切换时才会有响应式;
简单使用
//template 需要使用 {{}} 将 name包装起来{{$t('save')}}// jsthis.$t('save')
[命名格式化]
const messages = {en: {message: {title: 'hello, {name}'}}}//组件中使用{{ $t('message.title', { name: 'Kite' }) }}
//输出hello, Kite
这种叫做命名格式化,可以区域格式化信息。 这种情况下$t
的第二参数是一个对象,就代表了所有需要动态添加的内容,注意是所有在message
中所需要动态添加的变量都在第二个参数中。
1.未传第二个参数
const message = {en: {message: {title: '{a}{b}哈哈'}}}//组件中使用1{{ $t('message.title') }}
//输出{a}{b}哈哈
//组件中使用2, {{ $t('message.title',{a:1,b:2) }}
//输出12哈哈
如上述例子中显示,如果未传第二个参数,那么将会都以按照字符串的形式展示.
2.不同的命名类型
const messages = {en: {message: {title: '{a}{b}哈哈'}}}//组件中使用数值型与字符串类型 1{{ $t('message.title',{a:1,b:"2") }}
//输出12哈哈
//组件中使用2 对象类型{{ $t('message.title',{a:{},b:"2") }}
//输出[object Object]2哈哈
//组件中使用3 数组类型(内含布尔,对象类型){{ $t('message.title',{a:[1,2,3,true,{}],b:"2") }}
//输出123true[object Object]2哈哈
- object对象类型会直接输出
[object Object]
- boolean布尔类型会按照字符串输出
true-->"true"/false-->"false"
- array数组类型中,如果每一项都是数值或者字符串那么直接输出,否则按上述情况显示
[列表格式]
const message = {en: {message: {sing: '{0}{1}{2}忘了想念'}}}// 组件内使用 1 列表格式{{ $t('message.sing', ['我','以','为']) }}
//输出我以为忘了想念
// 组件内使用 2 类似命名格式的方式{{ $t('message.sing', { '0':"我",'1':'以','2':'为' }) }}
//输出我以为忘了想念
$tc 的使用 (复数)
const messages = {en: {apple: 'no apples | one apple | {count} apples',banana: 'no bananas | {n} banana | {n} bananas'}}
这里必须注意一点:必须定义具有管道|
分隔符的区域设置,并在管道分隔符中定义复数
//在组件内使用{{ $tc('apple', 10, { count: 10 }) }}
{{ $tc('apple', 10) }}
{{ $tc('banana', 1, { n: 1 }) }}
{{ $tc('banana', 1) }}
{{ $tc('banana', 100, { n: 'too many' }) }}
//输出10 apples
10 apples
1 banana
1 banana
too many bananas
1、在定义复数时,必要使用
|
管道符分隔。2、当
|
管道符等于1个时,索引从1开始,最大索引为2;当管道符大于1个时,索引从0开始(类似数组),最大索引为管道符的个数3、通过使用
$tc(“复数”,index)
获取对应语言文本
dateTimeFormats日期时间格式化
dateTimeFormats:{//设置 日期时间本地化'en': {short: { //显示英文 年月日year: 'numeric', month: 'short', day: 'numeric'},long: { //显示英文 年月日 星期 小时 分钟year: 'numeric', month: 'short', day: 'numeric',weekday: 'short', hour: 'numeric', minute: 'numeric' }},'zh': {short: {year: 'numeric', month: 'short', day: 'numeric'},long: {year: 'numeric', month: 'short', day: 'numeric',weekday: 'short', hour: 'numeric', minute: 'numeric'}}}
处理数字有$n
方法,对于日期时间格式化也有对应的方法——$d
//在组件中使用{{ $d(new Date(), 'short') }}
{{ $d(new Date(), 'long') }}
{{ $d(new Date(), 'short','zh') }}
{{ $d(new Date(), 'long', 'zh') }}
//输出Jul 31, 2022Sat, Jul 31, 2022, 5:55 PM2022年07月23日2018年07月23日 周六 下午5:55
numberFormats设置数字格式化包括货币类型等.
numberFormats:{ 'en': {currency: { //添加 $ style: 'currency', currency: 'USD'}},'zh': {currency: { //添加 ¥style: 'currency', currency: 'JPY', currencyDisplay: 'symbol'}}},
$n(数字本地化)
对于数字格式化在组件中如何使用,如同$t
一样,也提供了对应的方法$n
.
//组件中使用{{ $n(100, 'currency') }}
{{ $n(100, 'currency', 'zh') }}
//输出$100.00
¥100
$n(number,'path','locale')
方法,三个参数:
number | 用户传入数字 必传 |
path | 调用的格式化方案 必传 |
locale | 使用的语种,默认是当前this.$i18n.locale |
8.10+ 新增:
$n
方法返回的结果字符串带有完全格式化的数字,该数字只能作为整体使用。 在需要格式化格式化数字的某些部分(例如小数位)的情况下,$n
是不够的。 在这种情况下,功能组件将有所帮助。
有了最少的一组属性,
产生的输出与
$n
相同,并包装到已配置的DOM元素中。
{{ slotProps.currency }}{{ slotProps.integer }}{{ slotProps.group }}{{ slotProps.fraction }}// 结果€1,23400
常用方法
getLocaleMessage
this.i18n.getLocaleMessage('key')
,这个方法是用来获取全局语言包中某个语种的语言包,比如:
this.i18n.getLocaleMessage('en')//获取英文的语言包 返回的同样是一个对象
mergeLocaleMessage
this.i18n.mergeLocaleMessage('key',localeData)
,这是方法是用来对于 ‘key’语种中添加本地语言包,往往在某个子组件中,我们会将某个区域语言包合并到全局对应语种的语言包
this.$i18n.mergeLocaleMessage('zh', local.zh)//向全局中文语言包中补充内容
v-t
自定义指令v-t
.它和$t
作用一样,都是获取语言包中的某条数据,但也有很大的区别
v-t
的使用
//locale='en'const messages = {en: {message: {title: hello'}},zh:{ message: {title: '哈哈{name}'}}}//组件中使用1 字符串用法//输出 hello//组件中使用2 对象用法 //输出 哈哈cfz
v-t和$t的区别
$t 是扩展的Vue实例方法
[优点] 在模板中灵活使用小胡子语法(mustash)语法{{}},也可以在Vue实例中灵活地使用计算道具和方法。
[缺点] 每次重新渲染时都会执行,因此它比较消耗性能。
v-t是一个自定义指令
[优点] 当翻译一次时,由于其使用自定义指令进行缓存,因此具有比$t
方法更好的性能.
[缺点] v-t不能灵活使用$t,因为它相当复杂。已翻译的内容v-t将插入到textContent元素中。
还有一些其他的用法,具体的请参考官方文档
插入组件
如果遇到这样的场景,如何去处理?
I accept xxx Terms of Service Agreement
我的第一反应是分成两个字段,a标签不属于翻译的内容,只要写成:
{{ $t('xx1') }}{{ $t('xx2') }}
看了官网的介绍,说这种处理太笨拙了,可以通过组件的方式去处理
使用了两个变量存储信息 通过tag
来生产标签,path
来制定标签的内容
// 这里使用了i18n 组件{{ $t('tos') }}const messages = {en: {tos: 'Term of Service',term: 'I accept xxx {0}.'}}new Vue({el: '#app',i18n,data: {url: '/term'}})
更高级的用法,可以控制html元素的插入位置,通过place
来指定出现在html中的位置。
{{ changeLimit }}{{ $t('change') }}const messages = {en: {info: 'You can {action} until {limit} minutes from departure.',change: 'change your flight',refund: 'refund the ticket'}}const i18n = new VueI18n({locale: 'en',messages})new Vue({i18n,data: {changeUrl: '/change',refundUrl: '/refund',changeLimit: 15,refundLimit: 30}}).$mount('#app')// resultYou can change your flight until 15 minutes from departure.
动态加载语言包
一次加载所有的语言包是没有必要的,特别是语言包过的情况下,之前我也提出了这个问题,发现官网上是给了解决方式的。
//i18n-setup.jsimport Vue from 'vue'import VueI18n from 'vue-i18n'import messages from '@/i18n' // 语言包的地址,随项目本身设置修改import axios from 'axios' // 根据项目中使用api请求模块去设置,不一定是axiosVue.use(VueI18n)export const i18n = new VueI18n({locale: 'en', // set localefallbackLocale: 'en', // 默认语言设置,当其他语言没有的情况下,使用en作为默认语言messages // set locale messages})const loadedLanguages = ['en'] // our default language that is prelaoded function setI18nLanguage (lang) {i18n.locale = langaxios.defaults.headers.common['Accept-Language'] = lang // 设置请求头部document.querySelector('html').setAttribute('lang', lang) // 根元素增加lang属性return lang}export function loadLanguageAsync (lang) {if (i18n.locale !== lang) {if (!loadedLanguages.includes(lang)) {return import(/* webpackChunkName: "lang-[request]" */ `@/lang/${lang}`).then(msgs => {i18n.setLocaleMessage(lang, msgs.default)loadedLanguages.push(lang)return setI18nLanguage(lang)})} return Promise.resolve(setI18nLanguage(lang))}return Promise.resolve(lang)}// 在vue-router的beforeEach的全局钩子处理router.beforeEach((to, from, next) => {const lang = to.params.langloadLanguageAsync(lang).then(() => next())})