feat: i18n

This commit is contained in:
wangdan-fit2cloud 2025-01-20 18:00:53 +08:00
parent 7ea082427b
commit c4de677c86
17 changed files with 136 additions and 92 deletions

View File

@ -25,6 +25,7 @@ interface User {
is_edit_password?: boolean is_edit_password?: boolean
IS_XPACK?: boolean IS_XPACK?: boolean
XPACK_LICENSE_IS_VALID?: boolean XPACK_LICENSE_IS_VALID?: boolean
language: string
} }
interface LoginRequest { interface LoginRequest {

View File

@ -184,10 +184,10 @@ import { useRoute, useRouter } from 'vue-router'
import { getImgUrl } from '@/utils/utils' import { getImgUrl } from '@/utils/utils'
import bus from '@/bus' import bus from '@/bus'
import 'recorder-core/src/engine/mp3' import 'recorder-core/src/engine/mp3'
import 'recorder-core/src/engine/mp3-engine' import 'recorder-core/src/engine/mp3-engine'
import { MsgWarning } from '@/utils/message' import { MsgWarning } from '@/utils/message'
import useStore from '@/stores' import useStore from '@/stores'
import { t } from '@/locales'
const router = useRouter() const router = useRouter()
const route = useRoute() const route = useRoute()
const { application } = useStore() const { application } = useStore()

View File

@ -16,7 +16,7 @@ import bus from '@/bus'
import { ref, nextTick, onMounted } from 'vue' import { ref, nextTick, onMounted } from 'vue'
import { t } from '@/locales' import { t } from '@/locales'
const isOpen = ref<boolean>(false) const isOpen = ref<boolean>(false)
const eventVal = ref({}) const eventVal = ref()
function getSelection() { function getSelection() {
const selection = window.getSelection() const selection = window.getSelection()
if (selection && selection.anchorNode == null) { if (selection && selection.anchorNode == null) {

View File

@ -45,6 +45,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, nextTick, watch, computed, onMounted } from 'vue' import { ref, nextTick, watch, computed, onMounted } from 'vue'
import { MsgError } from '@/utils/message' import { MsgError } from '@/utils/message'
import { t } from '@/locales'
defineOptions({ name: 'AppTable' }) defineOptions({ name: 'AppTable' })
import useStore from '@/stores' import useStore from '@/stores'
@ -102,7 +103,7 @@ function submitHandle() {
loading.value = false loading.value = false
}, 200) }, 200)
} else { } else {
MsgError(`${props.quickCreateName}不能为空!`) MsgError(`${props.quickCreateName}${t('components.dynamicsForm.tip.requiredMessage')}`)
} }
} }

View File

@ -2,7 +2,7 @@
<el-form-item> <el-form-item>
<template #label> <template #label>
<div class="flex-between"> <div class="flex-between">
选项值 {{ $t('components.dynamicsForm.Select.label') }}
<el-button link type="primary" @click.stop="addOption()"> <el-button link type="primary" @click.stop="addOption()">
<el-icon class="mr-4"> <el-icon class="mr-4">
<Plus /> <Plus />
@ -12,13 +12,13 @@
</div> </div>
</template> </template>
<el-row style="width: 100%" :gutter="10"> <el-row style="width: 100%" :gutter="10">
<el-col :span="10" <el-col :span="10">
><div class="grid-content ep-bg-purple" /> <div class="grid-content ep-bg-purple" />
标签</el-col {{ $t('components.dynamicsForm.tag.label') }}
> </el-col>
<el-col :span="12" <el-col :span="12"
><div class="grid-content ep-bg-purple" /> ><div class="grid-content ep-bg-purple" />
选项值</el-col {{ $t('components.dynamicsForm.Select.label') }}</el-col
> >
</el-row> </el-row>
<el-row <el-row
@ -30,11 +30,13 @@
> >
<el-col :span="10" <el-col :span="10"
><div class="grid-content ep-bg-purple" /> ><div class="grid-content ep-bg-purple" />
<el-input v-model="formValue.option_list[$index].label" placeholder="请输入选项标签" <el-input v-model="formValue.option_list[$index].label" :placeholder="$t('components.dynamicsForm.tag.placeholder')"
/></el-col> /></el-col>
<el-col :span="12" <el-col :span="12"
><div class="grid-content ep-bg-purple" /> ><div class="grid-content ep-bg-purple" />
<el-input v-model="formValue.option_list[$index].value" placeholder="请输入选项值" <el-input
v-model="formValue.option_list[$index].value"
:placeholder="$t('components.dynamicsForm.Select.label')"
/></el-col> /></el-col>
<el-col :span="1" <el-col :span="1"
><div class="grid-content ep-bg-purple" /> ><div class="grid-content ep-bg-purple" />
@ -50,7 +52,11 @@
:label="$t('components.dynamicsForm.default.label')" :label="$t('components.dynamicsForm.default.label')"
:required="formValue.required" :required="formValue.required"
prop="default_value" prop="default_value"
:rules="formValue.required ? [{ required: true, message: $t('components.dynamicsForm.default.requiredMessage') }] : []" :rules="
formValue.required
? [{ required: true, message: $t('components.dynamicsForm.default.requiredMessage') }]
: []
"
> >
<div class="defaultValueCheckbox"> <div class="defaultValueCheckbox">
<el-checkbox <el-checkbox

View File

@ -2,7 +2,7 @@
<el-form-item> <el-form-item>
<template #label> <template #label>
<div class="flex-between"> <div class="flex-between">
选项值 {{ $t('components.dynamicsForm.Select.label') }}
<el-button link type="primary" @click.stop="addOption()"> <el-button link type="primary" @click.stop="addOption()">
<el-icon class="mr-4"> <el-icon class="mr-4">
<Plus /> <Plus />
@ -13,13 +13,13 @@
</template> </template>
<el-row style="width: 100%" :gutter="10"> <el-row style="width: 100%" :gutter="10">
<el-col :span="10" <el-col :span="10">
><div class="grid-content ep-bg-purple" /> <div class="grid-content ep-bg-purple" />
标签</el-col {{ $t('components.dynamicsForm.tag.label') }}
> </el-col>
<el-col :span="12" <el-col :span="12"
><div class="grid-content ep-bg-purple" /> ><div class="grid-content ep-bg-purple" />
选项值</el-col {{ $t('components.dynamicsForm.Select.label') }}</el-col
> >
</el-row> </el-row>
<el-row <el-row
@ -31,11 +31,13 @@
> >
<el-col :span="10" <el-col :span="10"
><div class="grid-content ep-bg-purple" /> ><div class="grid-content ep-bg-purple" />
<el-input v-model="formValue.option_list[$index].label" placeholder="请输入选项标签" <el-input v-model="formValue.option_list[$index].label" :placeholder="$t('components.dynamicsForm.tag.placeholder')"
/></el-col> /></el-col>
<el-col :span="12" <el-col :span="12"
><div class="grid-content ep-bg-purple" /> ><div class="grid-content ep-bg-purple" />
<el-input v-model="formValue.option_list[$index].value" placeholder="请输入选项值" <el-input
v-model="formValue.option_list[$index].value"
:placeholder="$t('components.dynamicsForm.Select.label')"
/></el-col> /></el-col>
<el-col :span="1" <el-col :span="1"
><div class="grid-content ep-bg-purple" /> ><div class="grid-content ep-bg-purple" />

View File

@ -2,7 +2,7 @@
<el-form-item> <el-form-item>
<template #label> <template #label>
<div class="flex-between"> <div class="flex-between">
选项值 {{ $t('components.dynamicsForm.Select.label') }}
<el-button link type="primary" @click.stop="addOption()"> <el-button link type="primary" @click.stop="addOption()">
<el-icon class="mr-4"> <el-icon class="mr-4">
<Plus /> <Plus />
@ -13,14 +13,14 @@
</template> </template>
<el-row style="width: 100%" :gutter="10"> <el-row style="width: 100%" :gutter="10">
<el-col :span="10" <el-col :span="10">
><div class="grid-content ep-bg-purple" /> <div class="grid-content ep-bg-purple" />
标签</el-col {{ $t('components.dynamicsForm.tag.label') }}
> </el-col>
<el-col :span="12" <el-col :span="12">
><div class="grid-content ep-bg-purple" /> <div class="grid-content ep-bg-purple" />
选项值</el-col {{ $t('components.dynamicsForm.Select.label') }}
> </el-col>
</el-row> </el-row>
<el-row <el-row
style="width: 100%" style="width: 100%"
@ -31,11 +31,15 @@
> >
<el-col :span="10" <el-col :span="10"
><div class="grid-content ep-bg-purple" /> ><div class="grid-content ep-bg-purple" />
<el-input v-model="formValue.option_list[$index].label" placeholder="请输入选项标签" <el-input
v-model="formValue.option_list[$index].label"
:placeholder="$t('components.dynamicsForm.tag.placeholder')"
/></el-col> /></el-col>
<el-col :span="12" <el-col :span="12"
><div class="grid-content ep-bg-purple" /> ><div class="grid-content ep-bg-purple" />
<el-input v-model="formValue.option_list[$index].value" placeholder="请输入选项值" <el-input
v-model="formValue.option_list[$index].value"
:placeholder="$t('components.dynamicsForm.Select.label')"
/></el-col> /></el-col>
<el-col :span="1" <el-col :span="1"
><div class="grid-content ep-bg-purple" /> ><div class="grid-content ep-bg-purple" />

