添加国际化支持:在多个组件中引入 vue-i18n,更新文本以支持多语言显示

This commit is contained in:
jxxghp
2025-04-27 17:53:22 +08:00
parent d0b3bc8137
commit c46d556684
9 changed files with 198 additions and 65 deletions
+3 -1
View File
@@ -2,9 +2,11 @@
import { getNavMenus } from '@/router/i18n-menu' import { getNavMenus } from '@/router/i18n-menu'
import { useDisplay } from 'vuetify' import { useDisplay } from 'vuetify'
import { NavMenu } from '@/@layouts/types' import { NavMenu } from '@/@layouts/types'
import { useI18n } from 'vue-i18n'
const display = useDisplay() const display = useDisplay()
const appMode = inject('pwaMode') && display.mdAndDown.value const appMode = inject('pwaMode') && display.mdAndDown.value
const { t } = useI18n()
const route = useRoute() const route = useRoute()
@@ -130,7 +132,7 @@ const showDynamicButton = computed(() => {
> >
<div class="btn-content"> <div class="btn-content">
<VIcon icon="mdi-dots-horizontal" size="24"></VIcon> <VIcon icon="mdi-dots-horizontal" size="24"></VIcon>
<span class="btn-text">更多</span> <span class="btn-text">{{ t('nav.more') }}</span>
</div> </div>
</VBtn> </VBtn>
</VBtnToggle> </VBtnToggle>
@@ -1,30 +1,33 @@
<script setup lang="ts"> <script setup lang="ts">
import type { ThemeSwitcherTheme } from '@layouts/types' import type { ThemeSwitcherTheme } from '@layouts/types'
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
const themes: ThemeSwitcherTheme[] = [ const themes: ThemeSwitcherTheme[] = [
{ {
name: 'auto', name: 'auto',
title: '跟随系统', title: t('theme.auto'),
icon: 'mdi-laptop', icon: 'mdi-laptop',
}, },
{ {
name: 'light', name: 'light',
title: '明亮', title: t('theme.light'),
icon: 'mdi-weather-sunny', icon: 'mdi-weather-sunny',
}, },
{ {
name: 'dark', name: 'dark',
title: '暗黑', title: t('theme.dark'),
icon: 'mdi-weather-night', icon: 'mdi-weather-night',
}, },
{ {
name: 'purple', name: 'purple',
title: '幻紫', title: t('theme.purple'),
icon: 'mdi-brightness-4', icon: 'mdi-brightness-4',
}, },
{ {
name: 'transparent', name: 'transparent',
title: '透明', title: t('theme.transparent'),
icon: 'mdi-gradient-horizontal', icon: 'mdi-gradient-horizontal',
}, },
] ]
+3 -1
View File
@@ -2,8 +2,10 @@
import * as Mousetrap from 'mousetrap' import * as Mousetrap from 'mousetrap'
import SearchBarDialog from '@/components/dialog/SearchBarDialog.vue' import SearchBarDialog from '@/components/dialog/SearchBarDialog.vue'
import { useDisplay } from 'vuetify' import { useDisplay } from 'vuetify'
import { useI18n } from 'vue-i18n'
const display = useDisplay() const display = useDisplay()
const { t } = useI18n()
const searchDialog = ref(false) const searchDialog = ref(false)
@@ -31,7 +33,7 @@ const metaKey = computed(() => (isMac() ? '⌘+K' : 'Ctrl+K'))
<VIcon icon="ri-search-line" /> <VIcon icon="ri-search-line" />
</IconBtn> </IconBtn>
<span v-if="display.lgAndUp.value" class="flex align-center text-disabled ms-2" @click="openSearchDialog"> <span v-if="display.lgAndUp.value" class="flex align-center text-disabled ms-2" @click="openSearchDialog">
<span class="me-3">搜索</span> <span class="me-3">{{ t('common.search') }}</span>
<span class="meta-key">{{ metaKey }}</span> <span class="meta-key">{{ metaKey }}</span>
</span> </span>
</div> </div>
+53 -46
View File
@@ -8,6 +8,10 @@ import MessageView from '@/views/system/MessageView.vue'
import api from '@/api' import api from '@/api'
import { useDisplay } from 'vuetify' import { useDisplay } from 'vuetify'
import { getQueryValue } from '@/@core/utils' import { getQueryValue } from '@/@core/utils'
import { useI18n } from 'vue-i18n'
// 国际化
const { t } = useI18n()
// 显示器宽度 // 显示器宽度
const display = useDisplay() const display = useDisplay()
@@ -48,43 +52,43 @@ const chatContainer = ref<HTMLElement>()
// 定义捷径列表 // 定义捷径列表
const shortcuts = [ const shortcuts = [
{ {
title: '识别', title: t('shortcut.recognition.title'),
subtitle: '名称识别测试', subtitle: t('shortcut.recognition.subtitle'),
icon: 'mdi-text-recognition', icon: 'mdi-text-recognition',
dialog: 'nameTest', dialog: 'nameTest',
dialogRef: nameTestDialog, dialogRef: nameTestDialog,
}, },
{ {
title: '规则', title: t('shortcut.rule.title'),
subtitle: '规则测试', subtitle: t('shortcut.rule.subtitle'),
icon: 'mdi-filter-cog', icon: 'mdi-filter-cog',
dialog: 'ruleTest', dialog: 'ruleTest',
dialogRef: ruleTestDialog, dialogRef: ruleTestDialog,
}, },
{ {
title: '日志', title: t('shortcut.log.title'),
subtitle: '实时日志', subtitle: t('shortcut.log.subtitle'),
icon: 'mdi-file-document', icon: 'mdi-file-document',
dialog: 'logging', dialog: 'logging',
dialogRef: loggingDialog, dialogRef: loggingDialog,
}, },
{ {
title: '网络', title: t('shortcut.network.title'),
subtitle: '网速连通性测试', subtitle: t('shortcut.network.subtitle'),
icon: 'mdi-network', icon: 'mdi-network',
dialog: 'netTest', dialog: 'netTest',
dialogRef: netTestDialog, dialogRef: netTestDialog,
}, },
{ {
title: '系统', title: t('shortcut.system.title'),
subtitle: '健康检查', subtitle: t('shortcut.system.subtitle'),
icon: 'mdi-cog', icon: 'mdi-cog',
dialog: 'systemTest', dialog: 'systemTest',
dialogRef: systemTestDialog, dialogRef: systemTestDialog,
}, },
{ {
title: '消息', title: t('shortcut.message.title'),
subtitle: '消息中心', subtitle: t('shortcut.message.subtitle'),
icon: 'mdi-message', icon: 'mdi-message',
dialog: 'message', dialog: 'message',
dialogRef: messageDialog, dialogRef: messageDialog,
@@ -158,7 +162,7 @@ onMounted(() => {
<!-- Menu Content --> <!-- Menu Content -->
<VCard class="overflow-hidden"> <VCard class="overflow-hidden">
<VCardItem class="py-3"> <VCardItem class="py-3">
<VCardTitle>捷径</VCardTitle> <VCardTitle>{{ t('shortcut.title') }}</VCardTitle>
<template #append> <template #append>
<IconBtn @click="appsMenu = false"> <IconBtn @click="appsMenu = false">
<VIcon icon="mdi-close" /> <VIcon icon="mdi-close" />
@@ -196,7 +200,7 @@ onMounted(() => {
<VCardItem> <VCardItem>
<VCardTitle> <VCardTitle>
<VIcon icon="mdi-text-recognition" class="me-2" /> <VIcon icon="mdi-text-recognition" class="me-2" />
名称识别测试 {{ t('shortcut.recognition.title') }}
</VCardTitle> </VCardTitle>
<VDialogCloseBtn @click="nameTestDialog = false" /> <VDialogCloseBtn @click="nameTestDialog = false" />
</VCardItem> </VCardItem>
@@ -212,7 +216,7 @@ onMounted(() => {
<VCardItem> <VCardItem>
<VCardTitle> <VCardTitle>
<VIcon icon="mdi-network" class="me-2" /> <VIcon icon="mdi-network" class="me-2" />
网速连通性测试 {{ t('shortcut.network.subtitle') }}
</VCardTitle> </VCardTitle>
<VDialogCloseBtn @click="netTestDialog = false" /> <VDialogCloseBtn @click="netTestDialog = false" />
</VCardItem> </VCardItem>
@@ -235,11 +239,11 @@ onMounted(() => {
<VCardItem> <VCardItem>
<VCardTitle class="d-inline-flex"> <VCardTitle class="d-inline-flex">
<VIcon icon="mdi-file-document" class="me-2" /> <VIcon icon="mdi-file-document" class="me-2" />
实时日志 {{ t('shortcut.log.subtitle') }}
<a class="mx-2 d-inline-flex align-center" :href="allLoggingUrl()" target="_blank"> <a class="mx-2 d-inline-flex align-center" :href="allLoggingUrl()" target="_blank">
<VChip color="grey-darken-1" size="small" class="ml-2"> <VChip color="grey-darken-1" size="small" class="ml-2">
<VIcon icon="mdi-open-in-new" size="small" start /> <VIcon icon="mdi-open-in-new" size="small" start />
在新窗口中打开 {{ t('common.openInNewWindow') }}
</VChip> </VChip>
</a> </a>
</VCardTitle> </VCardTitle>
@@ -250,13 +254,13 @@ onMounted(() => {
</VCardText> </VCardText>
</VCard> </VCard>
</VDialog> </VDialog>
<!-- 规则测试弹窗 --> <!-- 过滤规则弹窗 -->
<VDialog v-if="ruleTestDialog" v-model="ruleTestDialog" max-width="40rem" scrollable> <VDialog v-if="ruleTestDialog" v-model="ruleTestDialog" max-width="35rem" scrollable>
<VCard> <VCard>
<VCardItem> <VCardItem>
<VCardTitle> <VCardTitle>
<VIcon icon="mdi-filter-cog" class="me-2" /> <VIcon icon="mdi-filter-cog" class="me-2" />
规则测试 {{ t('shortcut.rule.subtitle') }}
</VCardTitle> </VCardTitle>
<VDialogCloseBtn @click="ruleTestDialog = false" /> <VDialogCloseBtn @click="ruleTestDialog = false" />
</VCardItem> </VCardItem>
@@ -267,12 +271,12 @@ onMounted(() => {
</VCard> </VCard>
</VDialog> </VDialog>
<!-- 系统健康检查弹窗 --> <!-- 系统健康检查弹窗 -->
<VDialog v-if="systemTestDialog" v-model="systemTestDialog" max-width="35rem" max-height="85vh" scrollable> <VDialog v-if="systemTestDialog" v-model="systemTestDialog" max-width="35rem" scrollable>
<VCard> <VCard>
<VCardItem> <VCardItem>
<VCardTitle> <VCardTitle>
<VIcon icon="mdi-cog" class="me-2" /> <VIcon icon="mdi-cog" class="me-2" />
系统健康检查 {{ t('shortcut.system.subtitle') }}
</VCardTitle> </VCardTitle>
<VDialogCloseBtn @click="systemTestDialog = false" /> <VDialogCloseBtn @click="systemTestDialog = false" />
</VCardItem> </VCardItem>
@@ -283,39 +287,42 @@ onMounted(() => {
</VCard> </VCard>
</VDialog> </VDialog>
<!-- 消息中心弹窗 --> <!-- 消息中心弹窗 -->
<VDialog <VDialog v-if="messageDialog" v-model="messageDialog" max-width="35rem" scrollable>
v-if="messageDialog"
v-model="messageDialog"
max-width="45rem"
scrollable
:fullscreen="!display.mdAndUp.value"
>
<VCard> <VCard>
<VCardItem> <VCardItem>
<VCardTitle> <VCardTitle>
<VIcon icon="mdi-message" class="me-2" /> <VIcon icon="mdi-message" class="me-2" />
消息中心 {{ t('shortcut.message.subtitle') }}
</VCardTitle> </VCardTitle>
<VDialogCloseBtn @click="messageDialog = false" /> <VDialogCloseBtn @click="messageDialog = false" />
</VCardItem> </VCardItem>
<VDivider /> <VDivider />
<VCardText ref="chatContainer"> <VCardText>
<MessageView @scroll="scrollMessageToEnd" /> <MessageView ref="chatContainer" />
</VCardText> </VCardText>
<VCardItem> <VDivider />
<VTextField <VCardActions class="pa-4">
v-model="user_message" <div class="d-flex w-100 gap-2">
variant="solo" <VTextField
placeholder="输入消息或命令" v-model="user_message"
clearable variant="outlined"
:disabled="sendButtonDisabled" hide-details
@keydown.enter="sendMessage" density="compact"
> :placeholder="t('common.inputMessage')"
<template #append-inner> @keyup.enter="sendMessage"
<VBtn color="primary" :disabled="sendButtonDisabled" @click="sendMessage"> 发送 </VBtn> />
</template> <VBtn
</VTextField> :disabled="sendButtonDisabled"
</VCardItem> @click="sendMessage"
:loading="sendButtonDisabled"
color="primary"
min-width="auto"
width="46"
height="38"
>{{ t('common.send') }}</VBtn
>
</div>
</VCardActions>
</VCard> </VCard>
</VDialog> </VDialog>
</template> </template>
+6 -3
View File
@@ -1,6 +1,9 @@
<script setup lang="ts"> <script setup lang="ts">
import { formatDateDifference } from '@core/utils/formatters' import { formatDateDifference } from '@core/utils/formatters'
import { SystemNotification } from '@/api/types' import { SystemNotification } from '@/api/types'
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
// 是否有新消息 // 是否有新消息
const hasNewMessage = ref(false) const hasNewMessage = ref(false)
@@ -63,9 +66,9 @@ onBeforeUnmount(() => {
<!-- Menu Content --> <!-- Menu Content -->
<VCard> <VCard>
<VCardItem class="py-3"> <VCardItem class="py-3">
<VCardTitle>通知中心</VCardTitle> <VCardTitle>{{ t('notification.center') }}</VCardTitle>
<template #append> <template #append>
<VTooltip text="设为已读"> <VTooltip :text="t('notification.markRead')">
<template #activator="{ props }"> <template #activator="{ props }">
<IconBtn <IconBtn
v-bind="props" v-bind="props"
@@ -107,7 +110,7 @@ onBeforeUnmount(() => {
</div> </div>
<div v-else class="py-8 text-center"> <div v-else class="py-8 text-center">
<VIcon icon="mdi-bell-sleep-outline" size="40" class="mb-3" /> <VIcon icon="mdi-bell-sleep-outline" size="40" class="mb-3" />
<div>暂无通知</div> <div>{{ t('notification.empty') }}</div>
</div> </div>
</VCard> </VCard>
</VMenu> </VMenu>
+6 -6
View File
@@ -99,7 +99,7 @@ const userLevel = computed(() => userStore.level)
</template> </template>
<div> <div>
<span class="text-primary text-sm font-medium d-block"> <span class="text-primary text-sm font-medium d-block">
{{ superUser ? '管理员' : '普通用户' }} {{ superUser ? t('user.admin') : t('user.normalUser') }}
</span> </span>
<span class="text-high-emphasis text-lg font-weight-bold"> <span class="text-high-emphasis text-lg font-weight-bold">
{{ userName }} {{ userName }}
@@ -113,14 +113,14 @@ const userLevel = computed(() => userStore.level)
<template #prepend> <template #prepend>
<VIcon icon="mdi-account-outline" /> <VIcon icon="mdi-account-outline" />
</template> </template>
<VListItemTitle>个人信息</VListItemTitle> <VListItemTitle>{{ t('user.profile') }}</VListItemTitle>
</VListItem> </VListItem>
<VListItem link @click="router.push('/setting')" class="mb-1 rounded-lg" hover> <VListItem link @click="router.push('/setting')" class="mb-1 rounded-lg" hover>
<template #prepend> <template #prepend>
<VIcon icon="mdi-cog-outline" /> <VIcon icon="mdi-cog-outline" />
</template> </template>
<VListItemTitle>系统设定</VListItemTitle> <VListItemTitle>{{ t('user.systemSettings') }}</VListItemTitle>
</VListItem> </VListItem>
<!-- 👉 Site Auth --> <!-- 👉 Site Auth -->
@@ -128,7 +128,7 @@ const userLevel = computed(() => userStore.level)
<template #prepend> <template #prepend>
<VIcon icon="mdi-lock-check-outline" /> <VIcon icon="mdi-lock-check-outline" />
</template> </template>
<VListItemTitle>用户认证</VListItemTitle> <VListItemTitle>{{ t('user.siteAuth') }}</VListItemTitle>
</VListItem> </VListItem>
<!-- 👉 FAQ --> <!-- 👉 FAQ -->
@@ -136,7 +136,7 @@ const userLevel = computed(() => userStore.level)
<template #prepend> <template #prepend>
<VIcon icon="mdi-help-circle-outline" /> <VIcon icon="mdi-help-circle-outline" />
</template> </template>
<VListItemTitle>帮助文档</VListItemTitle> <VListItemTitle>{{ t('user.helpDocs') }}</VListItemTitle>
</VListItem> </VListItem>
<!-- Divider --> <!-- Divider -->
@@ -147,7 +147,7 @@ const userLevel = computed(() => userStore.level)
<template #prepend> <template #prepend>
<VIcon icon="mdi-restart" /> <VIcon icon="mdi-restart" />
</template> </template>
<VListItemTitle>重启</VListItemTitle> <VListItemTitle>{{ t('user.restart') }}</VListItemTitle>
</VListItem> </VListItem>
</div> </div>
<!-- 👉 Logout --> <!-- 👉 Logout -->
+11 -3
View File
@@ -2,6 +2,7 @@
import api from '@/api' import api from '@/api'
import useDragAndDrop from '@core/utils/workflow' import useDragAndDrop from '@core/utils/workflow'
import { useDisplay } from 'vuetify' import { useDisplay } from 'vuetify'
import { useI18n } from 'vue-i18n'
interface ActionItem { interface ActionItem {
name: string name: string
@@ -12,6 +13,7 @@ interface ActionItem {
const display = useDisplay() const display = useDisplay()
// APP // APP
const appMode = inject('pwaMode') && display.mdAndDown.value const appMode = inject('pwaMode') && display.mdAndDown.value
const { t } = useI18n()
const { onDragStart } = useDragAndDrop() const { onDragStart } = useDragAndDrop()
@@ -119,7 +121,9 @@ onMounted(() => {
<VAvatar size="36" class="workflow-logo"> <VAvatar size="36" class="workflow-logo">
<VIcon icon="mdi-puzzle" /> <VIcon icon="mdi-puzzle" />
</VAvatar> </VAvatar>
<span v-if="!isSidebarCollapsed || display.smAndDown.value" class="header-title">动作组件</span> <span v-if="!isSidebarCollapsed || display.smAndDown.value" class="header-title">{{
t('workflow.components')
}}</span>
<IconBtn v-if="!display.smAndDown.value" @click="toggleSidebar" class="collapse-btn"> <IconBtn v-if="!display.smAndDown.value" @click="toggleSidebar" class="collapse-btn">
<VIcon :icon="isSidebarCollapsed ? 'mdi-chevron-right' : 'mdi-chevron-left'" /> <VIcon :icon="isSidebarCollapsed ? 'mdi-chevron-right' : 'mdi-chevron-left'" />
</IconBtn> </IconBtn>
@@ -142,7 +146,9 @@ onMounted(() => {
</VAvatar> </VAvatar>
<div v-if="!isSidebarCollapsed || display.smAndDown.value" class="component-info"> <div v-if="!isSidebarCollapsed || display.smAndDown.value" class="component-info">
<div class="component-name">{{ action.name }}</div> <div class="component-name">{{ action.name }}</div>
<div class="component-desc">{{ display.smAndDown.value ? '点击添加' : '拖动到画布' }}</div> <div class="component-desc">
{{ display.smAndDown.value ? t('workflow.clickToAdd') : t('workflow.dragToCanvas') }}
</div>
</div> </div>
</VCard> </VCard>
</div> </div>
@@ -155,7 +161,9 @@ onMounted(() => {
<VIcon v-if="isSidebarCollapsed && !display.smAndDown.value" class="footer-icon" icon="mdi-gesture-swipe" /> <VIcon v-if="isSidebarCollapsed && !display.smAndDown.value" class="footer-icon" icon="mdi-gesture-swipe" />
<template v-else> <template v-else>
<VIcon :icon="display.smAndDown.value ? 'mdi-gesture-tap' : 'mdi-gesture-swipe'" class="me-2" /> <VIcon :icon="display.smAndDown.value ? 'mdi-gesture-tap' : 'mdi-gesture-swipe'" class="me-2" />
<span>{{ display.smAndDown.value ? '点击组件添加到画布' : '拖动组件到画布' }}</span> <span>{{
display.smAndDown.value ? t('workflow.tapComponentHint') : t('workflow.dragComponentHint')
}}</span>
</template> </template>
</div> </div>
</VBtn> </VBtn>
+54
View File
@@ -10,6 +10,9 @@ export default {
loading: 'Loading', loading: 'Loading',
success: 'Success', success: 'Success',
error: 'Error', error: 'Error',
openInNewWindow: 'Open in New Window',
inputMessage: 'Enter message or command',
send: 'Send',
}, },
theme: { theme: {
light: 'Light', light: 'Light',
@@ -146,4 +149,55 @@ export default {
douban: 'Douban', douban: 'Douban',
bangumi: 'Bangumi', bangumi: 'Bangumi',
}, },
user: {
admin: 'Administrator',
normalUser: 'Normal User',
profile: 'Profile',
systemSettings: 'System Settings',
siteAuth: 'User Authentication',
helpDocs: 'Help Docs',
restart: 'Restart',
},
nav: {
more: 'More',
},
notification: {
center: 'Notification Center',
markRead: 'Mark as Read',
empty: 'No Notifications',
},
shortcut: {
title: 'Shortcuts',
recognition: {
title: 'Recognition',
subtitle: 'Name Recognition Test',
},
rule: {
title: 'Rules',
subtitle: 'Rule Test',
},
log: {
title: 'Logs',
subtitle: 'Real-time Logs',
},
network: {
title: 'Network',
subtitle: 'Speed & Connectivity Test',
},
system: {
title: 'System',
subtitle: 'Health Check',
},
message: {
title: 'Messages',
subtitle: 'Message Center',
},
},
workflow: {
components: 'Action Components',
clickToAdd: 'Click to Add',
dragToCanvas: 'Drag to Canvas',
tapComponentHint: 'Tap component to add to canvas',
dragComponentHint: 'Drag components to canvas',
},
} }
+54
View File
@@ -10,6 +10,9 @@ export default {
loading: '加载中', loading: '加载中',
success: '成功', success: '成功',
error: '错误', error: '错误',
openInNewWindow: '在新窗口中打开',
inputMessage: '输入消息或命令',
send: '发送',
}, },
theme: { theme: {
light: '浅色', light: '浅色',
@@ -146,4 +149,55 @@ export default {
douban: '豆瓣', douban: '豆瓣',
bangumi: 'Bangumi', bangumi: 'Bangumi',
}, },
user: {
admin: '管理员',
normalUser: '普通用户',
profile: '个人信息',
systemSettings: '系统设定',
siteAuth: '用户认证',
helpDocs: '帮助文档',
restart: '重启',
},
nav: {
more: '更多',
},
notification: {
center: '通知中心',
markRead: '设为已读',
empty: '暂无通知',
},
shortcut: {
title: '捷径',
recognition: {
title: '识别',
subtitle: '名称识别测试',
},
rule: {
title: '规则',
subtitle: '规则测试',
},
log: {
title: '日志',
subtitle: '实时日志',
},
network: {
title: '网络',
subtitle: '网速连通性测试',
},
system: {
title: '系统',
subtitle: '健康检查',
},
message: {
title: '消息',
subtitle: '消息中心',
},
},
workflow: {
components: '动作组件',
clickToAdd: '点击添加',
dragToCanvas: '拖动到画布',
tapComponentHint: '点击组件添加到画布',
dragComponentHint: '拖动组件到画布',
},
} }