Vue项目中i18n配置多语言(国际化)--配合ElementUi库

1,104 阅读4分钟

Vue项目中i18n配置多语言(国际化)--配合ElementUi库

要求

  1. vscode安装i18n Ally插件
  2. npm i element-ui -S
  3. node_modules→element-ui→lib→locale→lang(文件夹下找到对应的语言包.js,转成JSON格式,复制到相应位置)

src文件夹中创建语言包文件夹

├─src                   ---------页面文件夹(开发代码目录)
│ ├─lang                ---------语言包文件夹
│ ├─├─locale            ---------存放多语言文件夹和相关配置
│ ├─├─├─en-GB.json      ---------多语言英文
│ ├─├─├─zh-CN.json      ---------多语言中文
│ ├─├─i18n-setup.js     ---------i18n相关配置

en-GB.json

对象里面有两个key:

el是element-ui库的英文(key-value),其他ui库类似

lang是自己代码里面的英文(key-value)

{
  "el": {
    "colorpicker": { "confirm": "OK", "clear": "Clear" },
    "datepicker": {
      "now": "Now",
      "today": "Today",
      "cancel": "Cancel",
      "clear": "Clear",
      "confirm": "OK",
      "selectDate": "Select date",
      "selectTime": "Select time",
      "startDate": "Start Date",
      "startTime": "Start Time",
      "endDate": "End Date",
      "endTime": "End Time",
      "prevYear": "Previous Year",
      "nextYear": "Next Year",
      "prevMonth": "Previous Month",
      "nextMonth": "Next Month",
      "year": "",
      "month1": "January",
      "month2": "February",
      "month3": "March",
      "month4": "April",
      "month5": "May",
      "month6": "June",
      "month7": "July",
      "month8": "August",
      "month9": "September",
      "month10": "October",
      "month11": "November",
      "month12": "December",
      "week": "week",
      "weeks": {
        "sun": "Sun",
        "mon": "Mon",
        "tue": "Tue",
        "wed": "Wed",
        "thu": "Thu",
        "fri": "Fri",
        "sat": "Sat"
      },
      "months": {
        "jan": "Jan",
        "feb": "Feb",
        "mar": "Mar",
        "apr": "Apr",
        "may": "May",
        "jun": "Jun",
        "jul": "Jul",
        "aug": "Aug",
        "sep": "Sep",
        "oct": "Oct",
        "nov": "Nov",
        "dec": "Dec"
      }
    },
    "select": {
      "loading": "Loading",
      "noMatch": "No matching data",
      "noData": "No data",
      "placeholder": "Select"
    },
    "cascader": {
      "noMatch": "No matching data",
      "loading": "Loading",
      "placeholder": "Select",
      "noData": "No data"
    },
    "pagination": {
      "goto": "Go to",
      "pagesize": "/page",
      "total": "Total {total}",
      "pageClassifier": ""
    },
    "messagebox": {
      "title": "Message",
      "confirm": "OK",
      "cancel": "Cancel",
      "error": "Illegal input"
    },
    "upload": {
      "deleteTip": "press delete to remove",
      "delete": "Delete",
      "preview": "Preview",
      "continue": "Continue"
    },
    "table": {
      "emptyText": "No Data",
      "confirmFilter": "Confirm",
      "resetFilter": "Reset",
      "clearFilter": "All",
      "sumText": "Sum"
    },
    "tree": { "emptyText": "No Data" },
    "transfer": {
      "noMatch": "No matching data",
      "noData": "No data",
      "titles": ["List 1", "List 2"],
      "filterPlaceholder": "Enter keyword",
      "noCheckedFormat": "{total} items",
      "hasCheckedFormat": "{checked}/{total} checked"
    },
    "image": { "error": "FAILED" },
    "pageHeader": { "title": "Back" },
    "popconfirm": { "confirmButtonText": "Yes", "cancelButtonText": "No" },
    "empty": { "description": "No Data" }
  },
  "lang": {
    "mypage": {
      "t1": "A bottle of human samples",
      "t2": "I can't be described in one sentence",
      "t3": "Came and said nothing",
      "t4": "pick me",
      "t5": "Please choose",
      "t6": "Do not change your motto at will",
      "t7":"choose date"
    }
  }
}