View File

@ -2,7 +2,7 @@
<el-form-item> <el-form-item>
<template #label> <template #label>
<div class="flex-between"> <div class="flex-between">
选项值 {{ $t('components.dynamicsForm.Select.label') }}
<el-button link type="primary" @click.stop="addOption()"> <el-button link type="primary" @click.stop="addOption()">
<el-icon class="mr-4"> <el-icon class="mr-4">
<Plus /> <Plus />
@ -15,12 +15,12 @@
<el-row style="width: 100%" :gutter="10"> <el-row style="width: 100%" :gutter="10">
<el-col :span="10" <el-col :span="10"
><div class="grid-content ep-bg-purple" /> ><div class="grid-content ep-bg-purple" />
标签</el-col {{ $t('components.dynamicsForm.tag.label') }}</el-col
>
<el-col :span="12"
><div class="grid-content ep-bg-purple" />
选项值</el-col
> >
<el-col :span="12">
<div class="grid-content ep-bg-purple" />
{{ $t('components.dynamicsForm.Select.label') }}
</el-col>
</el-row> </el-row>
<el-row <el-row
style="width: 100%" style="width: 100%"
@ -31,11 +31,13 @@
> >
<el-col :span="10" <el-col :span="10"
><div class="grid-content ep-bg-purple" /> ><div class="grid-content ep-bg-purple" />
<el-input v-model="formValue.option_list[$index].label" placeholder="请输入选项标签" <el-input v-model="formValue.option_list[$index].label" :placeholder="$t('components.dynamicsForm.tag.placeholder')"
/></el-col> /></el-col>
<el-col :span="12" <el-col :span="12"
><div class="grid-content ep-bg-purple" /> ><div class="grid-content ep-bg-purple" />
<el-input v-model="formValue.option_list[$index].value" placeholder="请输入选项值" <el-input
v-model="formValue.option_list[$index].value"
:placeholder="$t('components.dynamicsForm.Select.label')"
/></el-col> /></el-col>
<el-col :span="1" <el-col :span="1"
><div class="grid-content ep-bg-purple" /> ><div class="grid-content ep-bg-purple" />

View File

