多语言
This commit is contained in:
parent
2441594f1c
commit
935d764bb5
|
|
@ -7,7 +7,7 @@
|
|||
<script setup>
|
||||
import {useI18n} from 'vue-i18n'
|
||||
import {computed, ref} from 'vue'
|
||||
import tools from "@/utils/tools.js";
|
||||
import tools from "@/utils/tools"
|
||||
import colorTool from '@/utils/color'
|
||||
|
||||
const {locale, messages} = useI18n()
|
||||
|
|
|
|||
|
|
@ -231,5 +231,8 @@ export default {
|
|||
del: async function (data = {}) {
|
||||
return await http.delete("translation/del", data);
|
||||
},
|
||||
load: async function (data = {}) {
|
||||
return await http.get("translations", data);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,27 @@
|
|||
<template>
|
||||
<el-form ref="form" label-width="120px" label-position="left" style="padding:0 20px;">
|
||||
<el-divider>{{t('user.thememode')}}</el-divider>
|
||||
<el-divider>{{ t('user.thememode') }}</el-divider>
|
||||
<el-row class="pi-theme" :gutter="20">
|
||||
<el-col :span="8"><img src="/images/light.png" class="pi-pic" :class="{'active': dark == 'light'}" @click="themeClick('light')"/><el-text type="info" class="pi-text">浅色</el-text></el-col>
|
||||
<el-col :span="8"><img src="/images/dark.png" class="pi-pic" :class="{'active': dark == 'dark'}" @click="themeClick('dark')"/><el-text type="info" class="pi-text">深色</el-text></el-col>
|
||||
<el-col :span="8"><img src="/images/follow.png" class="pi-pic" :class="{'active': dark == 'follow'}" @click="themeClick('follow')"/><el-text type="info" class="pi-text">跟随系统</el-text></el-col>
|
||||
<el-col :span="8"><img src="/images/light.png" class="pi-pic" :class="{'active': dark == 'light'}"
|
||||
@click="themeClick('light')"/>
|
||||
<el-text type="info" class="pi-text">浅色</el-text>
|
||||
</el-col>
|
||||
<el-col :span="8"><img src="/images/dark.png" class="pi-pic" :class="{'active': dark == 'dark'}"
|
||||
@click="themeClick('dark')"/>
|
||||
<el-text type="info" class="pi-text">深色</el-text>
|
||||
</el-col>
|
||||
<el-col :span="8"><img src="/images/follow.png" class="pi-pic" :class="{'active': dark == 'follow'}"
|
||||
@click="themeClick('follow')"/>
|
||||
<el-text type="info" class="pi-text">跟随系统</el-text>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-divider></el-divider>
|
||||
<el-form-item :label="t('user.language')">
|
||||
<el-select v-model="lang">
|
||||
<el-option label="简体中文" value="zh-cn"></el-option>
|
||||
<el-option label="English" value="en"></el-option>
|
||||
<el-option label="日本語" value="ja"></el-option>
|
||||
<el-option label="Tiếng Việt" value="vi"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-divider></el-divider>
|
||||
|
|
@ -46,9 +57,10 @@ import tools from "@/utils/tools";
|
|||
import config from "@/config";
|
||||
import globalStore from "@/store/global.js";
|
||||
import {useI18n} from "vue-i18n";
|
||||
import {setupI18n} from "@/locales/setup.js";
|
||||
|
||||
const global = globalStore()
|
||||
const {t, locale} = useI18n()
|
||||
const {t} = useI18n()
|
||||
|
||||
let layout = ref(tools.data.get('APP_LAYOUT') || global.layout);
|
||||
let menuIsCollapse = ref(global.menuIsCollapse);
|
||||
|
|
@ -83,8 +95,8 @@ watch(weakMode, (val) => {
|
|||
})
|
||||
|
||||
watch(lang, (val) => {
|
||||
locale.value = val
|
||||
tools.data.set("APP_LANG", val);
|
||||
setupI18n(val)
|
||||
tools.data.set("APP_LANG", val)
|
||||
})
|
||||
|
||||
watch(colorPrimary, (val) => {
|
||||
|
|
@ -106,14 +118,14 @@ function themeClick(val) {
|
|||
if (val == 'dark') {
|
||||
document.documentElement.classList.add("dark")
|
||||
localStorage.setItem("APP_DARK", val)
|
||||
} else if(val == 'light') {
|
||||
} else if (val == 'light') {
|
||||
document.documentElement.classList.remove("dark")
|
||||
localStorage.setItem("APP_DARK", val)
|
||||
} else {
|
||||
const systemTheme = window.matchMedia('(prefers-color-scheme: dark)')
|
||||
if (systemTheme.matches) {
|
||||
document.documentElement.classList.add("dark")
|
||||
}else {
|
||||
} else {
|
||||
document.documentElement.classList.remove("dark")
|
||||
}
|
||||
localStorage.setItem("APP_DARK", val)
|
||||
|
|
@ -123,8 +135,25 @@ function themeClick(val) {
|
|||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.pi-theme {text-align: center;}
|
||||
.pi-theme .pi-pic {cursor: pointer;margin-bottom: 6px;border-radius: 8px; box-shadow: 0 2px 8px #0003;width: 100px;}
|
||||
.pi-theme .pi-pic.active {border: 2px solid var(--el-color-primary); cursor: pointer;}
|
||||
.pi-theme .pi-text {margin-top: 6px; cursor: pointer;}
|
||||
.pi-theme {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.pi-theme .pi-pic {
|
||||
cursor: pointer;
|
||||
margin-bottom: 6px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px #0003;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.pi-theme .pi-pic.active {
|
||||
border: 2px solid var(--el-color-primary);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.pi-theme .pi-text {
|
||||
margin-top: 6px;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,29 +1,12 @@
|
|||
import { createI18n } from 'vue-i18n'
|
||||
import el_zh_cn from 'element-plus/dist/locale/zh-cn'
|
||||
import el_en from 'element-plus/dist/locale/en'
|
||||
|
||||
import {createI18n} from 'vue-i18n'
|
||||
import config from "@/config"
|
||||
import tools from '@/utils/tools'
|
||||
import zh_cn from '@/locales/lang/zh-cn'
|
||||
import en from '@/locales/lang/en'
|
||||
|
||||
const messages = {
|
||||
'zh-cn': {
|
||||
...el_zh_cn,
|
||||
...zh_cn
|
||||
},
|
||||
'en': {
|
||||
...el_en,
|
||||
...en
|
||||
}
|
||||
}
|
||||
|
||||
const i18n = createI18n({
|
||||
legacy: false,
|
||||
fallbackLocale: 'zh-cn',
|
||||
locale: tools.data.get("APP_LANG") || config.LANG,
|
||||
globalInjection: true,
|
||||
messages,
|
||||
legacy: false,
|
||||
fallbackLocale: config.LANG,
|
||||
locale: config.LANG,
|
||||
globalInjection: true,
|
||||
messages: {}
|
||||
})
|
||||
|
||||
export default i18n;
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
export default {}
|
||||
|
|
@ -0,0 +1 @@
|
|||
export default {}
|
||||
|
|
@ -0,0 +1 @@
|
|||
export default {}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
import el_zh_cn from "element-plus/dist/locale/zh-cn";
|
||||
import el_en from "element-plus/dist/locale/en";
|
||||
import el_vi from "element-plus/dist/locale/vi";
|
||||
import el_ms from "element-plus/dist/locale/ms";
|
||||
import el_ja from "element-plus/dist/locale/ja";
|
||||
|
||||
import lang_zh_cn from "@/locales/lang/zh-cn";
|
||||
import lang_en from "@/locales/lang/en";
|
||||
import lang_ja from "@/locales/lang/ja";
|
||||
import lang_vi from "@/locales/lang/vi";
|
||||
import lang_ms from "@/locales/lang/ms";
|
||||
|
||||
export const LANGUAGE_MAP: Record<
|
||||
string,
|
||||
{ el: any; local: any }
|
||||
> = {
|
||||
"zh-cn": {el: el_zh_cn, local: lang_zh_cn},
|
||||
en: {el: el_en, local: lang_en},
|
||||
ja: {el: el_ja, local: lang_ja},
|
||||
vi: {el: el_vi, local: lang_vi},
|
||||
ms: {el: el_ms, local: lang_ms},
|
||||
};
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
import i18n from "@/locales/index";
|
||||
import config from "@/config";
|
||||
import tools from "@/utils/tools";
|
||||
import api from "@/api";
|
||||
import {LANGUAGE_MAP} from "@/locales/languages";
|
||||
|
||||
export async function setupI18n(locale: string = null) {
|
||||
locale = locale || tools.data.get("APP_LANG") || config.LANG;
|
||||
const langConfig = LANGUAGE_MAP[locale];
|
||||
// 先从缓存中取
|
||||
var messages = tools.data.get("LOCALE:" + locale)
|
||||
if (!messages) {
|
||||
const res = await api.system.translation.load({locale});
|
||||
messages = tools.deepMerge(langConfig.el, langConfig.local, res['data'] || {})
|
||||
tools.data.set("LOCALE:" + locale, messages, 86400)
|
||||
}
|
||||
// 设置语言包内容
|
||||
i18n.global.setLocaleMessage(locale, messages);
|
||||
// 切换语言
|
||||
i18n.global.locale.value = locale;
|
||||
return i18n;
|
||||
}
|
||||
28
src/main.ts
28
src/main.ts
|
|
@ -1,21 +1,29 @@
|
|||
import { createApp } from 'vue'
|
||||
import {createApp} from 'vue'
|
||||
import ElementPlus from 'element-plus'
|
||||
import 'element-plus/dist/index.css'
|
||||
import 'element-plus/theme-chalk/display.css'
|
||||
import i18n from './locales'
|
||||
import {setupI18n} from "@/locales/setup"
|
||||
import App from './App.vue'
|
||||
|
||||
import pinia from './store'
|
||||
import router from './router'
|
||||
import pi from './pi'
|
||||
|
||||
const app = createApp(App);
|
||||
async function bootstrap() {
|
||||
await setupI18n();
|
||||
const app = createApp(App);
|
||||
|
||||
app.use(pinia);
|
||||
app.use(router);
|
||||
app.use(ElementPlus);
|
||||
app.use(i18n);
|
||||
app.use(pi);
|
||||
|
||||
//挂载app
|
||||
app.mount('#app');
|
||||
}
|
||||
|
||||
bootstrap()
|
||||
|
||||
|
||||
app.use(pinia);
|
||||
app.use(router);
|
||||
app.use(ElementPlus);
|
||||
app.use(i18n);
|
||||
app.use(pi);
|
||||
|
||||
//挂载app
|
||||
app.mount('#app');
|
||||
|
|
|
|||
|
|
@ -19,6 +19,9 @@ axios.interceptors.request.use((config) => {
|
|||
config.params = config.params || {};
|
||||
config.params['_'] = new Date().getTime();
|
||||
}
|
||||
// 多语言
|
||||
const lang = tools.data.get("APP_LANG") || 'zh-cn'
|
||||
config.headers['Accept-Language'] = lang
|
||||
return config;
|
||||
})
|
||||
//响应拦截
|
||||
|
|
|
|||
|
|
@ -214,6 +214,28 @@ const tools = {
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
// 深度合并数据
|
||||
deepMerge: (...objects: any[]) => {
|
||||
const result: any = {}
|
||||
for (const obj of objects) {
|
||||
tools.mergeObject(result, obj)
|
||||
}
|
||||
return result
|
||||
},
|
||||
mergeObject: (target: any, source: any) => {
|
||||
if (!source || typeof source !== 'object') return
|
||||
for (const key in source) {
|
||||
const value = source[key]
|
||||
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
||||
if (!target[key] || typeof target[key] !== 'object') {
|
||||
target[key] = {}
|
||||
}
|
||||
tools.mergeObject(target[key], value)
|
||||
} else {
|
||||
target[key] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
export default tools
|
||||
|
|
|
|||
|
|
@ -71,6 +71,7 @@
|
|||
<script setup>
|
||||
import {getCurrentInstance, ref, onMounted, watch} from 'vue'
|
||||
import {useI18n} from 'vue-i18n'
|
||||
import {setupI18n} from "@/locales/setup"
|
||||
import {useRouter, useRoute} from 'vue-router'
|
||||
import tools from "@/utils/tools";
|
||||
import sysConfig from "@/config/index"
|
||||
|
|
@ -81,7 +82,7 @@ defineOptions({
|
|||
})
|
||||
|
||||
const {proxy} = getCurrentInstance()
|
||||
const {t, locale} = useI18n()
|
||||
const {t} = useI18n()
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
import bg from '@/assets/images/bg.jpg'
|
||||
|
|
@ -98,6 +99,14 @@ const langs = ref([
|
|||
{
|
||||
name: 'English',
|
||||
value: 'en',
|
||||
},
|
||||
{
|
||||
name: '日本語',
|
||||
value: 'ja',
|
||||
},
|
||||
{
|
||||
name: 'Tiếng Việt',
|
||||
value: 'vi',
|
||||
}
|
||||
])
|
||||
|
||||
|
|
@ -140,7 +149,7 @@ onMounted(() => {
|
|||
})
|
||||
|
||||
watch(lang, (val) => {
|
||||
locale.value = val
|
||||
setupI18n(val)
|
||||
tools.data.set("APP_LANG", val)
|
||||
})
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue