添加国际化支持:在多个组件中引入 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

View File

@@ -2,9 +2,11 @@
import { getNavMenus } from '@/router/i18n-menu'
import { useDisplay } from 'vuetify'
import { NavMenu } from '@/@layouts/types'
import { useI18n } from 'vue-i18n'
const display = useDisplay()
const appMode = inject('pwaMode') && display.mdAndDown.value
const { t } = useI18n()
const route = useRoute()
@@ -130,7 +132,7 @@ const showDynamicButton = computed(() => {
>
<div class="btn-content">
<VIcon icon="mdi-dots-horizontal" size="24"></VIcon>
<span class="btn-text">更多</span>
<span class="btn-text">{{ t('nav.more') }}</span>
</div>
</VBtn>
</VBtnToggle>

View File

@@ -1,30 +1,33 @@
<script setup lang="ts">
import type { ThemeSwitcherTheme } from '@layouts/types'
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
const themes: ThemeSwitcherTheme[] = [
{
name: 'auto',
title: '跟随系统',
title: t('theme.auto'),
icon: 'mdi-laptop',
},
{
name: 'light',
title: '明亮',
title: t('theme.light'),
icon: 'mdi-weather-sunny',
},
{
name: 'dark',
title: '暗黑',
title: t('theme.dark'),
icon: 'mdi-weather-night',
},
{
name: 'purple',
title: '幻紫',
title: t('theme.purple'),
icon: 'mdi-brightness-4',
},
{
name: 'transparent',
title: '透明',
title: t('theme.transparent'),
icon: 'mdi-gradient-horizontal',
},
]

View File

@@ -2,8 +2,10 @@
import * as Mousetrap from 'mousetrap'
import SearchBarDialog from '@/components/dialog/SearchBarDialog.vue'
import { useDisplay } from 'vuetify'
import { useI18n } from 'vue-i18n'
const display = useDisplay()
const { t } = useI18n()
const searchDialog = ref(false)
@@ -31,7 +33,7 @@ const metaKey = computed(() => (isMac() ? '⌘+K' : 'Ctrl+K'))
<VIcon icon="ri-search-line" />
</IconBtn>
<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>
</div>

View File

@@ -8,6 +8,10 @@ import MessageView from '@/views/system/MessageView.vue'
import api from '@/api'
import { useDisplay } from 'vuetify'
import { getQueryValue } from '@/@core/utils'
import { useI18n } from 'vue-i18n'
// 国际化
const { t } = useI18n()
// 显示器宽度
const display = useDisplay()
@@ -48,43 +52,43 @@ const chatContainer = ref<HTMLElement>()
// 定义捷径列表
const shortcuts = [
{
title: '识别',
subtitle: '名称识别测试',
title: t('shortcut.recognition.title'),
subtitle: t('shortcut.recognition.subtitle'),
icon: 'mdi-text-recognition',
dialog: 'nameTest',
dialogRef: nameTestDialog,
},
{
title: '规则',
subtitle: '规则测试',
title: t('shortcut.rule.title'),
subtitle: t('shortcut.rule.subtitle'),
icon: 'mdi-filter-cog',
dialog: 'ruleTest',
dialogRef: ruleTestDialog,
},
{
title: '日志',
subtitle: '实时日志',
title: t('shortcut.log.title'),
subtitle: t('shortcut.log.subtitle'),
icon: 'mdi-file-document',
dialog: 'logging',
dialogRef: loggingDialog,
},
{
title: '网络',
subtitle: '网速连通性测试',
title: t('shortcut.network.title'),
subtitle: t('shortcut.network.subtitle'),
icon: 'mdi-network',
dialog: 'netTest',
dialogRef: netTestDialog,
},
{
title: '系统',
subtitle: '健康检查',
title: t('shortcut.system.title'),
subtitle: t('shortcut.system.subtitle'),
icon: 'mdi-cog',
dialog: 'systemTest',
dialogRef: systemTestDialog,
},
{
title: '消息',
subtitle: '消息中心',
title: t('shortcut.message.title'),
subtitle: t('shortcut.message.subtitle'),
icon: 'mdi-message',
dialog: 'message',
dialogRef: messageDialog,
@@ -158,7 +162,7 @@ onMounted(() => {
<!-- Menu Content -->
<VCard class="overflow-hidden">
<VCardItem class="py-3">
<VCardTitle>捷径</VCardTitle>
<VCardTitle>{{ t('shortcut.title') }}</VCardTitle>
<template #append>
<IconBtn @click="appsMenu = false">
<VIcon icon="mdi-close" />
@@ -196,7 +200,7 @@ onMounted(() => {
<VCardItem>
<VCardTitle>
<VIcon icon="mdi-text-recognition" class="me-2" />
名称识别测试
{{ t('shortcut.recognition.title') }}
</VCardTitle>
<VDialogCloseBtn @click="nameTestDialog = false" />
</VCardItem>
@@ -212,7 +216,7 @@ onMounted(() => {
<VCardItem>
<VCardTitle>
<VIcon icon="mdi-network" class="me-2" />
网速连通性测试
{{ t('shortcut.network.subtitle') }}
</VCardTitle>
<VDialogCloseBtn @click="netTestDialog = false" />
</VCardItem>
@@ -235,11 +239,11 @@ onMounted(() => {
<VCardItem>
<VCardTitle class="d-inline-flex">
<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">
<VChip color="grey-darken-1" size="small" class="ml-2">
<VIcon icon="mdi-open-in-new" size="small" start />
在新窗口中打开
{{ t('common.openInNewWindow') }}
</VChip>
</a>
</VCardTitle>
@@ -250,13 +254,13 @@ onMounted(() => {
</VCardText>
</VCard>
</VDialog>
<!-- 规则测试弹窗 -->
<VDialog v-if="ruleTestDialog" v-model="ruleTestDialog" max-width="40rem" scrollable>
<!-- 过滤规则弹窗 -->
<VDialog v-if="ruleTestDialog" v-model="ruleTestDialog" max-width="35rem" scrollable>
<VCard>
<VCardItem>
<VCardTitle>
<VIcon icon="mdi-filter-cog" class="me-2" />
规则测试
{{ t('shortcut.rule.subtitle') }}
</VCardTitle>
<VDialogCloseBtn @click="ruleTestDialog = false" />
</VCardItem>
@@ -267,12 +271,12 @@ onMounted(() => {
</VCard>
</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>
<VCardItem>
<VCardTitle>
<VIcon icon="mdi-cog" class="me-2" />
系统健康检查
{{ t('shortcut.system.subtitle') }}
</VCardTitle>
<VDialogCloseBtn @click="systemTestDialog = false" />
</VCardItem>
@@ -283,39 +287,42 @@ onMounted(() => {
</VCard>
</VDialog>
<!-- 消息中心弹窗 -->
<VDialog
v-if="messageDialog"
v-model="messageDialog"
max-width="45rem"
scrollable
:fullscreen="!display.mdAndUp.value"
>
<VDialog v-if="messageDialog" v-model="messageDialog" max-width="35rem" scrollable>
<VCard>
<VCardItem>
<VCardTitle>
<VIcon icon="mdi-message" class="me-2" />
消息中心
{{ t('shortcut.message.subtitle') }}
</VCardTitle>
<VDialogCloseBtn @click="messageDialog = false" />
</VCardItem>
<VDivider />
<VCardText ref="chatContainer">
<MessageView @scroll="scrollMessageToEnd" />
<VCardText>
<MessageView ref="chatContainer" />
</VCardText>
<VCardItem>
<VTextField
v-model="user_message"
variant="solo"
placeholder="输入消息或命令"
clearable
:disabled="sendButtonDisabled"
@keydown.enter="sendMessage"
>
<template #append-inner>
<VBtn color="primary" :disabled="sendButtonDisabled" @click="sendMessage"> 发送 </VBtn>
</template>
</VTextField>
</VCardItem>
<VDivider />
<VCardActions class="pa-4">
<div class="d-flex w-100 gap-2">
<VTextField
v-model="user_message"
variant="outlined"
hide-details
density="compact"
:placeholder="t('common.inputMessage')"
@keyup.enter="sendMessage"
/>
<VBtn
:disabled="sendButtonDisabled"
@click="sendMessage"
:loading="sendButtonDisabled"
color="primary"
min-width="auto"
width="46"
height="38"
>{{ t('common.send') }}</VBtn
>
</div>
</VCardActions>
</VCard>
</VDialog>
</template>

View File

@@ -1,6 +1,9 @@
<script setup lang="ts">
import { formatDateDifference } from '@core/utils/formatters'
import { SystemNotification } from '@/api/types'
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
// 是否有新消息
const hasNewMessage = ref(false)
@@ -63,9 +66,9 @@ onBeforeUnmount(() => {
<!-- Menu Content -->
<VCard>
<VCardItem class="py-3">
<VCardTitle>通知中心</VCardTitle>
<VCardTitle>{{ t('notification.center') }}</VCardTitle>
<template #append>
<VTooltip text="设为已读">
<VTooltip :text="t('notification.markRead')">
<template #activator="{ props }">
<IconBtn
v-bind="props"
@@ -107,7 +110,7 @@ onBeforeUnmount(() => {
</div>
<div v-else class="py-8 text-center">
<VIcon icon="mdi-bell-sleep-outline" size="40" class="mb-3" />
<div>暂无通知</div>
<div>{{ t('notification.empty') }}</div>
</div>
</VCard>
</VMenu>

View File

@@ -99,7 +99,7 @@ const userLevel = computed(() => userStore.level)
</template>
<div>
<span class="text-primary text-sm font-medium d-block">
{{ superUser ? '管理员' : '普通用户' }}
{{ superUser ? t('user.admin') : t('user.normalUser') }}
</span>
<span class="text-high-emphasis text-lg font-weight-bold">
{{ userName }}
@@ -113,14 +113,14 @@ const userLevel = computed(() => userStore.level)
<template #prepend>
<VIcon icon="mdi-account-outline" />
</template>
<VListItemTitle>个人信息</VListItemTitle>
<VListItemTitle>{{ t('user.profile') }}</VListItemTitle>
</VListItem>
<VListItem link @click="router.push('/setting')" class="mb-1 rounded-lg" hover>
<template #prepend>
<VIcon icon="mdi-cog-outline" />
</template>
<VListItemTitle>系统设定</VListItemTitle>
<VListItemTitle>{{ t('user.systemSettings') }}</VListItemTitle>
</VListItem>
<!-- 👉 Site Auth -->
@@ -128,7 +128,7 @@ const userLevel = computed(() => userStore.level)
<template #prepend>
<VIcon icon="mdi-lock-check-outline" />
</template>
<VListItemTitle>用户认证</VListItemTitle>
<VListItemTitle>{{ t('user.siteAuth') }}</VListItemTitle>
</VListItem>
<!-- 👉 FAQ -->
@@ -136,7 +136,7 @@ const userLevel = computed(() => userStore.level)
<template #prepend>
<VIcon icon="mdi-help-circle-outline" />
</template>
<VListItemTitle>帮助文档</VListItemTitle>
<VListItemTitle>{{ t('user.helpDocs') }}</VListItemTitle>
</VListItem>
<!-- Divider -->
@@ -147,7 +147,7 @@ const userLevel = computed(() => userStore.level)
<template #prepend>
<VIcon icon="mdi-restart" />
</template>
<VListItemTitle>重启</VListItemTitle>
<VListItemTitle>{{ t('user.restart') }}</VListItemTitle>
</VListItem>
</div>
<!-- 👉 Logout -->

View File

@@ -2,6 +2,7 @@
import api from '@/api'
import useDragAndDrop from '@core/utils/workflow'
import { useDisplay } from 'vuetify'
import { useI18n } from 'vue-i18n'
interface ActionItem {
name: string
@@ -12,6 +13,7 @@ interface ActionItem {
const display = useDisplay()
// APP
const appMode = inject('pwaMode') && display.mdAndDown.value
const { t } = useI18n()
const { onDragStart } = useDragAndDrop()
@@ -119,7 +121,9 @@ onMounted(() => {
<VAvatar size="36" class="workflow-logo">
<VIcon icon="mdi-puzzle" />
</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">
<VIcon :icon="isSidebarCollapsed ? 'mdi-chevron-right' : 'mdi-chevron-left'" />
</IconBtn>
@@ -142,7 +146,9 @@ onMounted(() => {
</VAvatar>
<div v-if="!isSidebarCollapsed || display.smAndDown.value" class="component-info">
<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>
</VCard>
</div>
@@ -155,7 +161,9 @@ onMounted(() => {
<VIcon v-if="isSidebarCollapsed && !display.smAndDown.value" class="footer-icon" icon="mdi-gesture-swipe" />
<template v-else>
<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>
</div>
</VBtn>

View File

@@ -10,6 +10,9 @@ export default {
loading: 'Loading',
success: 'Success',
error: 'Error',
openInNewWindow: 'Open in New Window',
inputMessage: 'Enter message or command',
send: 'Send',
},
theme: {
light: 'Light',
@@ -146,4 +149,55 @@ export default {
douban: 'Douban',
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',
},
}

View File

@@ -10,6 +10,9 @@ export default {
loading: '加载中',
success: '成功',
error: '错误',
openInNewWindow: '在新窗口中打开',
inputMessage: '输入消息或命令',
send: '发送',
},
theme: {
light: '浅色',
@@ -146,4 +149,55 @@ export default {
douban: '豆瓣',
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: '拖动组件到画布',
},
}