2
0
mirror of https://github.com/acepanel/panel.git synced 2026-02-04 09:13:49 +08:00

feat: 前端支持翻译

This commit is contained in:
2025-04-12 23:01:09 +08:00
parent 72c3bed203
commit 7e0ad2c799
11 changed files with 218 additions and 29 deletions

View File

@@ -1,7 +0,0 @@
module.exports = {
output: {
path: './src/locales',
potPath: './frontend.pot',
locales: ['en', 'zh_CN', 'zh_TW']
}
}

62
web/gettext.config.mjs Normal file
View File

@@ -0,0 +1,62 @@
export default {
input: {
include: ['**/*.js', '**/*.ts', '**/*.vue'],
exclude: ['utils/gettext/**'],
jsExtractorOpts: [
{
keyword: '__', // $gettext
options: {
content: {
replaceNewLines: '\n'
},
arguments: {
text: 0
}
}
},
{
keyword: '_n', // $ngettext
options: {
content: {
replaceNewLines: '\n'
},
arguments: {
text: 0,
textPlural: 1
}
}
},
{
keyword: '_x', // $pgettext
options: {
content: {
replaceNewLines: '\n'
},
arguments: {
context: 0,
text: 1
}
}
},
{
keyword: '_nx', // $npgettext
options: {
content: {
replaceNewLines: '\n'
},
arguments: {
context: 0,
text: 1,
textPlural: 2
}
}
}
]
},
output: {
path: './src/locales',
potPath: './frontend.pot',
locales: ['en', 'zh_CN', 'zh_TW'],
linguas: false
}
}

View File

@@ -1,5 +1,4 @@
import type { App } from 'vue'
import type { Composer } from 'vue-i18n'
import { createI18n } from 'vue-i18n'
import { useThemeStore } from '@/store'
@@ -24,7 +23,3 @@ export function setupI18n(app: App) {
})
app.use(i18n)
}
export const trans = (key: string, attributes = {}) => {
return (i18n.global.t as Composer['t'])(key, attributes)
}

30
web/src/locales/en.po Normal file
View File

@@ -0,0 +1,30 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Language: en\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: src/views/setting/SettingHttps.vue:25
msgid "Saved successfully"
msgstr "Saved successfully"
#: src/views/setting/SettingHttps.vue:34
msgid "Panel HTTPS"
msgstr "Panel HTTPS"
#: src/views/setting/SettingHttps.vue:37
msgid "Certificate"
msgstr "Certificate"
#: src/views/setting/SettingHttps.vue:44
msgid "Private Key"
msgstr "Private Key"
#: src/views/setting/SettingHttps.vue:54
msgid "Save"
msgstr "Save"

View File

@@ -0,0 +1,23 @@
msgid ""
msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
#: src/views/setting/SettingHttps.vue:25
msgid "Saved successfully"
msgstr ""
#: src/views/setting/SettingHttps.vue:34
msgid "Panel HTTPS"
msgstr ""
#: src/views/setting/SettingHttps.vue:37
msgid "Certificate"
msgstr ""
#: src/views/setting/SettingHttps.vue:44
msgid "Private Key"
msgstr ""
#: src/views/setting/SettingHttps.vue:54
msgid "Save"
msgstr ""

29
web/src/locales/zh_CN.po Normal file
View File

@@ -0,0 +1,29 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Language: zh_CN\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: src/views/setting/SettingHttps.vue:25
msgid "Saved successfully"
msgstr ""
#: src/views/setting/SettingHttps.vue:34
msgid "Panel HTTPS"
msgstr ""
#: src/views/setting/SettingHttps.vue:37
msgid "Certificate"
msgstr ""
#: src/views/setting/SettingHttps.vue:44
msgid "Private Key"
msgstr ""
#: src/views/setting/SettingHttps.vue:54
msgid "Save"
msgstr ""

29
web/src/locales/zh_TW.po Normal file
View File

@@ -0,0 +1,29 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Language: zh_TW\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: src/views/setting/SettingHttps.vue:25
msgid "Saved successfully"
msgstr ""
#: src/views/setting/SettingHttps.vue:34
msgid "Panel HTTPS"
msgstr ""
#: src/views/setting/SettingHttps.vue:37
msgid "Certificate"
msgstr ""
#: src/views/setting/SettingHttps.vue:44
msgid "Private Key"
msgstr ""
#: src/views/setting/SettingHttps.vue:54
msgid "Save"
msgstr ""

View File

