【Vue3.x】整合vue-i18n

363 阅读2分钟

1. Technology

Vue3.x + Vite + Pinia + Router + ElementPlux +I18n + axios + TypeScript

  "dependencies": {
    "@element-plus/icons-vue": "^2.3.1",
    "axios": "^1.7.0",
    "element-plus": "^2.7.3",
    "moment": "^2.24.0",
    "pinia": "^2.1.7",
    "pinia-plugin-persist": "^1.0.0",
    "pinia-plugin-persistedstate": "^3.2.1",
    "vue": "^3.4.21",
    "vue-i18n": "^9.5.0",
    "vue-router": "^4.3.0"
  },

2. Install

vue-i18n.intlify.dev/guide/essen…

npm install vue-i18n@9

3. Create File

3.1 Constructure

vue-demo
├── public/          # static files
│   └── favicon.ico  # title icon (可以多個,切換路由titile也切換) 
├── src/             # project root
│   ├── assets/      # images, icons, etc.靜態資源
│   ├── components/  # common components - 客製化頁面
│   ├── layouts/     # layout containers -header, footer,sidebar, etc.
│   ├── scss/        # scss styles
│   ├── config/      # 封裝http
│   ├── i18n/        # 多語言切換
│   │   ├── locales/        # 多語言切換
│   │   │   ├── en.ts         # 中文
│   │   │   ├── zh.ts         # 英文
│   │   │   └── xxx.ts        # 其他文
│   │   └── index.ts        # I18N切換
│   ├── router/       # routes config
│   ├── stores/      # template state example 
│   ├── views/pages  # Route apge,路由導航的頁面
│   ├── App.vue      # 三個標籤 template /script  /style
│   └── main.ts      # 加載所有組件,掛載都index.html
│
├── .env.devlopment  # 多環境配置,在package.json啟動腳本中設定讀取
├── .env.production  # 多環境配置,在package.json啟動腳本中設定讀取
├── env.d.ts         # vue識別所有格式的文件
├── index.html       # 入口文件,main.ts
├── package.json     # dependency
└── vite.config.ts   # project config,install plugin,proxy

3.2 en.ts

export default {
    username: 'Username12313',
    email: 'Email',
    mobile: 'Mobile'
}

3.3 zh.ts

export default {
    username: '用户名123',
    email: '邮箱',
    mobile: '手机'
}

3.4 index.ts

import { createI18n } from 'vue-i18n'
import enUS from './locales/en'
import zhCN from './locales/zh'

type MessageSchema = typeof enUS

const i18n = createI18n<[MessageSchema], 'en-US' | 'zh-CN'>({

    globalInjection: true,
    // 默认语言先取自定义的,再取浏览器的,最后默认英文
    locale: localStorage.getItem("language") || navigator.language || 'en-US',
    legacy: false,
    // allowComposition: true, // 是否允许在 Legacy API 模式下使用 Composition API
    messages: {
        'en-US': enUS,
        'zh-CN': zhCN // 可以追加其他语言
    }
})

export default i18n

3.5 main.ts

全局引入I18n

import './assets/main.css'

import { createApp } from 'vue'
import { createPinia, storeToRefs } from 'pinia'
import { createPersistedState } from 'pinia-plugin-persistedstate';
import ElementPlus from 'element-plus';
import 'element-plus/theme-chalk/index.css';
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import './assets/iconfont/iconfont.css'

import App from './App.vue'
import router from './router/index'
import i18n from './i18n';

const app = createApp(App)
// 应用插件

const pinia = createPinia()
pinia.use(
   createPersistedState({
      auto: true
   })
)
app.use(pinia)
   .use(router)
   .use(ElementPlus)
   .use(i18n)

for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
   app.component(key, component)
   }
app.mount('#app')

4. Use

可以在登錄頁,和Layout的header裡面進行語言切換

<template>
  <el-menu
    :default-active="activeIndex"
    class="el-menu-demo"
    mode="horizontal"
    :ellipsis="false"
    @select="handleSelect"
  >
    <el-menu-item index="0" class="left-first">
        <i class="iconfont icon-menu" />
    </el-menu-item>
    <el-menu-item index="1" class="left-second">
        <h2>DEMO2.0</h2>
    </el-menu-item>
    <div class="flex-grow" />
    <el-sub-menu index="2">
      <template #title><h2>Language</h2></template>
      <el-menu-item index="en-US">English</el-menu-item>
      <el-menu-item index="zh-CN">China</el-menu-item>
    </el-sub-menu>
  </el-menu>
</template>

<script lang="ts" setup>
import { ref } from 'vue'
import {
  ArrowDown,
  SwitchButton,
  Menu,
  ZoomIn,
  Check,
  CircleCheck,
  CirclePlus,
  CirclePlusFilled,
  Plus,
} from '@element-plus/icons-vue'
import i18n from '@/i18n';
import { getCurrentInstance } from 'vue';

  //const { appContext : { config: { globalProperties } } } = getCurrentInstance();
  const $this = getCurrentInstance()?.appContext.config.globalProperties as any;

  const activeIndex = ref('1')
  const handleSelect = (key: string, keyPath: string[]) => {
    console.log(keyPath);
    var language = keyPath[1];
    console.log(language)
    switch(language){
      case "zh-CN":
        $this.$i18n.locale = "zh-CN";
        localStorage.setItem('language',"zh-CN");
        break;
      case "en-US":
        $this.$i18n.locale = "en-US";
        localStorage.setItem('language',"en-US");
        break;
      default:
        break;
    }

  }

</script>

<style>
.flex-grow {
  flex-grow: 1;
}
.left-first{
  width: 60px
}
.left-second{
  width: 210px
}
img {
  position: relative;
  left: 5px;
  width: 110px;
  height: 30px;
}
h2 {
  position: relative;
  top: 6px;
  padding-left: 15px;
  font-weight: bold;
}
el-avatar{
  position: relative;
  top: 16px;
}
</style>

5. Practice

在template中使用

 <span>{{$t('key')}}</span>
 
 <el-tab-pane :label="$t('key')" name="Attribute"></el-tab-pane>

在script中使用

const i18n = createI18n({
  locale: 'en',
  messages: {
    zh: Chinese,
    en: English,
  },
})
export default i18n


const { t } = i18n.global;
ElMessage.success(t(key));

如果在javascript、typescript中沒有熱更新,就用computed,非常Nice

const pleaseEnterYourAccount = computed(() => t('login.pleaseEnterYourAccount'))
const pleaseEnterYourPassword = computed(() => t('login.pleaseEnterYourPassword'))
const pleaseEnterVerificationCode = computed(() => t('login.pleaseEnterVerificationCode'))
const loginRules = computed(() => ({
  username: [{ required: true, trigger: "blur", message: pleaseEnterYourAccount}],
  password: [{ required: true, trigger: "blur", message: pleaseEnterYourPassword}],
  code: [{ required: true, trigger: "change", message: pleaseEnterVerificationCode}],
}))