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}],
}))