@ -73,7 +73,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed, onBeforeMount, watch } from 'vue' import { computed, onBeforeMount, watch } from 'vue'
import { t } from '@/locales'
const props = defineProps<{ const props = defineProps<{
modelValue: any modelValue: any
}>() }>()
@ -101,7 +101,7 @@ const getData = () => {
props_info: { props_info: {
rules: [ rules: [
{ {
message: formValue.value.label + '不能为空', message: formValue.value.label + t('components.dynamicsForm.tip.requiredMessage'),
trigger: 'blur', trigger: 'blur',
required: formValue.value.required required: formValue.value.required
} }

View File

@ -1,66 +1,71 @@
import { useLocalStorage, usePreferredLanguages } from '@vueuse/core'; import { useLocalStorage, usePreferredLanguages } from '@vueuse/core'
import { computed } from 'vue'; import { computed } from 'vue'
import { createI18n } from 'vue-i18n';
import { createI18n } from 'vue-i18n'
import useStore from '@/stores'
const { user } = useStore()
// 导入语言文件 // 导入语言文件
const langModules = import.meta.glob('./lang/*/index.ts', { eager: true }) as Record<string, () => Promise<{ default: Object }>>; const langModules = import.meta.glob('./lang/*/index.ts', { eager: true }) as Record<
string,
() => Promise<{ default: Object }>
>
const langModuleMap = new Map<string, Object>(); const langModuleMap = new Map<string, Object>()
export const langCode: Array<string> = []; export const langCode: Array<string> = []
export const localeConfigKey = 'MaxKB-locale'; export const localeConfigKey = 'MaxKB-locale'
// 获取浏览器默认语言环境 // 获取浏览器默认语言环境
const languages = usePreferredLanguages(); const languages = usePreferredLanguages()
// 生成语言模块列表 // 生成语言模块列表
const generateLangModuleMap = () => { const generateLangModuleMap = () => {
const fullPaths = Object.keys(langModules); const fullPaths = Object.keys(langModules)
fullPaths.forEach((fullPath) => { fullPaths.forEach((fullPath) => {
const k = fullPath.replace('./lang', ''); const k = fullPath.replace('./lang', '')
const startIndex = 1; const startIndex = 1
const lastIndex = k.lastIndexOf('/'); const lastIndex = k.lastIndexOf('/')
const code = k.substring(startIndex, lastIndex); const code = k.substring(startIndex, lastIndex)
langCode.push(code); langCode.push(code)
langModuleMap.set(code, langModules[fullPath]); langModuleMap.set(code, langModules[fullPath])
}); })
}; }
// 导出 Message // 导出 Message
const importMessages = computed(() => { const importMessages = computed(() => {
generateLangModuleMap(); generateLangModuleMap()
const message: Recordable = {}; const message: Recordable = {}
langModuleMap.forEach((value: any, key) => { langModuleMap.forEach((value: any, key) => {
message[key] = value.default; message[key] = value.default
}); })
return message; return message
}); })
export const i18n = createI18n({ export const i18n = createI18n({
legacy: false, legacy: false,
locale: useLocalStorage(localeConfigKey, 'zh_CN').value || languages.value[0] || 'zh_CN', locale: user.getLanguage() || languages.value[0] || 'zh_CN',
fallbackLocale: 'zh_CN', fallbackLocale: 'zh_CN',
messages: importMessages.value, messages: importMessages.value,
globalInjection: true, globalInjection: true
}); })
export const langList = computed(() => { export const langList = computed(() => {
if (langModuleMap.size === 0) generateLangModuleMap(); if (langModuleMap.size === 0) generateLangModuleMap()
const list:any=[] const list: any = []
langModuleMap.forEach((value: any, key) => { langModuleMap.forEach((value: any, key) => {
list.push({ list.push({
label: value.default.lang, label: value.default.lang,
value: key, value: key
}); })
}); })
return list; return list
}); })
// @ts-ignore // @ts-ignore
export const { t } = i18n.global; export const { t } = i18n.global
export default i18n; export default i18n

View File

@ -95,7 +95,7 @@ export default {
}, },
table: { table: {
name: 'File Name', name: 'File Name',
char_length: 'Character Count', char_length: 'Character',
paragraph: 'Segment', paragraph: 'Segment',
all: 'All', all: 'All',
updateTime: 'Update Time' updateTime: 'Update Time'
@ -111,7 +111,7 @@ export default {
REVOKE: 'Cancelling' REVOKE: 'Cancelling'
}, },
enableStatus: { enableStatus: {
label: 'Enable Status', label: 'Status',
enable: 'Enabled', enable: 'Enabled',
close: 'Disabled' close: 'Disabled'
}, },

View File

@ -63,5 +63,16 @@ export default {
label: '格式', label: '格式',
placeholder: '请选择格式' placeholder: '请选择格式'
} }
},
Select: {
label: '选项值',
placeholder: '请输入选项值'
},
tag: {
label: '标签',
placeholder: '请输入选项标签'
},
Slider: {
showInput:''
} }
} }

View File

