更新国际化支持:为媒体服务器卡片和相关组件添加多语言文本,提升用户体验

This commit is contained in:
jxxghp
2025-04-28 20:05:49 +08:00
parent 40711fa640
commit 995e07c351
5 changed files with 169 additions and 64 deletions

View File

@@ -7,6 +7,10 @@ import plex_image from '@images/logos/plex.png'
import trimemedia_image from '@images/logos/trimemedia.png'
import api from '@/api'
import { cloneDeep } from 'lodash-es'
import { useI18n } from 'vue-i18n'
// 获取i18n实例
const { t } = useI18n()
// 定义输入
const props = defineProps({
@@ -32,17 +36,17 @@ const emit = defineEmits(['close', 'done', 'change'])
const infoItems = ref([
{
avatar: 'mdi-movie-roll',
title: '电影',
title: t('mediaType.movie'),
amount: '0',
},
{
avatar: 'mdi-television-box',
title: '电视剧',
title: t('mediaType.tv'),
amount: '0',
},
{
avatar: 'mdi-account',
title: '用户',
title: t('common.user'),
amount: '0',
},
])
@@ -50,7 +54,7 @@ const infoItems = ref([
// 同步媒体库选项
const librariesOptions = ref<{ title: string; value: string | undefined }[]>([
{
title: '全部',
title: t('common.all'),
value: 'all',
},
])
@@ -81,12 +85,12 @@ function openMediaServerInfoDialog() {
function saveMediaServerInfo() {
// 为空不保存,跳出警告框
if (!mediaServerInfo.value.name) {
$toast.error('名称不能为空,请输入后再确定')
$toast.error(t('common.nameRequired'))
return
}
// 重名判断
if (props.mediaservers.some(item => item.name === mediaServerInfo.value.name && item !== props.mediaserver)) {
$toast.error(`${mediaServerInfo.value.name}】已存在,请替换为其他名称`)
$toast.error(t('common.nameExists', { name: mediaServerInfo.value.name }))
return
}
// 执行保存
@@ -127,17 +131,17 @@ async function loadMediaStatistic() {
infoItems.value = [
{
avatar: 'mdi-movie-roll',
title: '电影',
title: t('mediaType.movie'),
amount: res.movie_count.toLocaleString(),
},
{
avatar: 'mdi-television-box',
title: '电视剧',
title: t('mediaType.tv'),
amount: res.tv_count.toLocaleString(),
},
{
avatar: 'mdi-account',
title: '用户',
title: t('common.user'),
amount: res.user_count.toLocaleString(),
},
]
@@ -160,7 +164,7 @@ async function loadLibrary(server: string) {
librariesOptions.value = []
}
librariesOptions.value.unshift({
title: '全部',
title: t('common.all'),
value: 'all',
})
} catch (e) {
@@ -189,23 +193,23 @@ onMounted(() => {
</VCardText>
</VCard>
<VDialog v-if="mediaServerInfoDialog" v-model="mediaServerInfoDialog" scrollable max-width="40rem" persistent>
<VCard :title="`${props.mediaserver.name} - 配置`" class="rounded-t">
<VCard :title="`${props.mediaserver.name} - ${t('common.config')}`" class="rounded-t">
<VDialogCloseBtn v-model="mediaServerInfoDialog" />
<VDivider />
<VCardText>
<VForm>
<VRow>
<VCol cols="12" md="6">
<VSwitch v-model="mediaServerInfo.enabled" label="启用媒体服务器" />
<VSwitch v-model="mediaServerInfo.enabled" :label="t('mediaserver.enableMediaServer')" />
</VCol>
</VRow>
<VRow v-if="mediaServerInfo.type == 'emby'">
<VCol cols="12" md="6">
<VTextField
v-model="mediaServerInfo.name"
label="名称"
placeholder="必填;不可与其他名称重名"
hint="媒体服务器的别名"
:label="t('common.name')"
:placeholder="t('mediaserver.nameRequired')"
:hint="t('mediaserver.serverAlias')"
persistent-hint
active
/>
@@ -213,9 +217,9 @@ onMounted(() => {
<VCol cols="12" md="6">
<VTextField
v-model="mediaServerInfo.config.host"
label="地址"
placeholder="http(s)://ip:port"
hint="服务端地址格式http(s)://ip:port"
:label="t('mediaserver.host')"
:placeholder="t('mediaserver.hostPlaceholder')"
:hint="t('mediaserver.hostHint')"
persistent-hint
active
/>
@@ -223,9 +227,9 @@ onMounted(() => {
<VCol cols="12" md="6">
<VTextField
v-model="mediaServerInfo.config.play_host"
label="外网播放地址"
placeholder="http(s)://domain:port"
hint="跳转播放页面使用的地址格式http(s)://domain:port"
:label="t('mediaserver.playHost')"
:placeholder="t('mediaserver.playHostPlaceholder')"
:hint="t('mediaserver.playHostHint')"
persistent-hint
active
/>
@@ -233,8 +237,8 @@ onMounted(() => {
<VCol cols="12" md="6">
<VTextField
v-model="mediaServerInfo.config.apikey"
label="API密钥"
hint="Emby设置->高级->API密钥中生成的密钥"
:label="t('mediaserver.apiKey')"
:hint="t('mediaserver.embyApiKeyHint')"
persistent-hint
active
/>
@@ -244,9 +248,9 @@ onMounted(() => {
<VCol cols="12" md="6">
<VTextField
v-model="mediaServerInfo.name"
label="名称"
placeholder="必填;不可与其他名称重名"
hint="媒体服务器的别名"
:label="t('common.name')"
:placeholder="t('mediaserver.nameRequired')"
:hint="t('mediaserver.serverAlias')"
persistent-hint
active
/>
@@ -254,9 +258,9 @@ onMounted(() => {
<VCol cols="12" md="6">
<VTextField
v-model="mediaServerInfo.config.host"
label="地址"
placeholder="http(s)://ip:port"
hint="服务端地址格式http(s)://ip:port"
:label="t('mediaserver.host')"
:placeholder="t('mediaserver.hostPlaceholder')"
:hint="t('mediaserver.hostHint')"
persistent-hint
active
/>
@@ -264,9 +268,9 @@ onMounted(() => {
<VCol cols="12" md="6">
<VTextField
v-model="mediaServerInfo.config.play_host"
label="外网播放地址"
placeholder="http(s)://domain:port"
hint="跳转播放页面使用的地址格式http(s)://domain:port"
:label="t('mediaserver.playHost')"
:placeholder="t('mediaserver.playHostPlaceholder')"
:hint="t('mediaserver.playHostHint')"
persistent-hint
active
/>
@@ -274,8 +278,8 @@ onMounted(() => {
<VCol cols="12" md="6">
<VTextField
v-model="mediaServerInfo.config.apikey"
label="API密钥"
hint="Jellyfin设置->高级->API密钥中生成的密钥"
:label="t('mediaserver.apiKey')"
:hint="t('mediaserver.jellyfinApiKeyHint')"
persistent-hint
active
/>
@@ -285,9 +289,9 @@ onMounted(() => {
<VCol cols="12" md="6">
<VTextField
v-model="mediaServerInfo.name"
label="名称"
placeholder="必填;不可与其他名称重名"
hint="媒体服务器的别名"
:label="t('common.name')"
:placeholder="t('mediaserver.nameRequired')"
:hint="t('mediaserver.serverAlias')"
persistent-hint
active
/>
@@ -295,9 +299,9 @@ onMounted(() => {
<VCol cols="12" md="6">
<VTextField
v-model="mediaServerInfo.config.host"
label="地址"
placeholder="http(s)://ip:port"
hint="服务端地址格式http(s)://ip:port"
:label="t('mediaserver.host')"
:placeholder="t('mediaserver.hostPlaceholder')"
:hint="t('mediaserver.hostHint')"
persistent-hint
active
/>
@@ -305,27 +309,32 @@ onMounted(() => {
<VCol cols="12">
<VTextField
v-model="mediaServerInfo.config.play_host"
label="外网播放地址"
placeholder="http(s)://domain:port"
hint="跳转播放页面使用的地址格式http(s)://domain:port"
:label="t('mediaserver.playHost')"
:placeholder="t('mediaserver.playHostPlaceholder')"
:hint="t('mediaserver.playHostHint')"
persistent-hint
active
/>
</VCol>
<VCol cols="12" md="6">
<VTextField v-model="mediaServerInfo.config.username" label="用户名" active />
<VTextField v-model="mediaServerInfo.config.username" :label="t('mediaserver.username')" active />
</VCol>
<VCol cols="12" md="6">
<VTextField type="password" v-model="mediaServerInfo.config.password" label="密码" active />
<VTextField
type="password"
v-model="mediaServerInfo.config.password"
:label="t('mediaserver.password')"
active
/>
</VCol>
</VRow>
<VRow v-if="mediaServerInfo.type == 'plex'">
<VCol cols="12" md="6">
<VTextField
v-model="mediaServerInfo.name"
label="名称"
placeholder="必填;不可与其他名称重名"
hint="媒体服务器的别名"
:label="t('common.name')"
:placeholder="t('mediaserver.nameRequired')"
:hint="t('mediaserver.serverAlias')"
persistent-hint
active
/>
@@ -333,9 +342,9 @@ onMounted(() => {
<VCol cols="12" md="6">
<VTextField
v-model="mediaServerInfo.config.host"
label="地址"
placeholder="http(s)://ip:port"
hint="服务端地址格式http(s)://ip:port"
:label="t('mediaserver.host')"
:placeholder="t('mediaserver.hostPlaceholder')"
:hint="t('mediaserver.hostHint')"
persistent-hint
active
/>
@@ -343,9 +352,9 @@ onMounted(() => {
<VCol cols="12" md="6">
<VTextField
v-model="mediaServerInfo.config.play_host"
label="外网播放地址"
placeholder="http(s)://domain:port"
hint="跳转播放页面使用的地址格式http(s)://domain:port"
:label="t('mediaserver.playHost')"
:placeholder="t('mediaserver.playHostPlaceholder')"
:hint="t('mediaserver.playHostHint')"
persistent-hint
active
/>
@@ -353,8 +362,8 @@ onMounted(() => {
<VCol cols="12" md="6">
<VTextField
v-model="mediaServerInfo.config.token"
label="X-Plex-Token"
hint="浏览器F12->网络从Plex请求URL中获取的X-Plex-Token"
:label="t('mediaserver.plexToken')"
:hint="t('mediaserver.plexTokenHint')"
persistent-hint
active
/>
@@ -364,12 +373,12 @@ onMounted(() => {
<VCol cols="12">
<VSelect
v-model="mediaServerInfo.sync_libraries"
label="同步媒体库"
:label="t('mediaserver.syncLibraries')"
:items="librariesOptions"
chips
multiple
clearable
hint="只有选中的媒体库才会被同步"
:hint="t('mediaserver.syncLibrariesHint')"
persistent-hint
active
append-inner-icon="mdi-refresh"
@@ -381,7 +390,7 @@ onMounted(() => {
</VCardText>
<VCardActions class="pt-3">
<VBtn @click="saveMediaServerInfo" variant="elevated" prepend-icon="mdi-content-save" class="px-5">
确定
{{ t('common.confirm') }}
</VBtn>
</VCardActions>
</VCard>

View File

@@ -42,11 +42,6 @@ function handleFlow(item: Workflow) {
flowDialog.value = true
}
// 计算已完成的动作数
function resolveDoneActions(item: Workflow) {
return item.current_action?.split(',').length || 0
}
// 编辑完成
function editDone() {
editDialog.value = false

View File

@@ -25,6 +25,29 @@ export default {
language: 'Language',
pleaseWait: 'Please wait...',
viewDetails: 'View Details',
user: 'User',
config: 'Configuration',
},
mediaserver: {
enableMediaServer: 'Enable Media Server',
nameRequired: 'Required; cannot be duplicated',
serverAlias: 'Media server alias',
host: 'Host',
hostPlaceholder: 'http(s)://ip:port',
hostHint: 'Server address, format: http(s)://ip:port',
playHost: 'External Playback URL',
playHostPlaceholder: 'http(s)://domain:port',
playHostHint: 'URL for playback page redirection, format: http(s)://domain:port',
apiKey: 'API Key',
embyApiKeyHint: 'API key generated in Emby Settings -> Advanced -> API Keys',
jellyfinApiKeyHint: 'API key generated in Jellyfin Settings -> Advanced -> API Keys',
plexToken: 'X-Plex-Token',
plexTokenHint: 'X-Plex-Token obtained from Plex request URL in browser F12 -> Network',
username: 'Username',
password: 'Password',
syncLibraries: 'Sync Libraries',
syncLibrariesHint: 'Only selected libraries will be synchronized',
nameExists: '【{name}】 already exists, please use a different name',
},
mediaType: {
movie: 'Movie',

View File

@@ -25,6 +25,29 @@ export default {
language: '语言',
pleaseWait: '请稍候...',
viewDetails: '查看详情',
user: '用户',
config: '配置',
},
mediaserver: {
enableMediaServer: '启用媒体服务器',
nameRequired: '必填;不可与其他名称重名',
serverAlias: '媒体服务器的别名',
host: '地址',
hostPlaceholder: 'http(s)://ip:port',
hostHint: '服务端地址格式http(s)://ip:port',
playHost: '外网播放地址',
playHostPlaceholder: 'http(s)://domain:port',
playHostHint: '跳转播放页面使用的地址格式http(s)://domain:port',
apiKey: 'API密钥',
embyApiKeyHint: 'Emby设置->高级->API密钥中生成的密钥',
jellyfinApiKeyHint: 'Jellyfin设置->高级->API密钥中生成的密钥',
plexToken: 'X-Plex-Token',
plexTokenHint: '浏览器F12->网络从Plex请求URL中获取的X-Plex-Token',
username: '用户名',
password: '密码',
syncLibraries: '同步媒体库',
syncLibrariesHint: '只有选中的媒体库才会被同步',
nameExists: '【{name}】已存在,请替换为其他名称',
},
mediaType: {
movie: '电影',
@@ -235,6 +258,38 @@ export default {
dragToCanvas: '拖动到画布',
tapComponentHint: '点击组件添加到画布',
dragComponentHint: '拖动组件到画布',
task: {
edit: '编辑任务',
continue: '继续执行',
restart: '重新执行',
run: '立即执行',
reset: '重置任务',
delete: '删除任务',
confirmDelete: '是否确认删除任务 {name} ?',
confirmReset: '是否确认重置任务 {name} ?',
deleteSuccess: '删除任务成功!',
deleteFailed: '删除任务失败:{message}',
enableSuccess: '启用任务成功!',
enableFailed: '启用任务失败:{message}',
pauseSuccess: '停用任务成功!',
pauseFailed: '停用任务失败:{message}',
runSuccess: '任务执行完成!',
runFailed: '任务执行失败:{message}',
resetSuccess: '重置任务成功!',
resetFailed: '重置任务失败:{message}',
status: {
success: '成功',
running: '运行中',
failed: '失败',
paused: '暂停',
waiting: '等待'
},
info: {
timer: '定时',
status: '状态',
actionCount: '动作数',
runCount: '已执行次数',
progress: '进度',
},
dashboard: {
storage: '存储空间',

View File

@@ -25,6 +25,29 @@ export default {
language: '語言',
pleaseWait: '請稍候...',
viewDetails: '查看詳情',
user: '用戶',
config: '配置',
},
mediaserver: {
enableMediaServer: '啟用媒體伺服器',
nameRequired: '必填;不可與其他名稱重名',
serverAlias: '媒體伺服器的別名',
host: '地址',
hostPlaceholder: 'http(s)://ip:port',
hostHint: '服務端地址格式http(s)://ip:port',
playHost: '外網播放地址',
playHostPlaceholder: 'http(s)://domain:port',
playHostHint: '跳轉播放頁面使用的地址格式http(s)://domain:port',
apiKey: 'API密鑰',
embyApiKeyHint: 'Emby設置->高級->API密鑰中生成的密鑰',
jellyfinApiKeyHint: 'Jellyfin設置->高級->API密鑰中生成的密鑰',
plexToken: 'X-Plex-Token',
plexTokenHint: '瀏覽器F12->網絡從Plex請求URL中獲取的X-Plex-Token',
username: '用戶名',
password: '密碼',
syncLibraries: '同步媒體庫',
syncLibrariesHint: '只有選中的媒體庫才會被同步',
nameExists: '【{name}】已存在,請替換為其他名稱',
},
mediaType: {
movie: '電影',