更新国际化支持:调整多个组件中的文本引入,优化语言切换和翻译功能,删除不再使用的类型文件。

This commit is contained in:
jxxghp
2025-04-27 20:49:44 +08:00
parent f809c8e538
commit 0396f180ae
21 changed files with 260 additions and 97 deletions

View File

@@ -1,5 +1,5 @@
<script setup lang="ts">
import { SUPPORTED_LOCALES, SupportedLocale } from '@/locales/types'
import { SUPPORTED_LOCALES, SupportedLocale } from '@/types/i18n'
import { setI18nLanguage, getCurrentLocale } from '@/plugins/i18n'
// 当前语言

View File

@@ -5,6 +5,10 @@ import { SubscribeShare } from '@/api/types'
import router from '@/router'
import { useToast } from 'vue-toast-notification'
import { VBtn } from 'vuetify/lib/components/index.mjs'
import { useI18n } from 'vue-i18n'
// 国际化
const { t } = useI18n()
// 输入参数
const props = defineProps({
@@ -116,11 +120,11 @@ async function doFork() {
const result: { [key: string]: any } = await api.post('subscribe/fork', props.media)
// 订阅状态
if (result.success) {
$toast.success(`${props.media?.share_title} 添加订阅成功!`)
$toast.success(t('subscribe.addSuccess', { name: props.media?.share_title }))
// 完成
emit('fork', result.data.id)
} else {
$toast.error(`${props.media?.share_title} 添加订阅失败:${result.message}`)
$toast.error(t('subscribe.addFailed', { name: props.media?.share_title, message: result.message }))
}
} catch (error) {
console.error(error)
@@ -144,11 +148,11 @@ async function doDelete() {
})
// 订阅状态
if (result.success) {
$toast.success(`${props.media?.share_title} 取消分享成功!`)
$toast.success(t('subscribe.cancelSuccess'))
// 完成
emit('delete', result.data.id)
} else {
$toast.error(`${props.media?.share_title} 取消分享失败:${result.message}`)
$toast.error(t('subscribe.cancelFailed', { message: result.message }))
}
} catch (error) {
console.error(error)
@@ -200,13 +204,13 @@ onMounted(() => {
<VList lines="one">
<VListItem class="ps-0">
<VListItemTitle class="text-center text-md-left">
<span class="font-weight-medium">分享人</span>
<span class="font-weight-medium">{{ t('subscribe.sharer') }}</span>
<span class="text-body-1"> {{ media?.share_user }}</span>
</VListItemTitle>
</VListItem>
<VListItem class="ps-0" v-if="media?.keyword">
<VListItemTitle class="text-center text-md-left">
<span class="font-weight-medium">搜索词</span>
<span class="font-weight-medium">{{ t('subscribe.keyword') }}</span>
<span class="text-body-1"> {{ media?.keyword }}</span>
</VListItemTitle>
</VListItem>
@@ -217,7 +221,7 @@ onMounted(() => {
'line-clamp-4 overflow-hidden text-ellipsis': !isExpanded,
}"
>
<span class="font-weight-medium">识别词</span>
<span class="font-weight-medium">{{ t('subscribe.recognitionWords') }}</span>
<span class="text-body-1"> {{ media?.custom_words }}</span>
</VListItemTitle>
</VListItem>
@@ -232,7 +236,7 @@ onMounted(() => {
:loading="processing"
class="mb-2 me-2"
>
订阅
{{ t('subscribe.normalSub') }}
</VBtn>
<VBtn
v-if="isFollowed && props.media?.share_uid"
@@ -241,7 +245,7 @@ onMounted(() => {
prepend-icon="mdi-account-remove"
class="mb-2 me-2"
>
取消关注
{{ t('subscribe.unfollow') }}
</VBtn>
<VBtn
v-else-if="props.media?.share_uid"
@@ -250,7 +254,7 @@ onMounted(() => {
prepend-icon="mdi-account-plus"
class="mb-2 me-2"
>
关注
{{ t('subscribe.follow') }}
</VBtn>
<VBtn
v-if="
@@ -264,11 +268,13 @@ onMounted(() => {
:loading="deleting"
class="mb-2 me-2"
>
取消分享
{{ t('subscribe.cancelShare') }}
</VBtn>
</div>
<div class="text-xs mt-2" v-if="props.media?.count">
<VIcon icon="mdi-fire" /> {{ props.media?.count?.toLocaleString() }} 次复用
<VIcon icon="mdi-fire" />{{
t('subscribe.usageCount', { count: props.media?.count?.toLocaleString() })
}}
</div>
</div>
</VCardItem>

View File

@@ -232,6 +232,18 @@ export default {
name: 'Name',
searchShares: 'Search Subscription Shares',
keyword: 'Keyword',
noShareData:
'No shared subscription data available, data sharing is not enabled or the server cannot be connected.',
noPopularData:
'No popular subscription data available, data sharing is not enabled or the server cannot be connected.',
noFilterData: 'No matching content found. Please change the filter criteria.',
noSubscribeData: 'Please search to add movie or TV show subscriptions.',
sharer: 'Shared by',
follow: 'Follow',
unfollow: 'Unfollow',
recognitionWords: 'Recognition Words',
cancelShare: 'Cancel Share',
usageCount: 'Used {count} times',
},
recommend: {
all: 'All',
@@ -307,4 +319,52 @@ export default {
loadMore: 'Load More',
noMatchingResults: 'No matching results',
},
calendar: {
episode: 'Episode {number}',
},
storage: {
usedPercent: 'Used {percent}%',
},
site: {
noSites: 'No Sites',
sitesWillBeShownHere: 'Added and supported sites will be displayed here.',
},
message: {
loadMore: 'Load More',
noMoreData: 'No More Data',
},
logging: {
level: 'Level',
time: 'Time',
program: 'Program',
content: 'Content',
refreshing: 'Refreshing',
},
moduleTest: {
normal: 'Normal',
disabled: 'Disabled',
error: 'Error',
},
nameTest: {
recognize: 'Recognize',
recognizing: 'Recognizing...',
recognizeAgain: 'Recognize Again',
title: 'Title',
subtitle: 'Subtitle',
},
netTest: {
notTested: 'Not Tested',
testing: 'Testing...',
normal: 'Normal',
},
ruleTest: {
test: 'Test',
testing: 'Testing...',
testAgain: 'Test Again',
title: 'Title',
subtitle: 'Subtitle',
ruleGroup: 'Rule Group',
priority: 'Priority: {value}',
noPriorityRule: 'No priority rule matched!',
},
}

View File

@@ -232,6 +232,16 @@ export default {
name: '名称',
searchShares: '搜索订阅分享',
keyword: '关键词',
noShareData: '未获取到分享订阅数据,未开启数据分享或服务器无法连接。',
noPopularData: '未获取到热门订阅数据,未开启数据分享或服务器无法连接。',
noFilterData: '没有筛选到相关内容,请更换筛选条件。',
noSubscribeData: '请通过搜索添加电影、电视剧订阅。',
sharer: '分享人',
follow: '关注',
unfollow: '取消关注',
recognitionWords: '识别词',
cancelShare: '取消分享',
usageCount: '共 {count} 次复用',
},
recommend: {
all: '全部',
@@ -307,4 +317,52 @@ export default {
loadMore: '加载更多',
noMatchingResults: '没有匹配的结果',
},
calendar: {
episode: '第{number}集',
},
storage: {
usedPercent: '已使用 {percent}%',
},
site: {
noSites: '没有站点',
sitesWillBeShownHere: '已添加并支持的站点将会在这里显示。',
},
message: {
loadMore: '加载更多',
noMoreData: '没有更多数据',
},
logging: {
level: '级别',
time: '时间',
program: '程序',
content: '内容',
refreshing: '正在刷新',
},
moduleTest: {
normal: '正常',
disabled: '未启用',
error: '错误',
},
nameTest: {
recognize: '识别',
recognizing: '识别中...',
recognizeAgain: '重新识别',
title: '标题',
subtitle: '副标题',
},
netTest: {
notTested: '未测试',
testing: '测试中...',
normal: '正常',
},
ruleTest: {
test: '测试',
testing: '正在测试...',
testAgain: '重新测试',
title: '标题',
subtitle: '副标题',
ruleGroup: '规则组',
priority: '优先级:{value}',
noPriorityRule: '未命中任何优先级规则!',
},
}

View File

@@ -10,7 +10,7 @@ import logo from '@images/logo.png'
import { useTheme } from 'vuetify'
import { urlBase64ToUint8Array } from '@/@core/utils/navigator'
import { useI18n } from 'vue-i18n'
import { SUPPORTED_LOCALES, SupportedLocale } from '@/locales/types'
import { SUPPORTED_LOCALES, SupportedLocale } from '@/types/i18n'
import { getCurrentLocale, setI18nLanguage } from '@/plugins/i18n'
// 主题

View File

@@ -1,6 +1,6 @@
import { createI18n } from 'vue-i18n'
import { nextTick } from 'vue'
import { SUPPORTED_LOCALES, SupportedLocale } from '@/locales/types'
import { SUPPORTED_LOCALES, SupportedLocale } from '@/types/i18n'
// 导入语言文件
import zhCN from '@/locales/zh-CN'

View File

@@ -1,5 +1,6 @@
import zhCN from './zh-CN'
import zhCN from '@/locales/zh-CN'
// 导出类型和常量,而不是作为语言消息文件
export type MessageSchema = typeof zhCN
export type LocaleKey = keyof typeof zhCN

View File

@@ -5,6 +5,10 @@ import api from '@/api'
import trophy from '@images/misc/storage.png'
import triangleDark from '@images/misc/triangle-dark.png'
import triangleLight from '@images/misc/triangle-light.png'
import { useI18n } from 'vue-i18n'
// 国际化
const { t } = useI18n()
const { global } = useTheme()
@@ -52,13 +56,13 @@ onActivated(() => {
<template #append>
<VIcon class="cursor-move" v-if="hover.isHovering">mdi-drag</VIcon>
</template>
<VCardTitle>存储空间</VCardTitle>
<VCardTitle>{{ t('dashboard.storage') }}</VCardTitle>
</VCardItem>
<VCardText>
<h5 class="text-2xl font-weight-medium text-primary">
{{ formatFileSize(storage) }}
</h5>
<p class="mt-2">已使用 {{ usedPercent }}% 🚀</p>
<p class="mt-2">{{ t('storage.usedPercent', { percent: usedPercent }) }} 🚀</p>
<p class="mt-1">
<VProgressLinear :model-value="usedPercent" color="primary" />
</p>

View File

@@ -2,6 +2,10 @@
import api from '@/api'
import type { MediaServerConf, MediaServerLibrary } from '@/api/types'
import LibraryCard from '@/components/cards/LibraryCard.vue'
import { useI18n } from 'vue-i18n'
// 国际化
const { t } = useI18n()
// 媒体库列表
const libraryList = ref<MediaServerLibrary[]>([])
@@ -63,7 +67,7 @@ onActivated(() => {
<template #append>
<VIcon class="cursor-move" v-if="hover.isHovering">mdi-drag</VIcon>
</template>
<VCardTitle>我的媒体库</VCardTitle>
<VCardTitle>{{ t('dashboard.library') }}</VCardTitle>
</VCardItem>
<div class="grid gap-4 grid-backdrop-card mx-3" tabindex="0">
<LibraryCard v-for="item in libraryList" :key="item.id" :media="item" height="10rem" />

View File

@@ -7,6 +7,10 @@ import NoDataFound from '@/components/NoDataFound.vue'
import SiteAddEditDialog from '@/components/dialog/SiteAddEditDialog.vue'
import { useDisplay } from 'vuetify'
import { useDynamicButton } from '@/composables/useDynamicButton'
import { useI18n } from 'vue-i18n'
// 国际化
const { t } = useI18n()
// APP
const display = useDisplay()
@@ -98,7 +102,7 @@ useDynamicButton({
<template>
<div class="card-list-container">
<!-- 页面标题 -->
<VPageContentTitle title="站点管理" />
<VPageContentTitle :title="t('navItems.siteManager')" />
<LoadingBanner v-if="!isRefreshed" class="mt-12" />
<draggable
v-if="siteList.length > 0"
@@ -117,8 +121,8 @@ useDynamicButton({
<NoDataFound
v-if="siteList.length === 0 && isRefreshed"
error-code="404"
error-title="没有站点"
error-description="已添加并支持的站点将会在这里显示"
:error-title="t('site.noSites')"
:error-description="t('site.sitesWillBeShownHere')"
/>
<!-- 新增站点按钮 -->
<VFab

View File

@@ -9,6 +9,11 @@ import type { MediaInfo, Subscribe, TmdbEpisode } from '@/api/types'
import api from '@/api'
import { formatEp, parseDate } from '@/@core/utils/formatters'
import ProgressDialog from '@/components/dialog/ProgressDialog.vue'
import { useI18n } from 'vue-i18n'
import { getCurrentLocale } from '@/plugins/i18n'
// 国际化
const { t } = useI18n()
// 进度框
const progressDialog = ref(false)
@@ -19,10 +24,13 @@ const loading = ref(false)
// 已加载过
const isLoaded = ref(false)
// 获取当前语言
const currentLocale = getCurrentLocale().split('-')[0]
// 日历属性
const calendarOptions: Ref<CalendarOptions> = ref({
height: 'auto',
locale: 'zh-cn',
locale: currentLocale,
plugins: [
dayGridPlugin,
timeGridPlugin,
@@ -46,7 +54,7 @@ const calendarOptions: Ref<CalendarOptions> = ref({
async function eventsHander(subscribe: Subscribe) {
// 如果是电影直接返回
if (subscribe.type === '电影') {
if (subscribe.type === t('media.movie')) {
// 调用API查询TMDB详情
const movie: MediaInfo = await api.get(`media/tmdb:${subscribe.tmdbid}`, {
params: { type_name: subscribe.type },
@@ -160,14 +168,14 @@ onActivated(() => {
{{ arg.event.title }}
</VCardSubtitle>
<VCardText v-if="arg.event.extendedProps.subtitle" class="pa-0 px-2 break-words">
{{ arg.event.extendedProps.subtitle }}
{{ t('calendar.episode', { number: arg.event.extendedProps.subtitle }) }}
</VCardText>
</div>
</div>
</VCard>
</div>
<div class="md:hidden">
<VTooltip :text="`${arg.event.title} ${arg.event.extendedProps.subtitle} 集`">
<VTooltip :text="`${arg.event.title} ${t('calendar.episode', { number: arg.event.extendedProps.subtitle })}`">
<template #activator="{ props }">
<VImg
height="60"
@@ -198,7 +206,7 @@ onActivated(() => {
</div>
</template>
</FullCalendar>
<ProgressDialog v-if="progressDialog" v-model="progressDialog" text="正在加载 ..." />
<ProgressDialog v-if="progressDialog" v-model="progressDialog" :text="t('common.loading') + ' ...'" />
</template>
<style lang="scss">

View File

@@ -8,6 +8,10 @@ import SubscribeHistoryDialog from '@/components/dialog/SubscribeHistoryDialog.v
import { useUserStore } from '@/stores'
import { useDisplay } from 'vuetify'
import { useDynamicButton } from '@/composables/useDynamicButton'
import { useI18n } from 'vue-i18n'
// 国际化
const { t } = useI18n()
// APP
const display = useDisplay()
@@ -31,8 +35,8 @@ const props = defineProps({
let isRefreshed = ref(false)
// 顺序存储键值
const localOrderKey = props.type === '电影' ? 'MP_SUBSCRIBE_MOVIE_ORDER' : 'MP_SUBSCRIBE_TV_ORDER'
const orderRequestKey = props.type === '电影' ? 'SubscribeMovieOrder' : 'SubscribeTvOrder'
const localOrderKey = props.type === t('media.movie') ? 'MP_SUBSCRIBE_MOVIE_ORDER' : 'MP_SUBSCRIBE_TV_ORDER'
const orderRequestKey = props.type === t('media.movie') ? 'SubscribeMovieOrder' : 'SubscribeTvOrder'
// 刷新状态
const loading = ref(false)
@@ -160,7 +164,7 @@ useDynamicButton({
</script>
<template>
<VPageContentTitle v-if="keyword" :title="`筛选${keyword}`" />
<VPageContentTitle v-if="keyword" :title="`${t('subscribe.filterSubscriptions')}${keyword}`" />
<LoadingBanner v-if="!isRefreshed" class="mt-12" />
<draggable
v-if="displayList.length > 0"
@@ -178,8 +182,8 @@ useDynamicButton({
<NoDataFound
v-if="displayList.length === 0 && isRefreshed"
error-code="404"
error-title="没有数据"
:error-description="keyword ? '没有筛选到相关内容请更换筛选条件' : '请通过搜索添加电影电视剧订阅'"
:error-title="t('common.noData')"
:error-description="keyword ? t('subscribe.noFilterData') : t('subscribe.noSubscribeData')"
/>
<!-- 底部操作按钮 -->
<div v-if="isRefreshed">

View File

@@ -3,6 +3,10 @@ import api from '@/api'
import type { MediaInfo } from '@/api/types'
import MediaCard from '@/components/cards/MediaCard.vue'
import NoDataFound from '@/components/NoDataFound.vue'
import { useI18n } from 'vue-i18n'
// 国际化
const { t } = useI18n()
// 输入参数
const props = defineProps({
@@ -122,8 +126,8 @@ async function fetchData({ done }: { done: any }) {
<NoDataFound
v-if="dataList.length === 0 && isRefreshed"
error-code="404"
error-title="没有数据"
error-description="未获取到热门订阅数据未开启数据分享或服务器无法连接"
:error-title="t('common.noData')"
:error-description="t('subscribe.noPopularData')"
/>
</VInfiniteScroll>
</template>

View File

@@ -3,6 +3,10 @@ import api from '@/api'
import type { SubscribeShare } from '@/api/types'
import NoDataFound from '@/components/NoDataFound.vue'
import SubscribeShareCard from '@/components/cards/SubscribeShareCard.vue'
import { useI18n } from 'vue-i18n'
// 国际化
const { t } = useI18n()
// 定义输入参数
const props = defineProps({
@@ -115,7 +119,7 @@ function removeData(id: number) {
</script>
<template>
<VPageContentTitle v-if="keyword" :title="`搜索${keyword}`" />
<VPageContentTitle v-if="keyword" :title="`${t('common.search')}${keyword}`" />
<LoadingBanner v-if="!isRefreshed" class="mt-12" />
<VInfiniteScroll mode="intersect" side="end" :items="dataList" class="overflow-visible" @load="fetchData">
<template #loading />
@@ -128,10 +132,8 @@ function removeData(id: number) {
<NoDataFound
v-if="dataList.length === 0 && isRefreshed"
error-code="404"
error-title="没有数据"
:error-description="
keyword ? '没有搜索到相关内容请更换搜索关键词' : '未获取到分享订阅数据未开启数据分享或服务器无法连接'
"
:error-title="t('common.noData')"
:error-description="keyword ? t('common.noContent') : t('subscribe.noShareData')"
/>
</VInfiniteScroll>
</template>

View File

@@ -1,13 +1,18 @@
<script lang="ts" setup>
import { useI18n } from 'vue-i18n'
// 国际化
const { t } = useI18n()
// 已解析的日志列表
const parsedLogs = ref<{ level: string; time: string; program: string; content: string }[]>([])
// 表头
const headers = [
{ title: '级别', value: 'level' },
{ title: '时间', value: 'time' },
{ title: '程序', value: 'program' },
{ title: '内容', value: 'content' },
{ title: t('logging.level'), value: 'level' },
{ title: t('logging.time'), value: 'time' },
{ title: t('logging.program'), value: 'program' },
{ title: t('logging.content'), value: 'content' },
]
// SSE消息对象
@@ -73,7 +78,7 @@ onBeforeUnmount(() => {
</script>
<template>
<LoadingBanner v-if="parsedLogs.length === 0" class="mt-12" text="正在刷新 ..." />
<LoadingBanner v-if="parsedLogs.length === 0" class="mt-12" :text="t('logging.refreshing') + ' ...'" />
<div v-else>
<VTable class="table-rounded" hide-default-footer disable-sort>
<tbody>

View File

@@ -2,6 +2,10 @@
import type { Message } from '@/api/types'
import MessageCard from '@/components/cards/MessageCard.vue'
import api from '@/api'
import { useI18n } from 'vue-i18n'
// 国际化
const { t } = useI18n()
// 定义事件
const emit = defineEmits(['scroll'])
@@ -103,12 +107,12 @@ onBeforeUnmount(() => {
:items="messages"
class="overflow-visible"
@load="loadMessages"
load-more-text="加载更多 ..."
:load-more-text="t('message.loadMore') + ' ...'"
>
<template #loading>
<LoadingBanner />
</template>
<template #empty> 没有更多数据 </template>
<template #empty> {{ t('message.noMoreData') }} </template>
<div>
<div
v-for="(msg, index) in messages"

View File

@@ -1,5 +1,9 @@
<script setup lang="ts">
import api from '@/api'
import { useI18n } from 'vue-i18n'
// 国际化
const { t } = useI18n()
// 定义所有的模块ID、名称列表
const modules = ref<
@@ -41,13 +45,13 @@ async function moduleTest(index: number) {
target.loading = false
if (result.success) {
target.state = 'success'
target.name = `${target.name} - 正常`
target.name = `${target.name} - ${t('moduleTest.normal')}`
} else if (!result.message) {
target.state = undefined
target.name = `${target.name} - 未启用`
target.name = `${target.name} - ${t('moduleTest.disabled')}`
} else {
target.state = 'error'
target.name = `${target.name} - 错误`
target.name = `${target.name} - ${t('moduleTest.error')}`
target.errmsg = result.message
}
} catch (error) {

View File

@@ -4,6 +4,10 @@ import { requiredValidator } from '@/@validators'
import api from '@/api'
import type { Context } from '@/api/types'
import MediaInfoCard from '@/components/cards/MediaInfoCard.vue'
import { useI18n } from 'vue-i18n'
// 国际化
const { t } = useI18n()
// 识别结果
const nameTestResult = ref<Context>()
@@ -18,19 +22,18 @@ const nameTestForm = reactive({
const nameTestLoading = ref(false)
// 识别按钮文本
const nameTestText = ref('识别')
const nameTestText = ref(t('nameTest.recognize'))
// 是否显示结果
const showResult = ref(false)
// 调用API识别
async function nameTest() {
if (!nameTestForm.title)
return
if (!nameTestForm.title) return
try {
nameTestLoading.value = true
nameTestText.value = '识别中...'
nameTestText.value = t('nameTest.recognizing')
showResult.value = false
nameTestResult.value = await api.get('media/recognize', {
params: {
@@ -39,10 +42,9 @@ async function nameTest() {
},
})
nameTestLoading.value = false
nameTestText.value = '重新识别'
nameTestText.value = t('nameTest.recognizeAgain')
showResult.value = true
}
catch (error) {
} catch (error) {
console.error(error)
}
}
@@ -52,30 +54,15 @@ async function nameTest() {
<VForm @submit.prevent="() => {}">
<VRow class="pt-2">
<VCol cols="12">
<VTextField
v-model="nameTestForm.title"
label="标题"
:rules="[requiredValidator]"
/>
<VTextField v-model="nameTestForm.title" :label="t('nameTest.title')" :rules="[requiredValidator]" />
</VCol>
<VCol cols="12">
<VTextarea
v-model="nameTestForm.subtitle"
label="副标题"
rows="2"
auto-grow
/>
<VTextarea v-model="nameTestForm.subtitle" :label="t('nameTest.subtitle')" rows="2" auto-grow />
</VCol>
</VRow>
<VRow>
<VCol
cols="12"
class="text-center"
>
<VBtn
:disabled="nameTestLoading"
@click="nameTest"
>
<VCol cols="12" class="text-center">
<VBtn :disabled="nameTestLoading" @click="nameTest">
<template #prepend>
<VIcon icon="mdi-text-recognition" />
</template>

View File

@@ -8,6 +8,10 @@ import tmdb from '@images/logos/tmdb.png'
import wechat from '@images/logos/wechat.png'
import fanart from '@images/logos/fanart.webp'
import tvdb from '@images/logos/thetvdb.jpeg'
import { useI18n } from 'vue-i18n'
// 国际化
const { t } = useI18n()
interface Status {
OK: string
@@ -46,7 +50,7 @@ const targets = ref<Address[]>([
proxy: true,
status: 'Normal',
time: '',
message: '未测试',
message: t('netTest.notTested'),
btndisable: false,
},
{
@@ -56,7 +60,7 @@ const targets = ref<Address[]>([
proxy: true,
status: 'Normal',
time: '',
message: '未测试',
message: t('netTest.notTested'),
btndisable: false,
},
{
@@ -66,7 +70,7 @@ const targets = ref<Address[]>([
proxy: true,
status: 'Normal',
time: '',
message: '未测试',
message: t('netTest.notTested'),
btndisable: false,
},
{
@@ -76,7 +80,7 @@ const targets = ref<Address[]>([
proxy: true,
status: 'Normal',
time: '',
message: '未测试',
message: t('netTest.notTested'),
btndisable: false,
},
{
@@ -86,7 +90,7 @@ const targets = ref<Address[]>([
proxy: true,
status: 'Normal',
time: '',
message: '未测试',
message: t('netTest.notTested'),
btndisable: false,
},
{
@@ -96,7 +100,7 @@ const targets = ref<Address[]>([
proxy: false,
status: 'Normal',
time: '',
message: '未测试',
message: t('netTest.notTested'),
btndisable: false,
},
{
@@ -106,7 +110,7 @@ const targets = ref<Address[]>([
proxy: false,
status: 'Normal',
time: '',
message: '未测试',
message: t('netTest.notTested'),
btndisable: false,
},
{
@@ -116,7 +120,7 @@ const targets = ref<Address[]>([
proxy: false,
status: 'Normal',
time: '',
message: '未测试',
message: t('netTest.notTested'),
btndisable: false,
},
{
@@ -126,7 +130,7 @@ const targets = ref<Address[]>([
proxy: true,
status: 'Normal',
time: '',
message: '未测试',
message: t('netTest.notTested'),
btndisable: false,
},
{
@@ -136,7 +140,7 @@ const targets = ref<Address[]>([
proxy: true,
status: 'Normal',
time: '',
message: '未测试',
message: t('netTest.notTested'),
btndisable: false,
},
{
@@ -146,7 +150,7 @@ const targets = ref<Address[]>([
proxy: true,
status: 'Normal',
time: '',
message: '未测试',
message: t('netTest.notTested'),
btndisable: false,
},
])
@@ -165,7 +169,7 @@ async function netTest(index: number) {
target.btndisable = true
target.status = 'Doing'
target.message = '测试中...'
target.message = t('netTest.testing')
const result: { [key: string]: any } = await api.get('system/nettest', {
params: {
@@ -176,7 +180,7 @@ async function netTest(index: number) {
if (result.success) {
target.status = 'OK'
target.message = '正常'
target.message = t('netTest.normal')
} else {
target.status = 'Fail'
target.message = result.message

View File

@@ -3,6 +3,10 @@ import { reactive, ref } from 'vue'
import { requiredValidator } from '@/@validators'
import api from '@/api'
import { FilterRuleGroup } from '@/api/types'
import { useI18n } from 'vue-i18n'
// 国际化
const { t } = useI18n()
// 识别结果
const ruleTestResult = ref('')
@@ -18,7 +22,7 @@ const ruleTestForm = reactive({
const ruleTestLoading = ref(false)
// 识别按钮文本
const ruleTestText = ref('测试')
const ruleTestText = ref(t('ruleTest.test'))
// 是否显示结果
const showResult = ref(false)
@@ -47,7 +51,7 @@ async function ruleTest() {
try {
ruleTestLoading.value = true
ruleTestText.value = '正在测试...'
ruleTestText.value = t('ruleTest.testing')
showResult.value = false
const result: { [key: string]: any } = await api.get('system/ruletest', {
params: {
@@ -56,11 +60,11 @@ async function ruleTest() {
rulegroup_name: ruleTestForm.rulegroup,
},
})
if (result.success) ruleTestResult.value = `优先级:${result.data.priority}`
else ruleTestResult.value = '未命中任何优先级规则!'
if (result.success) ruleTestResult.value = t('ruleTest.priority', { value: result.data.priority })
else ruleTestResult.value = t('ruleTest.noPriorityRule')
ruleTestLoading.value = false
ruleTestText.value = '重新测试'
ruleTestText.value = t('ruleTest.testAgain')
showResult.value = true
} catch (error) {
console.error(error)
@@ -76,13 +80,13 @@ onMounted(() => {
<VForm @submit.prevent="() => {}">
<VRow class="pt-2">
<VCol cols="12" md="8">
<VTextField v-model="ruleTestForm.title" label="标题" :rules="[requiredValidator]" />
<VTextField v-model="ruleTestForm.title" :label="t('ruleTest.title')" :rules="[requiredValidator]" />
</VCol>
<VCol cols="12" md="4">
<VSelect v-model="ruleTestForm.rulegroup" label="规则组" :items="filterRuleGroupItems" />
<VSelect v-model="ruleTestForm.rulegroup" :label="t('ruleTest.ruleGroup')" :items="filterRuleGroupItems" />
</VCol>
<VCol cols="12">
<VTextarea v-model="ruleTestForm.subtitle" label="副标题" rows="2" auto-grow />
<VTextarea v-model="ruleTestForm.subtitle" :label="t('ruleTest.subtitle')" rows="2" auto-grow />
</VCol>
</VRow>
<VRow>

View File

@@ -29,7 +29,7 @@ export default defineConfig({
vueTemplate: true,
}),
VueI18n({
include: [resolve(__dirname, 'src/locales/**')],
include: [resolve(__dirname, 'src/locales/*.ts')],
}),
VitePWA({
injectRegister: 'script',