@ -203,10 +203,12 @@ export const postStream: (url: string, data?: unknown) => Promise<Result<any> |
) => { ) => {
const { user } = useStore() const { user } = useStore()
const token = user.getToken() const token = user.getToken()
const language = user.getLanguage()
const headers: HeadersInit = { 'Content-Type': 'application/json' } const headers: HeadersInit = { 'Content-Type': 'application/json' }
if (token) { if (token) {
headers['AUTHORIZATION'] = `${token}` headers['AUTHORIZATION'] = `${token}`
} }
headers['Accept-Language'] = `${language}`
return fetch(url, { return fetch(url, {
method: 'POST', method: 'POST',
body: data ? JSON.stringify(data) : undefined, body: data ? JSON.stringify(data) : undefined,

View File

@ -8,7 +8,8 @@ import useUserStore from './user'
const useApplicationStore = defineStore({ const useApplicationStore = defineStore({
id: 'application', id: 'application',
state: () => ({ state: () => ({
location: `${window.location.origin}/ui/chat/` location: `${window.location.origin}/ui/chat/`,
userLanguage: ''
}), }),
actions: { actions: {
async asyncGetAllApplication() { async asyncGetAllApplication() {
@ -77,10 +78,10 @@ const useApplicationStore = defineStore({
async asyncGetAppProfile(loading?: Ref<boolean>) { async asyncGetAppProfile(loading?: Ref<boolean>) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const user = useUserStore()
applicationApi applicationApi
.getAppProfile(loading) .getAppProfile(loading)
.then((data) => { .then((data) => {
this.userLanguage = data.data?.language
resolve(data) resolve(data)
}) })
.catch((error) => { .catch((error) => {

View File

@ -6,7 +6,7 @@ import UserApi from '@/api/user'
import ThemeApi from '@/api/theme' import ThemeApi from '@/api/theme'
import { useElementPlusTheme } from 'use-element-plus-theme' import { useElementPlusTheme } from 'use-element-plus-theme'
import { defaultPlatformSetting } from '@/utils/theme' import { defaultPlatformSetting } from '@/utils/theme'
import useApplicationStore from './application'
export interface userStateTypes { export interface userStateTypes {
userType: number // 1 系统操作者 2 对话用户 userType: number // 1 系统操作者 2 对话用户
userInfo: User | null userInfo: User | null
@ -30,6 +30,12 @@ const useUserStore = defineStore({
themeInfo: null themeInfo: null
}), }),
actions: { actions: {
getLanguage() {
const application = useApplicationStore()
return this.userType === 1
? this.userInfo?.language || localStorage.getItem('language')
: application?.userLanguage
},
showXpack() { showXpack() {
return this.isXPack return this.isXPack
}, },
@ -118,6 +124,7 @@ const useUserStore = defineStore({
async profile() { async profile() {
return UserApi.profile().then(async (ok) => { return UserApi.profile().then(async (ok) => {
this.userInfo = ok.data this.userInfo = ok.data
localStorage.setItem('language', ok.data?.language)
return this.asyncGetProfile() return this.asyncGetProfile()
}) })
}, },

View File

@ -76,6 +76,7 @@
prop="char_length" prop="char_length"
:label="$t('views.document.table.char_length')" :label="$t('views.document.table.char_length')"
align="right" align="right"
min-width="90"
> >
<template #default="{ row }"> <template #default="{ row }">
{{ numberFormat(row.char_length) }} {{ numberFormat(row.char_length) }}
@ -85,6 +86,7 @@
prop="paragraph_count" prop="paragraph_count"
:label="$t('views.document.table.paragraph')" :label="$t('views.document.table.paragraph')"
align="right" align="right"
min-width="90"
/> />
<el-table-column prop="status" :label="$t('views.document.fileStatus.label')" width="130"> <el-table-column prop="status" :label="$t('views.document.fileStatus.label')" width="130">
<template #header> <template #header>
@ -216,7 +218,7 @@
<el-icon><Filter /></el-icon> <el-icon><Filter /></el-icon>
</el-button> </el-button>
<template #dropdown> <template #dropdown>
<el-dropdown-menu style="width: 100px"> <el-dropdown-menu style="width: 150px">
<el-dropdown-item <el-dropdown-item
:class="filterMethod['hit_handling_method'] ? '' : 'is-active'" :class="filterMethod['hit_handling_method'] ? '' : 'is-active'"
:command="beforeCommand('hit_handling_method', '')" :command="beforeCommand('hit_handling_method', '')"
@ -228,7 +230,7 @@
:class="filterMethod['hit_handling_method'] === key ? 'is-active' : ''" :class="filterMethod['hit_handling_method'] === key ? 'is-active' : ''"
class="justify-center" class="justify-center"
:command="beforeCommand('hit_handling_method', key)" :command="beforeCommand('hit_handling_method', key)"
>{{ value }}</el-dropdown-item >{{ $t(value) }}</el-dropdown-item
> >
</template> </template>
</el-dropdown-menu> </el-dropdown-menu>

View File

@ -79,7 +79,7 @@ import AddParamDrawer from './AddParamDrawer.vue'
import { MsgError, MsgSuccess } from '@/utils/message' import { MsgError, MsgSuccess } from '@/utils/message'
import ModelApi from '@/api/model' import ModelApi from '@/api/model'
import { input_type_list } from '@/components/dynamics-form/constructor/data' import { input_type_list } from '@/components/dynamics-form/constructor/data'
import { t } from '@/locales'
const props = defineProps<{ const props = defineProps<{
model: Model model: Model
}>() }>()