zh-CN.json

同en-GB类似

{
  "el": {
    "colorpicker": { "confirm": "确定", "clear": "清空" },
    "datepicker": {
      "now": "此刻",
      "today": "今天",
      "cancel": "取消",
      "clear": "清空",
      "confirm": "确定",
      "selectDate": "选择日期",
      "selectTime": "选择时间",
      "startDate": "开始日期",
      "startTime": "开始时间",
      "endDate": "结束日期",
      "endTime": "结束时间",
      "prevYear": "前一年",
      "nextYear": "后一年",
      "prevMonth": "上个月",
      "nextMonth": "下个月",
      "year": "年",
      "month1": "1 月",
      "month2": "2 月",
      "month3": "3 月",
      "month4": "4 月",
      "month5": "5 月",
      "month6": "6 月",
      "month7": "7 月",
      "month8": "8 月",
      "month9": "9 月",
      "month10": "10 月",
      "month11": "11 月",
      "month12": "12 月",
      "weeks": {
        "sun": "日",
        "mon": "一",
        "tue": "二",
        "wed": "三",
        "thu": "四",
        "fri": "五",
        "sat": "六"
      },
      "months": {
        "jan": "一月",
        "feb": "二月",
        "mar": "三月",
        "apr": "四月",
        "may": "五月",
        "jun": "六月",
        "jul": "七月",
        "aug": "八月",
        "sep": "九月",
        "oct": "十月",
        "nov": "十一月",
        "dec": "十二月"
      }
    },
    "select": {
      "loading": "加载中",
      "noMatch": "无匹配数据",
      "noData": "无数据",
      "placeholder": "请选择"
    },
    "cascader": {
      "noMatch": "无匹配数据",
      "loading": "加载中",
      "placeholder": "请选择",
      "noData": "暂无数据"
    },
    "pagination": {
      "goto": "前往",
      "pagesize": "条/页",
      "total": "共 {total} 条",
      "pageClassifier": "页"
    },
    "messagebox": {
      "title": "提示",
      "confirm": "确定",
      "cancel": "取消",
      "error": "输入的数据不合法!"
    },
    "upload": {
      "deleteTip": "按 delete 键可删除",
      "delete": "删除",
      "preview": "查看图片",
      "continue": "继续上传"
    },
    "table": {
      "emptyText": "暂无数据",
      "confirmFilter": "筛选",
      "resetFilter": "重置",
      "clearFilter": "全部",
      "sumText": "合计"
    },
    "tree": { "emptyText": "暂无数据" },
    "transfer": {
      "noMatch": "无匹配数据",
      "noData": "无数据",
      "titles": ["列表 1", "列表 2"],
      "filterPlaceholder": "请输入搜索内容",
      "noCheckedFormat": "共 {total} 项",
      "hasCheckedFormat": "已选 {checked}/{total} 项"
    },
    "image": { "error": "加载失败" },
    "pageHeader": { "title": "返回" },
    "popconfirm": { "confirmButtonText": "确定", "cancelButtonText": "取消" },
    "empty": { "description": "暂无数据" }
  },
  "lang": {
    "mypage": {
      "t1": "一瓶人间小样",
      "t2": "一句话简介不了我",
      "t3": "来过什么话都没有留下",
      "t4": "点我",
      "t5": "请选择",
      "t6": "座右铭岂不随意更改",
      "t7":"选择日期"
    }
  }
}

i18n-setup.js