@@ -9,7 +9,7 @@ import App from './App.vue'
import { setupI18n } from '@/i18n/i18n'
import { setupRouter } from '@/router'
import { setupStore, useThemeStore } from '@/store'
import { setupGettext, setupNaiveDiscreteApi } from '@/utils'
import { createGettext, setupNaiveDiscreteApi } from '@/utils'
import { install as VueMonacoEditorPlugin } from '@guolao/vue-monaco-editor'
@@ -30,7 +30,7 @@ async function setupApp() {
await setupStore(app)
await setupNaiveDiscreteApi()
await setupPanel().then(() => {
setupGettext(app)
app.use(createGettext)
setupI18n(app)
})
await setupRouter(app)

View File

@@ -1,13 +1,12 @@
import type { Router } from 'vue-router'
import { trans } from '@/i18n/i18n'
import { useThemeStore } from '@/store'
export function createPageTitleGuard(router: Router) {
const themeStore = useThemeStore()
router.afterEach((to) => {
const pageTitle = String(to.meta.title)
if (pageTitle) document.title = `${trans(pageTitle)} | ${themeStore.name}`
if (pageTitle) document.title = `${pageTitle} | ${themeStore.name}`
else document.title = themeStore.name
})
}

View File

@@ -1,10 +1,41 @@
import type { App } from 'vue'
import { createGettext } from 'vue3-gettext'
import { createGettext as vue3Gettext } from 'vue3-gettext'
let gettext: ReturnType<typeof createGettext>
export let gettext: ReturnType<typeof vue3Gettext>
export function $gettext(msgid: string, params?: Record<string, string | number>) {
return gettext.$gettext(msgid, params)
}
export function $ngettext(
msgid: string,
plural: string,
n: number,
params?: Record<string, string | number>
) {
return gettext.$ngettext(msgid, plural, n, params)
}
export function setupGettext(app: App) {
gettext = createGettext({
gettext = vue3Gettext({
availableLanguages: {
en: 'English',
zh_CN: '简体中文',
zh_TW: '繁體中文'
},
defaultLanguage: 'zh_CN',
globalProperties: {
gettext: ['$gettext', '__'], // 这样支持同时使用 $gettext, __ 两种方式
ngettext: ['$ngettext', '_n'],
pgettext: ['$pgettext', '_x'],
npgettext: ['$npgettext', '_nx']
}
})
app.use(gettext)
}
export function createGettext(): any {
gettext = vue3Gettext({
availableLanguages: {
en: 'English',
zh_CN: '简体中文',
@@ -12,5 +43,6 @@ export function setupGettext(app: App) {
},
defaultLanguage: 'zh_CN'
})
app.use(gettext)
return gettext
}

View File

@@ -1,9 +1,6 @@
<script setup lang="ts">
import { useI18n } from 'vue-i18n'
import setting from '@/api/panel/setting'
const { t } = useI18n()
import { $gettext } from '@/utils/gettext'
const { data: model } = useRequest(setting.list, {
initialData: {
@@ -25,7 +22,7 @@ const { data: model } = useRequest(setting.list, {
const handleSave = () => {
useRequest(setting.update(model.value)).onSuccess(() => {
window.$message.success(t('settingIndex.edit.toasts.success'))
window.$message.success($gettext('Saved successfully'))
})
}
</script>
@@ -34,17 +31,17 @@ const handleSave = () => {
<n-space vertical>
<n-alert type="warning"> 错误的证书可能导致面板无法访问请谨慎操作</n-alert>
<n-form>
<n-form-item :label="$t('settingIndex.edit.fields.https.label')">
<n-form-item :label="$gettext('Panel HTTPS')">
<n-switch v-model:value="model.https" />
</n-form-item>
<n-form-item v-if="model.https" :label="$t('settingIndex.edit.fields.cert.label')">
<n-form-item v-if="model.https" :label="$gettext('Certificate')">
<n-input
v-model:value="model.cert"
type="textarea"
:autosize="{ minRows: 10, maxRows: 15 }"
/>
</n-form-item>
<n-form-item v-if="model.https" :label="$t('settingIndex.edit.fields.key.label')">
<n-form-item v-if="model.https" :label="$gettext('Private Key')">
<n-input
v-model:value="model.key"
type="textarea"
@@ -54,7 +51,7 @@ const handleSave = () => {
</n-form>
</n-space>
<n-button type="primary" @click="handleSave">
{{ $t('settingIndex.edit.actions.submit') }}
{{ $gettext('Save') }}
</n-button>
</template>