美文网首页
Vue2 element-ui 框架中集成国际化 vue-i18

Vue2 element-ui 框架中集成国际化 vue-i18

作者: _Bell_ | 来源:发表于2023-08-24 20:19 被阅读0次

    在 Vue 2 中配置国际化,您可以使用 Vue I18n 插件。Vue I18n 是 Vue.js 官方推荐的国际化插件,它可以帮助您轻松地实现多语言支持。

    安装 vue-i18n

    项目根目录下打开终端或命令行工具,运行以下命令来安装相关依赖包:

    npm install vue-i18n@8.27.1 --save
    

    使用

    1. 在 src/components 目录中新增一个名为 i18n 的目录,并添加以下3个文件:

    src/components/i18n/locales/en/index.js 英语语言包:

    export default {
      'Language': 'English'
    }
    

    src/components/i18n/locales/zh-CN/index.js 中文语言包:

    export default {
      'Language': '中文'
    }
    

    src/components/i18n/index.js 语言包入口文件:

    import Vue from 'vue'
    import VueI18n from 'vue-i18n'
    
    Vue.use(VueI18n)
    
    // 各个国家的key
    const localeKeys = ['en', 'zh-CN']
    
    // 各个国家语言包
    const messages = {}
    for (const key of localeKeys) {
      messages[key] = require(`./locales/${key}/index.js`).default
    }
    
    export default new VueI18n({
      locale: 'en',
      messages,
      silentTranslationWarn: true // 忽略翻译警告
    })
    
    2. 打开 src/main.js 文件,挂载到Vue实例:
    import i18n from './components/i18n'
    
    new Vue({
      i18n
    })
    

    修改后的代码如下:

    import Vue from 'vue'
    import Element from 'element-ui'
    import 'element-ui/lib/theme-chalk/index.css'
    
    import App from './App.vue'
    import router from './router'
    import store from './store'
    import i18n from './components/i18n'
    import '@css/index.less'
    
    // 禁用生产环境提示
    Vue.config.productionTip = false
    
    // Element挂载到Vue
    Vue.$message = Element.Message
    Vue.use(Element)
    
    new Vue({
      router,
      store,
      i18n,
      render: (h) => h(App)
    }).$mount('#app')
    
    3. 在页面、js代码中使用:

    一旦将 VueI18n 实例挂载到 Vue 实例上,在 Vue 组件中直接使用 $t 方法,可以通过指定键(key)来获取对应语言的翻译文本。这个键可以是简单的字符串,也可以是一个对象,用于支持更复杂的翻译需求。

    以下是使用 $t 方法获取翻译文本的示例:

    <template>
      <div>
        <p>{{ $t('Language') }}</p>
      </div>
    </template>
    
    <script>
    export default {
      mounted() {
        console.log(this.$t('Language'))
      }
    }
    </script>
    

    Element 语言包

    完成上述步骤后,我们在使用 Element 组件时可能会遇到一个问题:虽然我们成功切换了自定义的语言,在应用中自定义的文本已经被正确翻译,但是 Element UI 组件的显示内容并没有随之切换。

    这是因为 Element UI 组件库本身并不直接集成 vue-i18n 插件,因此它并不会自动根据我们设置的语言环境来翻译组件的显示文本。

    解决这个问题的办法是,我们需要手动为 Element UI 组件进行国际化配置和翻译。

    以下是解决方案的基本步骤:

    1. 创建 Element 语言包文件

    src/components/i18n 目录下添加一个名为 element 的目录,并在目录中添加以下两个文件:

    en.js

    // 英语
    exports.default = {
      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'], // to be translated
          filterPlaceholder: 'Enter keyword', // to be translated
          noCheckedFormat: '{total} items', // to be translated
          hasCheckedFormat: '{checked}/{total} checked' // to be translated
        },
        image: {
          error: 'FAILED'
        },
        pageHeader: {
          title: 'Back' // to be translated
        },
        popconfirm: {
          confirmButtonText: 'Yes',
          cancelButtonText: 'No'
        },
        empty: {
          description: 'No Data'
        }
      }
    }
    

    zh-CN.js

    // 中文
    exports.default = {
      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 月',
          // week: '周次',
          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: '暂无数据'
        }
      }
    }
    

    以上两个文件来源于 Element官方。在当前项目中,将Element语言包放到本地的目的是为了便于后期对语言包进行修改拓展。

    如果不想在本地创建这两个文件,也可以通过引入的方式直接获取到对应的文件,例如:import elementLang from 'element-ui/src/locale/lang/en'

    2. 在语言包入口文件引入 Element 语言包

    src/components/i18n/index.js 文件中找到以下代码块:

    // 各个国家语言包
    const messages = {}
    for (const key of localeKeys) {
      messages[key] = require(`./locales/${key}/index.js`).default
    }
    

    将其修改为:

    // 各个国家语言包
    const messages = {}
    for (const key of localeKeys) {
      const langObj = require(`./locales/${key}/index.js`).default
      const langElement = require(`./element/${key}`)
      messages[key] = {
        ...langObj,
        ...langElement ? langElement.default : {}
      }
    }
    

    修改后的代码如下:

    import Vue from 'vue'
    import VueI18n from 'vue-i18n'
    
    Vue.use(VueI18n)
    
    // 各个国家的key
    const localeKeys = ['en', 'zh-CN']
    
    // 各个国家语言包
    const messages = {}
    for (const key of localeKeys) {
      const langObj = require(`./locales/${key}/index.js`).default
      const langElement = require(`./element/${key}`)
      messages[key] = {
        ...langObj,
        ...langElement ? langElement.default : {}
      }
    }
    
    export default new VueI18n({
      locale: 'en',
      messages,
      silentTranslationWarn: true // 忽略翻译警告
    })
    

    通过这种方式,Element语言包与我们自定义的语言包便合并在一起了。

    3. 挂载 Element 到 Vue 实例中时将语言包注入

    src/main.js 中找到以下代码块

    Vue.use(Element)
    

    将其修改为:

    Vue.use(Element, {
      i18n: (key, value) => i18n.t(key, value)
    })
    

    修改后的代码如下:

    import Vue from 'vue'
    import Element from 'element-ui'
    import 'element-ui/lib/theme-chalk/index.css'
    
    import App from './App.vue'
    import router from './router'
    import store from './store'
    import i18n from './components/i18n'
    import '@css/index.less'
    
    // 禁用生产环境提示
    Vue.config.productionTip = false
    
    // Element挂载到Vue
    Vue.$message = Element.Message
    Vue.use(Element, {
      i18n: (key, value) => i18n.t(key, value)
    })
    
    new Vue({
      router,
      store,
      i18n,
      render: (h) => h(App)
    }).$mount('#app')
    

    完成后 Element 相关组件就能正常的显示翻译后的内容了。

    切换组件的封装

    在我们的UI组件中,我们使用了 Element 中的下拉菜单组件。如果想了解更多关于下拉菜单组件的用法,请访问 Dropdown下拉菜单文档地址 查看详细文档内容。

    新建组件文件

    src/components/i18n目录中,新建一个名为change-language.vue 的文件,并添加以下内容:

    <template>
      <el-dropdown @command="handle">
        <span class="el-dropdown-link">
          {{$t('Language')}}<i class="el-icon-caret-bottom el-icon--right"></i>
        </span>
        <el-dropdown-menu slot="dropdown">
          <el-dropdown-item v-for="(item, index) of list" :key="index" :command="item.key">{{item.name}}</el-dropdown-item>
        </el-dropdown-menu>
      </el-dropdown>
    </template>
    
    <script>
    export default {
      name: 'change-language',
      data() {
        return {
          list: [
            { key: 'en', name: 'English' }, // 英语
            { key: 'zh-CN', name: '中文' } // 中文
          ]
        }
      },
      methods: {
        handle(value) {
        }
      }
    }
    </script>
    <style scoped lang="less">
    </style>
    

    然后,我们可以找一个Vue文件,在其中使用这个组件进行观察。使用方式如下(以Vue单文件组件为例):

    • 引入组件
    import ChangeLanguage from '@/components/i18n/change-language'
    
    • 注册组件
    components: {
      ChangeLanguage
    }
    
    • 使用
    <ChangeLanguage />
    

    这样一个基本的语言包下拉单组件样式就完成了,之后还需要对组件进行功能上的开发。

    切换语言

    在组件handle函数代码中添加代码,如下:

    handle(value) {
      this.$i18n.locale = value
    }
    

    通过将value赋给this.$i18n.locale,我们可以动态地切换当前语言为下拉菜单中所选中的语言。

    现在虽然基本的切换功能已经完成,但是当我们在网页中切换语言包并刷新页面后,之前选择的语言包不会被保留,而是重新加载页面时返回到默认的语言包。这是因为在刷新页面时,浏览器会重新加载整个应用程序,并重置Vue实例的状态,包括设置的语言包。这时候我们就需要做切换数据的持久化处理,来保证我们切换后的内容显示不会出错。

    数据持久化

    数据持久化的方式有很多种,这里我们采用了浏览器的本地存储 LocalStorage 来实现。

    在组件handle函数代码中添加代码,如下:

    handle(value) {
      this.$i18n.locale = value
      localStorage.setItem('change-language', value)
    }
    

    找到 src/components/i18n/index.js 文件中以下代码块:

    export default new VueI18n({
      locale: 'en',
      messages,
      silentTranslationWarn: true // 忽略翻译警告
    })
    

    将其中 locale 写死的值 'en' 改为 localStorage 获取的值,如下:

    export default new VueI18n({
      locale: localStorage.getItem('change-language') || 'zh-CN',
      messages,
      silentTranslationWarn: true // 忽略翻译警告
    })
    

    上述代码中,语言包的key值会先从本地存储中获取,如果获取不到则设置为默认值'zh-CN'

    这样,我们切换语言包再刷新页面后,仍然可以正确地显示之前选择的语言包。

    后端语言包

    在一般情况下,仅仅使用前端的语言包是无法涵盖整个系统的翻译需求的。例如,后端接口返回的菜单、下拉列表的数据等。针对这种情况,我们可以采取一种方案,即在切换语言后调用location.reload()方法刷新页面以重新获取后端的翻译数据。

    使用这种方案时,我们需要与后端约定好国际化的翻译key的格式,并在语言切换时通过前端与后端接口进行数据交互。前端可以将当前选择的语言作为参数传递给后端,后端将返回相应语言的翻译数据。然后在前端刷新页面时,通过重新加载页面来获取更新后的翻译内容。

    请注意,使用location.reload()方法会导致整个页面重新加载,这可能会对用户的体验产生一定影响,特别是在数据量较大或网络较慢的情况下。因此,在实施这种方案之前,请确保综合考虑用户体验和性能方面的因素。

    总结来说,为了在整个系统中实现翻译需求,我们可以通过与后端接口协作,传递语言选择,并在需要时重新加载页面来获取最新的翻译内容。这样就能够在前后端协同工作下,正确显示经过翻译的内容。

    在组件handle函数中添加 location.reload()

    handle(value) {
      this.$i18n.locale = value
      localStorage.setItem('change-language', value)
      location.reload()
    }
    
    小小的优化一下

    上述步骤完成,我们的组件整体功能就已经开发完毕了,但是代码中有一部部分内容可以复用简化。

    组件代码中的以下部分:

    list: [
      { key: 'en', name: 'English' }, // 英语
      { key: 'zh-CN', name: '中文' } // 中文
    ]
    

    src/components/i18n/index.js 代码中的以下部分:

    // 各个国家的key
    const localeKeys = ['en', 'zh-CN']
    

    以上两部分代码对比,就能够发现他们有两个相同点:都是数组、内容中都有一样的值。

    这样我们就能将组件代码中的内容移动到 src/components/i18n/index.js 文件中,避免后期维护需要新增国家时,还要同时维护两个文件的列表了。

    根据以下步骤进行优化:

    1. 找到 src/components/i18n/index.js 文件中以下内容:
    // 各个国家的key
    const localeKeys = ['en', 'zh-CN']
    
    // 各个国家语言包
    const messages = {}
    for (const key of localeKeys) {
      const langObj = require(`./locales/${key}/index.js`).default
      const langElement = require(`./element/${key}`)
      messages[key] = {
        ...langObj,
        ...langElement ? langElement.default : {}
      }
    }
    
    1. 将其修改为:
    // 各个国家的key
    export const localeKeys = [
      { key: 'en', name: 'English' }, // 英语
      { key: 'zh-CN', name: '中文' } // 中文
    ]
    
    // 各个国家语言包
    const messages = {}
    for (const item of localeKeys) {
      const key = item.key
      const langObj = require(`./locales/${key}/index.js`).default
      const langElement = require(`./element/${key}`)
      messages[key] = {
        ...langObj,
        ...langElement ? langElement.default : {}
      }
    }
    
    1. 在组件中引入 src/components/i18n/index.js 文件中的 localeKeys
    import { localeKeys } from './index'
    
    1. localeKeys赋值给 list
    return {
      list: localeKeys
    }
    

    这样,这个小小的优化就完成了。

    完整代码

    src/components/i18n/change-language.vue 组件代码:

    <!--
    @Descripttion 国际化语言切换
    @version 1.0.0
    @Author Bell
    @ 使用
      引入组件
        import ChangeLanguage from '@/components/i18n/change-language'
      注册组件
        components: {
          ChangeLanguage
        }
      使用
        <ChangeLanguage />
     -->
    <template>
      <el-dropdown @command="handle">
        <span class="el-dropdown-link">
          {{$t('Language')}}<i class="el-icon-caret-bottom el-icon--right"></i>
        </span>
        <el-dropdown-menu slot="dropdown">
          <el-dropdown-item v-for="(item, index) of list" :key="index" :command="item.key">{{item.name}}</el-dropdown-item>
        </el-dropdown-menu>
      </el-dropdown>
    </template>
    
    <script>
    import { localeKeys } from './index'
    
    export default {
      name: 'change-language',
      data() {
        return {
          list: localeKeys
        }
      },
      methods: {
        handle(value) {
          this.$i18n.locale = value
          localStorage.setItem('change-language', value)
          location.reload()
        }
      }
    }
    </script>
    <style scoped lang="less">
    </style>
    

    src/components/i18n/index.js 国际化入口文件:

    import Vue from 'vue'
    import VueI18n from 'vue-i18n'
    
    Vue.use(VueI18n)
    
    // 各个国家的key
    export const localeKeys = [
      { key: 'en', name: 'English' }, // 英语
      { key: 'zh-CN', name: '中文' } // 中文
    ]
    
    // 各个国家语言包
    const messages = {}
    for (const item of localeKeys) {
      const key = item.key
      const langObj = require(`./locales/${key}/index.js`).default
      const langElement = require(`./element/${key}`)
      messages[key] = {
        ...langObj,
        ...langElement ? langElement.default : {}
      }
    }
    
    export default new VueI18n({
      locale: localStorage.getItem('change-language') || 'zh-CN',
      messages,
      silentTranslationWarn: true // 忽略翻译警告
    })
    


    框架搭建整体流程

    点击下载步骤 1-7 配置完成的完整 Demo


    本框架更多功能 Demo 下载

    相关文章

      网友评论

          本文标题:Vue2 element-ui 框架中集成国际化 vue-i18

          本文链接:https://www.haomeiwen.com/subject/vnwkmdtx.html