语言优先级:url参数设置的 > Cookie保存的 > 浏览器默认的

  1. SYS_LANG(浏览器默认语言)
  2. SUPPORT_LANGUAGE(当前支持的语言)
  3. COOKIES_LANG(Cookies里保存的语言)
  4. LINK_LANG(URL里设置的语言)
  5. DEFAULT_LANGUAGE(默认语言)
  6. CURRENT_LANG(入口文件直接拿取当前语言环境, .vue 文件内部用 this.$i18n.locale 来获取即可
import Vue from 'vue';
import Cookies from 'js-cookie';
import VueI18n from 'vue-i18n';
import ElementLocal from 'element-ui/lib/locale' // 引入Element-ui语言包
Vue.use(VueI18n); 

// 浏览器默认语言
let SYS_LANG = navigator.language || navigator.userLanguage;
// 兼容zh-cn模式
SYS_LANG = SYS_LANG.toLowerCase().split('-')[1] ? SYS_LANG.toLowerCase().split('-')[0] + '-' + SYS_LANG.toUpperCase().split('-')[1] : SYS_LANG;

// 当前支持的语言
const SUPPORT_LANGUAGE = ['zh-CN', 'en-GB'];
// Cookies里保存的语言
const COOKIES_LANG = !Cookies.get('my_language') || Cookies.get('my_language') == 'undefined' ? '' : Cookies.get('my_language');
// URL里设置的语言,例如: www.xxx.com/page/xxx.html#/?my-lang=zh-CN(无关参数位置)
const LINK_LANG = location.hash.indexOf('my-lang') != -1 ? location.hash.split('?')[1].split('my-lang')[1].split('=')[1].split('&')[0] : '';
// 上述所有的 语言参数 都为空值的话,则需要提供一个默认的语言。(英语。20210812 确定)
const DEFAULT_LANGUAGE = 'en-GB';
// 当前语言环境, 优先级:  url参数设置的 > Cookie保存的 > 浏览器默认的
// 输出这个参数, 可以在项目main.js入口文件处获取当前语言环境, .vue 文件内部就用 this.$i18n.locale 来获取即可
let CURRENT_LANG = LINK_LANG || COOKIES_LANG || SYS_LANG || DEFAULT_LANGUAGE;// 同上规则设置语言优先级顺序
// 兼容之前的链接方式
CURRENT_LANG = CURRENT_LANG == 'en' ? 'en-GB' : CURRENT_LANG;
CURRENT_LANG = CURRENT_LANG == 'zh' ? 'zh-CN' : CURRENT_LANG;
// 实例化i18n
export const i18n = new VueI18n({
	locale: CURRENT_LANG, // 设置当前语言环境
	fallbackLocale: DEFAULT_LANGUAGE, // 回滚设置
	messages: {}, // 设置语言环境包
	silentTranslationWarn: true
});

ElementLocal.i18n((key, value) => i18n.t(key, value)); // 兼容 element-ui i18n函数

// 改变语言环境
function setI18nLanguage(lang) {
	Cookies.set('my_language', lang, {
		expires: 7,
		path: '/'
	}); // 添加到cookie
	i18n.locale = lang; // 切换语言环境
	document.querySelector('html').setAttribute('lang', lang); // html标签加lang属性备用
	document.querySelector('body').setAttribute('id', lang == 'zh-CN' ? 'zh-CN' : 'en-GB'); // 给body标签加id
	return lang;
}
// 更新本地缓存,并返回对应的语言包
const updataLocalStorage = (lang, langMessage) => {
	// 如果有传入语言包, 就直接更新 localStorage,这里就是针对后端请求有返回,就保存
    // 如果是后端请求返回语言包(下面会做补充),这里不需要后端,存本地也是行的
	if (!!langMessage) {
		localStorage.setItem(lang, JSON.stringify(langMessage));
		return Promise.resolve(langMessage);
    }
    // 如果本地不存在语言
    if (!localStorage.getItem(lang)) {
        return import(`@/lang/locale/${lang}.json`).then((messages) => {
            localStorage.setItem(lang, JSON.stringify(messages));
			return messages
        })
    }
    // 如果本地存在
	return Promise.resolve(JSON.parse(localStorage.getItem(lang)));
};
// 异步加载语言包
export async function loadLanguageAsync(_lang, message) {
	// 语言环境优先级: 需要设置的 > url参数设置的 > Cookie保存的 > 浏览器默认的
	let lang = _lang || CURRENT_LANG;
	lang = SUPPORT_LANGUAGE.includes(lang) ? lang : DEFAULT_LANGUAGE; // 还没有支持的语言就直接用默认的语言
	// updateMetaInfo(lang); // 更新 html页面的 meta信息(可根据自己的情况去写逻辑更新,这里就不在做详细说明)
    const langMessage = await updataLocalStorage(lang, message); // 先获取本地缓存直接使用,后面再根据后端的请求是否要更新
    // console.log(langMessage)
	i18n.setLocaleMessage(lang, Object.assign({}, langMessage));
	return setI18nLanguage(lang);
}

i18n.loadLanguageAsync = loadLanguageAsync;

补充1:取本地语言包或者后端接口返回语言包处理方法(与后端沟通看需要传的参数)

// 请求语言包
async getTranslation(item) {
    try {
	let res = await axios.get('/base/i18n/v1/translation.php', {
	params: {
            l:'zh-CN',// 当前什么语言,可变的,根据自己需求,这里暂时写死为zh-CN
            m: 'pc',// 什么端,PC或h5
	}
	});
	if (Array.isArray(res.data)) return;
	// 设置版本
	localStorage.setItem(this.lang + '-version', item['last_translation_pair_id']);
	// 请求成功,缓存
	this.$i18n.loadLanguageAsync(this.lang, res.data);
    } catch (error) {
    }
},

main.js中使用


import Vue from 'vue'
import App from './App'
import router from './router'

// 按需导入elementUI库
import { Button, Select, Option,Message,Tabs,TabPane,DatePicker} from 'element-ui';
// i18n
import { i18n } from './lang/i18n-setup';
Vue.component(Button.name, Button);
Vue.component(Select.name, Select);
Vue.component(Option.name, Option);
Vue.component(Message.name, Message);
Vue.component(Tabs.name, Tabs);
Vue.component(TabPane.name, TabPane);
Vue.component(DatePicker.name, DatePicker);
Vue.prototype.$message=Message
// i18n项目中的使用方法
i18n.loadLanguageAsync().then(() => {
    new Vue({
        el: '#app',
          router,
          store,
          i18n,
        components: { App },
        template: '<App/>'
      })
})

补充:下面的写法,只会让当前页面只有英文和你传的语言包

// i18n项目中的使用方法
i18n.loadLanguageAsync('en-GB',{语言包}).then(() => {
    new Vue({
        el: '#app',
          router,
          store,
          i18n,
        components: { App },
        template: '<App/>'
      })
})

.vue文件夹中使用

template中:使用多语言:$t('lang.mypage.t5')翻译为:请选择

<template>
  <div>
    <el-select v-model="language" :placeholder="$t('lang.mypage.t5')" @change="change">
      <el-option v-for="item in options":key="item.value":label="item.label":value="item.value">
      </el-option>
    </el-select>
  </div>
</template>

script中

<script>
export default {
  created() {
    // console.log(this.$i18n.locale, "语言包");
  },
  data() {
    return {
      language: "",
      options: [
        {
          value: "zh-CN",
          label: "中文"
        },
        {
          value: "en-GB",
          label: "English"
        }
      ],// 暂时只有中文英文,这是elementui中的下拉切换语言用
    };
  },
  methods: {
    async change(val) {
      if (this.$i18n.locale === val) return;// 如果是当前语言直接return
      await this.$i18n.loadLanguageAsync(val);// 不是当前语言,切换语言
    },
  }
};
</script>

展示效果

image-20220408142500711.png