feat: add favicon upload functionality to settings panel

- Introduced a new `webFavicon` property in the AllSetting model to store the favicon data URL.
- Implemented upload and delete functionality for the favicon in the GeneralTab component, including validation for file type and size.
- Updated the backend to serve the favicon in the HTML head if set.
- Added translations for favicon-related messages across multiple languages.
This commit is contained in:
Alex.Petrov 2026-05-15 18:02:32 +05:00
parent 5a1019534f
commit a6f7fe547b
No known key found for this signature in database
GPG key ID: 391C927BB73C9BB8
18 changed files with 246 additions and 15 deletions

View file

@ -14,6 +14,7 @@ export class AllSetting {
this.webCertFile = ""; this.webCertFile = "";
this.webKeyFile = ""; this.webKeyFile = "";
this.webBasePath = "/"; this.webBasePath = "/";
this.webFavicon = "";
this.sessionMaxAge = 360; this.sessionMaxAge = 360;
this.trustedProxyCIDRs = "127.0.0.1/32,::1/128"; this.trustedProxyCIDRs = "127.0.0.1/32,::1/128";
this.pageSize = 25; this.pageSize = 25;

View file

@ -1,6 +1,8 @@
<script setup> <script setup>
import { computed, onMounted, ref } from 'vue'; import { computed, onMounted, ref } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { DeleteOutlined, UploadOutlined } from '@ant-design/icons-vue';
import { message } from 'ant-design-vue';
import { HttpUtil, LanguageManager } from '@/utils'; import { HttpUtil, LanguageManager } from '@/utils';
import SettingListItem from '@/components/SettingListItem.vue'; import SettingListItem from '@/components/SettingListItem.vue';
@ -77,6 +79,10 @@ const ldapInboundTagList = computed({
}); });
const inboundOptions = ref([]); const inboundOptions = ref([]);
const faviconPreview = computed(() => props.allSetting.webFavicon);
const faviconMaxSize = 256 * 1024;
const faviconMimeTypes = ['image/png', 'image/x-icon', 'image/vnd.microsoft.icon'];
async function loadInboundTags() { async function loadInboundTags() {
const msg = await HttpUtil.get('/panel/api/inbounds/list'); const msg = await HttpUtil.get('/panel/api/inbounds/list');
if (msg?.success && Array.isArray(msg.obj)) { if (msg?.success && Array.isArray(msg.obj)) {
@ -89,6 +95,57 @@ async function loadInboundTags() {
} }
} }
const uploadFavicon = async ({ file, onSuccess, onError }) => {
try {
const dataUrl = await fileToFaviconDataUrl(file);
props.allSetting.webFavicon = dataUrl;
onSuccess?.();
} catch (error) {
message.error(error?.message || t('pages.settings.toasts.faviconUploadFailed'));
onError?.(error);
}
};
const deleteFavicon = () => {
props.allSetting.webFavicon = '';
};
const fileToFaviconDataUrl = (file) => new Promise((resolve, reject) => {
if (!file) {
reject(new Error(t('pages.settings.toasts.faviconUploadFailed')));
return;
}
const ext = file.name?.split('.').pop()?.toLowerCase();
const mime = file.type || (ext === 'ico' ? 'image/x-icon' : '');
if (!faviconMimeTypes.includes(mime)) {
reject(new Error(t('pages.settings.toasts.faviconUploadUnsupported')));
return;
}
if (file.size > faviconMaxSize) {
reject(new Error(t('pages.settings.toasts.faviconUploadTooLarge')));
return;
}
const reader = new FileReader();
reader.onload = () => {
const result = String(reader.result || '');
if (result) {
resolve(result);
} else {
reject(new Error(t('pages.settings.toasts.faviconUploadReadError')));
}
};
reader.onerror = () => reject(new Error(t('pages.settings.toasts.faviconUploadReadError')));
try {
reader.readAsDataURL(file);
} catch {
reject(new Error(t('pages.settings.toasts.faviconUploadReadError')));
}
});
onMounted(loadInboundTags); onMounted(loadInboundTags);
</script> </script>
@ -145,6 +202,33 @@ onMounted(loadInboundTags);
</template> </template>
</SettingListItem> </SettingListItem>
<SettingListItem paddings="small">
<template #title>{{ t('pages.settings.panelFavicon') }}</template>
<template #description>{{ t('pages.settings.panelFaviconDesc') }}</template>
<template #control>
<a-space>
<a-avatar v-if="faviconPreview" shape="square" :size="32" :src="faviconPreview" />
<a-upload accept=".ico,.png,image/x-icon,image/vnd.microsoft.icon,image/png"
:show-upload-list="false"
:custom-request="uploadFavicon">
<a-button>
<template #icon>
<UploadOutlined />
</template>
{{ t('pages.settings.upload') }}
</a-button>
</a-upload>
<a-button v-if="faviconPreview" danger @click="deleteFavicon">
<template #icon>
<DeleteOutlined />
</template>
</a-button>
</a-space>
</template>
</SettingListItem>
<SettingListItem paddings="small"> <SettingListItem paddings="small">
<template #title>{{ t('pages.settings.sessionMaxAge') }}</template> <template #title>{{ t('pages.settings.sessionMaxAge') }}</template>
<template #description>{{ t('pages.settings.sessionMaxAgeDesc') }}</template> <template #description>{{ t('pages.settings.sessionMaxAgeDesc') }}</template>
@ -429,6 +513,12 @@ onMounted(loadInboundTags);
</template> </template>
<style scoped> <style scoped>
.favicon-control {
display: flex;
align-items: center;
gap: 8px;
}
.ldap-no-inbounds { .ldap-no-inbounds {
margin-top: 6px; margin-top: 6px;
color: #999; color: #999;

View file

@ -12,6 +12,7 @@ import (
"github.com/mhsanaei/3x-ui/v3/config" "github.com/mhsanaei/3x-ui/v3/config"
"github.com/mhsanaei/3x-ui/v3/logger" "github.com/mhsanaei/3x-ui/v3/logger"
"github.com/mhsanaei/3x-ui/v3/web/service"
"github.com/mhsanaei/3x-ui/v3/web/session" "github.com/mhsanaei/3x-ui/v3/web/session"
) )
@ -67,11 +68,19 @@ func serveDistPage(c *gin.Context, name string) {
escapedVer := jsEscape.Replace(config.GetVersion()) escapedVer := jsEscape.Replace(config.GetVersion())
script += `;window.X_UI_CUR_VER="` + escapedVer + `"` script += `;window.X_UI_CUR_VER="` + escapedVer + `"`
} }
faviconLink := []byte("")
settingService := service.SettingService{}
if allSetting, err := settingService.GetAllSetting(); err == nil && strings.TrimSpace(allSetting.WebFavicon) != "" {
faviconLink = []byte(`<link rel="icon" href="` + htmlpkg.EscapeString(strings.TrimSpace(allSetting.WebFavicon)) + `">`)
} else if err != nil {
logger.Warning("Unable to load web favicon for", name+":", err)
}
script += `;</script>` script += `;</script>`
inject := []byte(script) inject := []byte(script)
inject = append(inject, csrfMeta...) inject = append(inject, csrfMeta...)
inject = append(inject, basePathMeta...) inject = append(inject, basePathMeta...)
inject = append(inject, []byte(`</head>`)...) inject = append(inject, []byte(`</head>`)...)
inject = append(inject, faviconLink...)
out := bytes.Replace(body, []byte("</head>"), inject, 1) out := bytes.Replace(body, []byte("</head>"), inject, 1)
c.Header("Cache-Control", "no-cache, no-store, must-revalidate") c.Header("Cache-Control", "no-cache, no-store, must-revalidate")

View file

@ -27,6 +27,7 @@ type AllSetting struct {
WebCertFile string `json:"webCertFile" form:"webCertFile"` // Path to SSL certificate file for web server WebCertFile string `json:"webCertFile" form:"webCertFile"` // Path to SSL certificate file for web server
WebKeyFile string `json:"webKeyFile" form:"webKeyFile"` // Path to SSL private key file for web server WebKeyFile string `json:"webKeyFile" form:"webKeyFile"` // Path to SSL private key file for web server
WebBasePath string `json:"webBasePath" form:"webBasePath"` // Base path for web panel URLs WebBasePath string `json:"webBasePath" form:"webBasePath"` // Base path for web panel URLs
WebFavicon string `json:"webFavicon" form:"webFavicon"` // Data URL for the web panel favicon
SessionMaxAge int `json:"sessionMaxAge" form:"sessionMaxAge"` // Session maximum age in minutes SessionMaxAge int `json:"sessionMaxAge" form:"sessionMaxAge"` // Session maximum age in minutes
TrustedProxyCIDRs string `json:"trustedProxyCIDRs" form:"trustedProxyCIDRs"` // Trusted reverse proxy IPs/CIDRs for forwarded headers TrustedProxyCIDRs string `json:"trustedProxyCIDRs" form:"trustedProxyCIDRs"` // Trusted reverse proxy IPs/CIDRs for forwarded headers

View file

@ -2,6 +2,7 @@ package service
import ( import (
_ "embed" _ "embed"
"encoding/base64"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
@ -34,6 +35,7 @@ var defaultValueMap = map[string]string{
"secret": random.Seq(32), "secret": random.Seq(32),
"apiToken": "", "apiToken": "",
"webBasePath": "/", "webBasePath": "/",
"webFavicon": "",
"sessionMaxAge": "360", "sessionMaxAge": "360",
"trustedProxyCIDRs": "127.0.0.1/32,::1/128", "trustedProxyCIDRs": "127.0.0.1/32,::1/128",
"pageSize": "25", "pageSize": "25",
@ -116,6 +118,8 @@ var defaultValueMap = map[string]string{
// It handles configuration storage, retrieval, and validation for all system settings. // It handles configuration storage, retrieval, and validation for all system settings.
type SettingService struct{} type SettingService struct{}
const faviconMaxSize = 256 * 1024
func (s *SettingService) GetDefaultJSONConfig() (any, error) { func (s *SettingService) GetDefaultJSONConfig() (any, error) {
var jsonData any var jsonData any
err := json.Unmarshal([]byte(xrayTemplateConfig), &jsonData) err := json.Unmarshal([]byte(xrayTemplateConfig), &jsonData)
@ -774,7 +778,7 @@ func (s *SettingService) UpdateAllSetting(allSetting *entity.AllSetting) error {
if err := s.preserveRedactedSecrets(allSetting); err != nil { if err := s.preserveRedactedSecrets(allSetting); err != nil {
return err return err
} }
if err := validateSettingsURLs(allSetting); err != nil { if err := validateSettingsInputs(allSetting); err != nil {
return err return err
} }
if err := allSetting.CheckValid(); err != nil { if err := allSetting.CheckValid(); err != nil {
@ -822,7 +826,11 @@ func (s *SettingService) preserveRedactedSecrets(allSetting *entity.AllSetting)
return nil return nil
} }
func validateSettingsURLs(allSetting *entity.AllSetting) error { func validateSettingsInputs(allSetting *entity.AllSetting) error {
if err := validateWebFavicon(allSetting); err != nil {
return err
}
if allSetting.ExternalTrafficInformURI != "" { if allSetting.ExternalTrafficInformURI != "" {
u, err := SanitizeHTTPURL(allSetting.ExternalTrafficInformURI) u, err := SanitizeHTTPURL(allSetting.ExternalTrafficInformURI)
if err != nil { if err != nil {
@ -840,6 +848,37 @@ func validateSettingsURLs(allSetting *entity.AllSetting) error {
return nil return nil
} }
func validateWebFavicon(allSetting *entity.AllSetting) error {
allSetting.WebFavicon = strings.TrimSpace(allSetting.WebFavicon)
if allSetting.WebFavicon == "" {
return nil
}
const marker = ";base64,"
idx := strings.Index(allSetting.WebFavicon, marker)
if idx < len("data:") || !strings.HasPrefix(allSetting.WebFavicon, "data:") {
return common.NewError("web favicon is invalid")
}
mimeType := allSetting.WebFavicon[len("data:"):idx]
switch mimeType {
case "image/png", "image/x-icon", "image/vnd.microsoft.icon":
default:
return common.NewError("web favicon file type is not supported")
}
data, err := base64.StdEncoding.DecodeString(allSetting.WebFavicon[idx+len(marker):])
if err != nil {
return common.NewError("web favicon is invalid:", err)
}
if len(data) > faviconMaxSize {
return common.NewError("web favicon file is too large")
}
return nil
}
func (s *SettingService) UpdateSecret(key string, value string) error { func (s *SettingService) UpdateSecret(key string, value string) error {
switch key { switch key {
case "tgBotToken", "ldapPassword", "twoFactorToken": case "tgBotToken", "ldapPassword", "twoFactorToken":

View file

@ -457,6 +457,7 @@
"settings": { "settings": {
"title": "إعدادات البانل", "title": "إعدادات البانل",
"save": "حفظ", "save": "حفظ",
"upload": "رفع",
"infoDesc": "كل تغيير هتعمله هنا لازم يتخزن. ياريت تعيد تشغيل البانل عشان التعديلات تتفعل.", "infoDesc": "كل تغيير هتعمله هنا لازم يتخزن. ياريت تعيد تشغيل البانل عشان التعديلات تتفعل.",
"restartPanel": "إعادة تشغيل البانل", "restartPanel": "إعادة تشغيل البانل",
"restartPanelDesc": "متأكد إنك عايز تعيد تشغيل البانل؟ لو ماقدرتش تدخل بعد إعادة التشغيل، شوف سجل البانل على السيرفر.", "restartPanelDesc": "متأكد إنك عايز تعيد تشغيل البانل؟ لو ماقدرتش تدخل بعد إعادة التشغيل، شوف سجل البانل على السيرفر.",
@ -478,6 +479,8 @@
"privateKeyPathDesc": "مسار ملف المفتاح الخاص للبانل. (يبدأ بـ '/')", "privateKeyPathDesc": "مسار ملف المفتاح الخاص للبانل. (يبدأ بـ '/')",
"panelUrlPath": "مسار URI", "panelUrlPath": "مسار URI",
"panelUrlPathDesc": "مسار URI للبانل. (يبدأ بـ '/' وبينتهي بـ '/')", "panelUrlPathDesc": "مسار URI للبانل. (يبدأ بـ '/' وبينتهي بـ '/')",
"panelFavicon": "الأيقونة المفضلة",
"panelFaviconDesc": "ارفع أيقونة مفضلة للوحة بصيغة PNG أو ICO",
"pageSize": "حجم الصفحة", "pageSize": "حجم الصفحة",
"pageSizeDesc": "حدد حجم الصفحة لجدول الإدخالات. (0 = تعطيل)", "pageSizeDesc": "حدد حجم الصفحة لجدول الإدخالات. (0 = تعطيل)",
"remarkModel": "نموذج الملاحظة وحرف الفصل", "remarkModel": "نموذج الملاحظة وحرف الفصل",
@ -611,7 +614,11 @@
"originalUserPassIncorrect": "اسم المستخدم أو الباسورد الحالي غير صحيح", "originalUserPassIncorrect": "اسم المستخدم أو الباسورد الحالي غير صحيح",
"userPassMustBeNotEmpty": "اسم المستخدم والباسورد الجديدين فاضيين", "userPassMustBeNotEmpty": "اسم المستخدم والباسورد الجديدين فاضيين",
"getOutboundTrafficError": "خطأ في الحصول على حركات المرور الصادرة", "getOutboundTrafficError": "خطأ في الحصول على حركات المرور الصادرة",
"resetOutboundTrafficError": "خطأ في إعادة تعيين حركات المرور الصادرة" "resetOutboundTrafficError": "خطأ في إعادة تعيين حركات المرور الصادرة",
"faviconUploadUnsupported": "يتم دعم ملفات الأيقونة المفضلة بصيغة PNG و ICO فقط",
"faviconUploadTooLarge": "يجب أن يكون حجم ملف الأيقونة المفضلة 256 كيلوبايت أو أقل",
"faviconUploadReadError": "فشل في قراءة ملف الأيقونة المفضلة",
"faviconUploadFailed": "فشل في رفع الأيقونة المفضلة"
} }
}, },
"xray": { "xray": {

View file

@ -457,6 +457,7 @@
"settings": { "settings": {
"title": "Panel Settings", "title": "Panel Settings",
"save": "Save", "save": "Save",
"upload": "Upload",
"infoDesc": "Every change made here needs to be saved. Please restart the panel to apply changes.", "infoDesc": "Every change made here needs to be saved. Please restart the panel to apply changes.",
"restartPanel": "Restart Panel", "restartPanel": "Restart Panel",
"restartPanelDesc": "Are you sure you want to restart the panel? If you cannot access the panel after restarting, please view the panel log info on the server.", "restartPanelDesc": "Are you sure you want to restart the panel? If you cannot access the panel after restarting, please view the panel log info on the server.",
@ -478,6 +479,8 @@
"privateKeyPathDesc": "The private key file path for the web panel. (begins with /)", "privateKeyPathDesc": "The private key file path for the web panel. (begins with /)",
"panelUrlPath": "URI Path", "panelUrlPath": "URI Path",
"panelUrlPathDesc": "The URI path for the web panel. (begins with / and concludes with /)", "panelUrlPathDesc": "The URI path for the web panel. (begins with / and concludes with /)",
"panelFavicon": "Favicon",
"panelFaviconDesc": "Upload a PNG or ICO favicon for the panel",
"pageSize": "Pagination Size", "pageSize": "Pagination Size",
"pageSizeDesc": "Define page size for inbounds table. (0 = disable)", "pageSizeDesc": "Define page size for inbounds table. (0 = disable)",
"remarkModel": "Remark Model & Separation Character", "remarkModel": "Remark Model & Separation Character",
@ -611,7 +614,11 @@
"originalUserPassIncorrect": "The сurrent username or password is invalid", "originalUserPassIncorrect": "The сurrent username or password is invalid",
"userPassMustBeNotEmpty": "The new username and password is empty", "userPassMustBeNotEmpty": "The new username and password is empty",
"getOutboundTrafficError": "Error getting traffics", "getOutboundTrafficError": "Error getting traffics",
"resetOutboundTrafficError": "Error in reset outbound traffics" "resetOutboundTrafficError": "Error in reset outbound traffics",
"faviconUploadUnsupported": "Only PNG and ICO favicon files are supported",
"faviconUploadTooLarge": "Favicon file must be 256 KB or smaller",
"faviconUploadReadError": "Failed to read favicon file",
"faviconUploadFailed": "Failed to upload favicon"
} }
}, },
"xray": { "xray": {

View file

@ -457,6 +457,7 @@
"settings": { "settings": {
"title": "Configuraciones", "title": "Configuraciones",
"save": "Guardar", "save": "Guardar",
"upload": "Subir",
"infoDesc": "Cada cambio realizado aquí debe ser guardado. Por favor, reinicie el panel para aplicar los cambios.", "infoDesc": "Cada cambio realizado aquí debe ser guardado. Por favor, reinicie el panel para aplicar los cambios.",
"restartPanel": "Reiniciar Panel", "restartPanel": "Reiniciar Panel",
"restartPanelDesc": "¿Está seguro de que desea reiniciar el panel? Haga clic en Aceptar para reiniciar después de 3 segundos. Si no puede acceder al panel después de reiniciar, por favor, consulte la información de registro del panel en el servidor.", "restartPanelDesc": "¿Está seguro de que desea reiniciar el panel? Haga clic en Aceptar para reiniciar después de 3 segundos. Si no puede acceder al panel después de reiniciar, por favor, consulte la información de registro del panel en el servidor.",
@ -478,6 +479,8 @@
"privateKeyPathDesc": "Complete con una ruta absoluta que comience con.", "privateKeyPathDesc": "Complete con una ruta absoluta que comience con.",
"panelUrlPath": "Ruta Raíz de la URL del Panel", "panelUrlPath": "Ruta Raíz de la URL del Panel",
"panelUrlPathDesc": "Debe empezar con '/' y terminar con.", "panelUrlPathDesc": "Debe empezar con '/' y terminar con.",
"panelFavicon": "Favicon",
"panelFaviconDesc": "Sube un favicon PNG o ICO para el panel",
"pageSize": "Tamaño de paginación", "pageSize": "Tamaño de paginación",
"pageSizeDesc": "Defina el tamaño de página para la tabla de entradas. Establezca 0 para desactivar", "pageSizeDesc": "Defina el tamaño de página para la tabla de entradas. Establezca 0 para desactivar",
"remarkModel": "Modelo de observación y carácter de separación", "remarkModel": "Modelo de observación y carácter de separación",
@ -611,7 +614,11 @@
"originalUserPassIncorrect": "Nombre de usuario o contraseña original incorrectos", "originalUserPassIncorrect": "Nombre de usuario o contraseña original incorrectos",
"userPassMustBeNotEmpty": "El nuevo nombre de usuario y la nueva contraseña no pueden estar vacíos", "userPassMustBeNotEmpty": "El nuevo nombre de usuario y la nueva contraseña no pueden estar vacíos",
"getOutboundTrafficError": "Error al obtener el tráfico saliente", "getOutboundTrafficError": "Error al obtener el tráfico saliente",
"resetOutboundTrafficError": "Error al reiniciar el tráfico saliente" "resetOutboundTrafficError": "Error al reiniciar el tráfico saliente",
"faviconUploadUnsupported": "Solo se admiten archivos favicon PNG e ICO",
"faviconUploadTooLarge": "El archivo favicon debe ser de 256 KB o menos",
"faviconUploadReadError": "No se pudo leer el archivo favicon",
"faviconUploadFailed": "No se pudo subir el favicon"
} }
}, },
"xray": { "xray": {

View file

@ -457,6 +457,7 @@
"settings": { "settings": {
"title": "تنظیمات پنل", "title": "تنظیمات پنل",
"save": "ذخیره", "save": "ذخیره",
"upload": "آپلود",
"infoDesc": "برای اعمال تغییرات در این بخش باید پس از ذخیره کردن، پنل را ریستارت کنید", "infoDesc": "برای اعمال تغییرات در این بخش باید پس از ذخیره کردن، پنل را ریستارت کنید",
"restartPanel": "ریستارت پنل", "restartPanel": "ریستارت پنل",
"restartPanelDesc": "آیا مطمئن به ریستارت پنل هستید؟ اگر پس‌از ریستارت نمی‌توانید به پنل دسترسی پیدا کنید، لطفاً گزارش‌های موجود در اسکریپت پنل را بررسی کنید", "restartPanelDesc": "آیا مطمئن به ریستارت پنل هستید؟ اگر پس‌از ریستارت نمی‌توانید به پنل دسترسی پیدا کنید، لطفاً گزارش‌های موجود در اسکریپت پنل را بررسی کنید",
@ -478,6 +479,8 @@
"privateKeyPathDesc": "مسیر فایل کلیدخصوصی برای وب پنل. با '/' شروع‌می‌شود", "privateKeyPathDesc": "مسیر فایل کلیدخصوصی برای وب پنل. با '/' شروع‌می‌شود",
"panelUrlPath": "URI مسیر", "panelUrlPath": "URI مسیر",
"panelUrlPathDesc": "برای وب پنل. با '/' شروع‌ و با '/' خاتمه‌ می‌یابد URI مسیر", "panelUrlPathDesc": "برای وب پنل. با '/' شروع‌ و با '/' خاتمه‌ می‌یابد URI مسیر",
"panelFavicon": "فاوآیکون",
"panelFaviconDesc": "یک فاوآیکون PNG یا ICO برای پنل آپلود کنید",
"pageSize": "اندازه صفحه بندی جدول", "pageSize": "اندازه صفحه بندی جدول",
"pageSizeDesc": "(اندازه صفحه برای جدول ورودی‌ها.(0 = غیرفعال", "pageSizeDesc": "(اندازه صفحه برای جدول ورودی‌ها.(0 = غیرفعال",
"remarkModel": "نام‌کانفیگ و جداکننده", "remarkModel": "نام‌کانفیگ و جداکننده",
@ -611,7 +614,11 @@
"originalUserPassIncorrect": "نام‌کاربری یا رمزعبور فعلی اشتباه‌است", "originalUserPassIncorrect": "نام‌کاربری یا رمزعبور فعلی اشتباه‌است",
"userPassMustBeNotEmpty": "نام‌کاربری یا رمزعبور جدید خالی‌است", "userPassMustBeNotEmpty": "نام‌کاربری یا رمزعبور جدید خالی‌است",
"getOutboundTrafficError": "خطا در دریافت ترافیک خروجی", "getOutboundTrafficError": "خطا در دریافت ترافیک خروجی",
"resetOutboundTrafficError": "خطا در بازنشانی ترافیک خروجی" "resetOutboundTrafficError": "خطا در بازنشانی ترافیک خروجی",
"faviconUploadUnsupported": "فقط فایل‌های فاوآیکون PNG و ICO پشتیبانی می‌شوند",
"faviconUploadTooLarge": "حجم فایل فاوآیکون باید ۲۵۶ کیلوبایت یا کمتر باشد",
"faviconUploadReadError": "خواندن فایل فاوآیکون ناموفق بود",
"faviconUploadFailed": "آپلود فاوآیکون ناموفق بود"
} }
}, },
"xray": { "xray": {

View file

@ -457,6 +457,7 @@
"settings": { "settings": {
"title": "Pengaturan Panel", "title": "Pengaturan Panel",
"save": "Simpan", "save": "Simpan",
"upload": "Unggah",
"infoDesc": "Setiap perubahan yang dibuat di sini perlu disimpan. Harap restart panel untuk menerapkan perubahan.", "infoDesc": "Setiap perubahan yang dibuat di sini perlu disimpan. Harap restart panel untuk menerapkan perubahan.",
"restartPanel": "Restart Panel", "restartPanel": "Restart Panel",
"restartPanelDesc": "Apakah Anda yakin ingin merestart panel? Jika Anda tidak dapat mengakses panel setelah merestart, lihat info log panel di server.", "restartPanelDesc": "Apakah Anda yakin ingin merestart panel? Jika Anda tidak dapat mengakses panel setelah merestart, lihat info log panel di server.",
@ -478,6 +479,8 @@
"privateKeyPathDesc": "Path berkas kunci privat untuk panel web. (dimulai dengan /)", "privateKeyPathDesc": "Path berkas kunci privat untuk panel web. (dimulai dengan /)",
"panelUrlPath": "URI Path", "panelUrlPath": "URI Path",
"panelUrlPathDesc": "URI path untuk panel web. (dimulai dengan / dan diakhiri dengan /)", "panelUrlPathDesc": "URI path untuk panel web. (dimulai dengan / dan diakhiri dengan /)",
"panelFavicon": "Favicon",
"panelFaviconDesc": "Unggah favicon PNG atau ICO untuk panel",
"pageSize": "Ukuran Halaman", "pageSize": "Ukuran Halaman",
"pageSizeDesc": "Tentukan ukuran halaman untuk tabel masuk. (0 = nonaktif)", "pageSizeDesc": "Tentukan ukuran halaman untuk tabel masuk. (0 = nonaktif)",
"remarkModel": "Model Catatan & Karakter Pemisah", "remarkModel": "Model Catatan & Karakter Pemisah",
@ -611,7 +614,11 @@
"originalUserPassIncorrect": "Username atau password saat ini tidak valid", "originalUserPassIncorrect": "Username atau password saat ini tidak valid",
"userPassMustBeNotEmpty": "Username dan password baru tidak boleh kosong", "userPassMustBeNotEmpty": "Username dan password baru tidak boleh kosong",
"getOutboundTrafficError": "Gagal mendapatkan lalu lintas keluar", "getOutboundTrafficError": "Gagal mendapatkan lalu lintas keluar",
"resetOutboundTrafficError": "Gagal mereset lalu lintas keluar" "resetOutboundTrafficError": "Gagal mereset lalu lintas keluar",
"faviconUploadUnsupported": "Hanya file favicon PNG dan ICO yang didukung",
"faviconUploadTooLarge": "File favicon harus berukuran 256 KB atau lebih kecil",
"faviconUploadReadError": "Gagal membaca file favicon",
"faviconUploadFailed": "Gagal mengunggah favicon"
} }
}, },
"xray": { "xray": {

View file

@ -457,6 +457,7 @@
"settings": { "settings": {
"title": "パネル設定", "title": "パネル設定",
"save": "保存", "save": "保存",
"upload": "アップロード",
"infoDesc": "ここでのすべての変更は、保存してパネルを再起動する必要があります", "infoDesc": "ここでのすべての変更は、保存してパネルを再起動する必要があります",
"restartPanel": "パネル再起動", "restartPanel": "パネル再起動",
"restartPanelDesc": "パネルを再起動してもよろしいですか?再起動後にパネルにアクセスできない場合は、サーバーでパネルログを確認してください", "restartPanelDesc": "パネルを再起動してもよろしいですか?再起動後にパネルにアクセスできない場合は、サーバーでパネルログを確認してください",
@ -478,6 +479,8 @@
"privateKeyPathDesc": "'/'で始まる絶対パスを入力", "privateKeyPathDesc": "'/'で始まる絶対パスを入力",
"panelUrlPath": "パネルURLルートパス", "panelUrlPath": "パネルURLルートパス",
"panelUrlPathDesc": "'/'で始まり、'/'で終わる必要があります", "panelUrlPathDesc": "'/'で始まり、'/'で終わる必要があります",
"panelFavicon": "ファビコン",
"panelFaviconDesc": "パネル用の PNG または ICO ファビコンをアップロードします",
"pageSize": "ページサイズ", "pageSize": "ページサイズ",
"pageSizeDesc": "インバウンドテーブルのページサイズを定義します。0を設定すると無効化されます", "pageSizeDesc": "インバウンドテーブルのページサイズを定義します。0を設定すると無効化されます",
"remarkModel": "備考モデルと区切り記号", "remarkModel": "備考モデルと区切り記号",
@ -611,7 +614,11 @@
"originalUserPassIncorrect": "旧ユーザー名または旧パスワードが間違っています", "originalUserPassIncorrect": "旧ユーザー名または旧パスワードが間違っています",
"userPassMustBeNotEmpty": "新しいユーザー名と新しいパスワードは空にできません", "userPassMustBeNotEmpty": "新しいユーザー名と新しいパスワードは空にできません",
"getOutboundTrafficError": "送信トラフィックの取得エラー", "getOutboundTrafficError": "送信トラフィックの取得エラー",
"resetOutboundTrafficError": "送信トラフィックのリセットエラー" "resetOutboundTrafficError": "送信トラフィックのリセットエラー",
"faviconUploadUnsupported": "PNG および ICO のファビコンファイルのみ対応しています",
"faviconUploadTooLarge": "ファビコンファイルは 256 KB 以下にしてください",
"faviconUploadReadError": "ファビコンファイルの読み込みに失敗しました",
"faviconUploadFailed": "ファビコンのアップロードに失敗しました"
} }
}, },
"xray": { "xray": {

View file

@ -457,6 +457,7 @@
"settings": { "settings": {
"title": "Configurações do Painel", "title": "Configurações do Painel",
"save": "Salvar", "save": "Salvar",
"upload": "Enviar",
"infoDesc": "Toda alteração feita aqui precisa ser salva. Reinicie o painel para aplicar as alterações.", "infoDesc": "Toda alteração feita aqui precisa ser salva. Reinicie o painel para aplicar as alterações.",
"restartPanel": "Reiniciar Painel", "restartPanel": "Reiniciar Painel",
"restartPanelDesc": "Tem certeza de que deseja reiniciar o painel? Se não conseguir acessar o painel após reiniciar, consulte os logs do painel no servidor.", "restartPanelDesc": "Tem certeza de que deseja reiniciar o painel? Se não conseguir acessar o painel após reiniciar, consulte os logs do painel no servidor.",
@ -478,6 +479,8 @@
"privateKeyPathDesc": "O caminho do arquivo de chave privada para o painel web. (começa com /)", "privateKeyPathDesc": "O caminho do arquivo de chave privada para o painel web. (começa com /)",
"panelUrlPath": "Caminho URI", "panelUrlPath": "Caminho URI",
"panelUrlPathDesc": "O caminho URI para o painel web. (começa com / e termina com /)", "panelUrlPathDesc": "O caminho URI para o painel web. (começa com / e termina com /)",
"panelFavicon": "Favicon",
"panelFaviconDesc": "Envie um favicon PNG ou ICO para o painel",
"pageSize": "Tamanho da Paginação", "pageSize": "Tamanho da Paginação",
"pageSizeDesc": "Definir o tamanho da página para a tabela de entradas. (0 = desativado)", "pageSizeDesc": "Definir o tamanho da página para a tabela de entradas. (0 = desativado)",
"remarkModel": "Modelo de Observação & Caractere de Separação", "remarkModel": "Modelo de Observação & Caractere de Separação",
@ -611,7 +614,11 @@
"originalUserPassIncorrect": "O nome de usuário ou senha atual é inválido", "originalUserPassIncorrect": "O nome de usuário ou senha atual é inválido",
"userPassMustBeNotEmpty": "O novo nome de usuário e senha não podem estar vazios", "userPassMustBeNotEmpty": "O novo nome de usuário e senha não podem estar vazios",
"getOutboundTrafficError": "Erro ao obter tráfego de saída", "getOutboundTrafficError": "Erro ao obter tráfego de saída",
"resetOutboundTrafficError": "Erro ao redefinir tráfego de saída" "resetOutboundTrafficError": "Erro ao redefinir tráfego de saída",
"faviconUploadUnsupported": "Somente arquivos de favicon PNG e ICO são suportados",
"faviconUploadTooLarge": "O arquivo de favicon deve ter 256 KB ou menos",
"faviconUploadReadError": "Falha ao ler o arquivo de favicon",
"faviconUploadFailed": "Falha ao enviar o favicon"
} }
}, },
"xray": { "xray": {

View file

@ -457,6 +457,7 @@
"settings": { "settings": {
"title": "Настройки", "title": "Настройки",
"save": "Сохранить", "save": "Сохранить",
"upload": "Загрузить",
"infoDesc": "Сохраните изменения и перезапустите панель для их применения.", "infoDesc": "Сохраните изменения и перезапустите панель для их применения.",
"restartPanel": "Перезапуск панели", "restartPanel": "Перезапуск панели",
"restartPanelDesc": "Вы уверены, что хотите перезапустить панель? Подтвердите, и перезапуск произойдёт через 3 секунды. Если панель будет недоступна, проверьте лог сервера", "restartPanelDesc": "Вы уверены, что хотите перезапустить панель? Подтвердите, и перезапуск произойдёт через 3 секунды. Если панель будет недоступна, проверьте лог сервера",
@ -478,6 +479,8 @@
"privateKeyPathDesc": "Введите полный путь, начинающийся с '/'", "privateKeyPathDesc": "Введите полный путь, начинающийся с '/'",
"panelUrlPath": "Корневой путь URL адреса панели", "panelUrlPath": "Корневой путь URL адреса панели",
"panelUrlPathDesc": "Должен начинаться с '/' и заканчиваться '/'", "panelUrlPathDesc": "Должен начинаться с '/' и заканчиваться '/'",
"panelFavicon": "Favicon",
"panelFaviconDesc": "Загрузите PNG или ICO favicon для панели",
"pageSize": "Размер нумерации страниц", "pageSize": "Размер нумерации страниц",
"pageSizeDesc": "Определить размер страницы для таблицы подключений. Установите 0, чтобы отключить", "pageSizeDesc": "Определить размер страницы для таблицы подключений. Установите 0, чтобы отключить",
"remarkModel": "Модель примечания и символ разделения", "remarkModel": "Модель примечания и символ разделения",
@ -611,7 +614,11 @@
"originalUserPassIncorrect": "Неверное имя пользователя или пароль", "originalUserPassIncorrect": "Неверное имя пользователя или пароль",
"userPassMustBeNotEmpty": "Новое имя пользователя и новый пароль должны быть заполнены", "userPassMustBeNotEmpty": "Новое имя пользователя и новый пароль должны быть заполнены",
"getOutboundTrafficError": "Ошибка получения трафика исходящего подключения", "getOutboundTrafficError": "Ошибка получения трафика исходящего подключения",
"resetOutboundTrafficError": "Ошибка сброса трафика исходящего подключения" "resetOutboundTrafficError": "Ошибка сброса трафика исходящего подключения",
"faviconUploadUnsupported": "Поддерживаются только favicon файлы PNG и ICO",
"faviconUploadTooLarge": "Размер favicon должен быть не больше 256 КБ",
"faviconUploadReadError": "Не удалось прочитать файл favicon",
"faviconUploadFailed": "Не удалось загрузить favicon"
} }
}, },
"xray": { "xray": {

View file

@ -457,6 +457,7 @@
"settings": { "settings": {
"title": "Panel Ayarları", "title": "Panel Ayarları",
"save": "Kaydet", "save": "Kaydet",
"upload": "Yükle",
"infoDesc": "Burada yapılan her değişikliğin kaydedilmesi gerekir. Değişikliklerin uygulanması için paneli yeniden başlatın.", "infoDesc": "Burada yapılan her değişikliğin kaydedilmesi gerekir. Değişikliklerin uygulanması için paneli yeniden başlatın.",
"restartPanel": "Paneli Yeniden Başlat", "restartPanel": "Paneli Yeniden Başlat",
"restartPanelDesc": "Paneli yeniden başlatmak istediğinizden emin misiniz? Yeniden başlattıktan sonra panele erişemezseniz, sunucudaki panel günlük bilgilerini görüntüleyin.", "restartPanelDesc": "Paneli yeniden başlatmak istediğinizden emin misiniz? Yeniden başlattıktan sonra panele erişemezseniz, sunucudaki panel günlük bilgilerini görüntüleyin.",
@ -478,6 +479,8 @@
"privateKeyPathDesc": "Web paneli için özel anahtar dosya yolu. ('/' ile başlar)", "privateKeyPathDesc": "Web paneli için özel anahtar dosya yolu. ('/' ile başlar)",
"panelUrlPath": "URI Yolu", "panelUrlPath": "URI Yolu",
"panelUrlPathDesc": "Web paneli için URI yolu. ('/' ile başlar ve '/' ile biter)", "panelUrlPathDesc": "Web paneli için URI yolu. ('/' ile başlar ve '/' ile biter)",
"panelFavicon": "Favicon",
"panelFaviconDesc": "Panel için PNG veya ICO favicon yükleyin",
"pageSize": "Sayfa Boyutu", "pageSize": "Sayfa Boyutu",
"pageSizeDesc": "Gelenler tablosu için sayfa boyutunu belirleyin. (0 = devre dışı)", "pageSizeDesc": "Gelenler tablosu için sayfa boyutunu belirleyin. (0 = devre dışı)",
"remarkModel": "Açıklama Modeli & Ayırma Karakteri", "remarkModel": "Açıklama Modeli & Ayırma Karakteri",
@ -611,7 +614,11 @@
"originalUserPassIncorrect": "Mevcut kullanıcı adı veya şifre geçersiz", "originalUserPassIncorrect": "Mevcut kullanıcı adı veya şifre geçersiz",
"userPassMustBeNotEmpty": "Yeni kullanıcı adı ve şifre boş olamaz", "userPassMustBeNotEmpty": "Yeni kullanıcı adı ve şifre boş olamaz",
"getOutboundTrafficError": "Giden trafik alınırken hata", "getOutboundTrafficError": "Giden trafik alınırken hata",
"resetOutboundTrafficError": "Giden trafik sıfırlanırken hata" "resetOutboundTrafficError": "Giden trafik sıfırlanırken hata",
"faviconUploadUnsupported": "Yalnızca PNG ve ICO favicon dosyaları desteklenir",
"faviconUploadTooLarge": "Favicon dosyası 256 KB veya daha küçük olmalıdır",
"faviconUploadReadError": "Favicon dosyası okunamadı",
"faviconUploadFailed": "Favicon yüklenemedi"
} }
}, },
"xray": { "xray": {

View file

@ -457,6 +457,7 @@
"settings": { "settings": {
"title": "Параметри панелі", "title": "Параметри панелі",
"save": "Зберегти", "save": "Зберегти",
"upload": "Завантажити",
"infoDesc": "Кожна внесена тут зміна повинна бути збережена. Перезапустіть панель, щоб застосувати зміни.", "infoDesc": "Кожна внесена тут зміна повинна бути збережена. Перезапустіть панель, щоб застосувати зміни.",
"restartPanel": "Перезапустити панель", "restartPanel": "Перезапустити панель",
"restartPanelDesc": "Ви впевнені, що бажаєте перезапустити панель? Якщо ви не можете отримати доступ до панелі після перезапуску, будь ласка, перегляньте інформацію журналу панелі на сервері.", "restartPanelDesc": "Ви впевнені, що бажаєте перезапустити панель? Якщо ви не можете отримати доступ до панелі після перезапуску, будь ласка, перегляньте інформацію журналу панелі на сервері.",
@ -478,6 +479,8 @@
"privateKeyPathDesc": "Шлях до файлу приватного ключа для веб-панелі. (починається з /)", "privateKeyPathDesc": "Шлях до файлу приватного ключа для веб-панелі. (починається з /)",
"panelUrlPath": "Шлях URL", "panelUrlPath": "Шлях URL",
"panelUrlPathDesc": "Шлях URL для веб-панелі. (починається з / і закінчується /)", "panelUrlPathDesc": "Шлях URL для веб-панелі. (починається з / і закінчується /)",
"panelFavicon": "Favicon",
"panelFaviconDesc": "Завантажте PNG або ICO favicon для панелі",
"pageSize": "Розмір сторінки", "pageSize": "Розмір сторінки",
"pageSizeDesc": "Визначити розмір сторінки для вхідної таблиці. (0 = вимкнено)", "pageSizeDesc": "Визначити розмір сторінки для вхідної таблиці. (0 = вимкнено)",
"remarkModel": "Модель зауваження та роздільний символ", "remarkModel": "Модель зауваження та роздільний символ",
@ -611,7 +614,11 @@
"originalUserPassIncorrect": "Поточне ім'я користувача або пароль недійсні", "originalUserPassIncorrect": "Поточне ім'я користувача або пароль недійсні",
"userPassMustBeNotEmpty": "Нове ім'я користувача та пароль порожні", "userPassMustBeNotEmpty": "Нове ім'я користувача та пароль порожні",
"getOutboundTrafficError": "Помилка отримання вихідного трафіку", "getOutboundTrafficError": "Помилка отримання вихідного трафіку",
"resetOutboundTrafficError": "Помилка скидання вихідного трафіку" "resetOutboundTrafficError": "Помилка скидання вихідного трафіку",
"faviconUploadUnsupported": "Підтримуються лише файли favicon у форматі PNG або ICO",
"faviconUploadTooLarge": "Файл favicon має бути розміром 256 КБ або менше",
"faviconUploadReadError": "Не вдалося прочитати файл favicon",
"faviconUploadFailed": "Не вдалося завантажити favicon"
} }
}, },
"xray": { "xray": {

View file

@ -457,6 +457,7 @@
"settings": { "settings": {
"title": "Cài đặt", "title": "Cài đặt",
"save": "Lưu", "save": "Lưu",
"upload": "Tải lên",
"infoDesc": "Mọi thay đổi được thực hiện ở đây cần phải được lưu. Vui lòng khởi động lại bảng điều khiển để áp dụng các thay đổi.", "infoDesc": "Mọi thay đổi được thực hiện ở đây cần phải được lưu. Vui lòng khởi động lại bảng điều khiển để áp dụng các thay đổi.",
"restartPanel": "Khởi động lại bảng điều khiển", "restartPanel": "Khởi động lại bảng điều khiển",
"restartPanelDesc": "Bạn có chắc chắn muốn khởi động lại bảng điều khiển? Nhấn OK để khởi động lại sau 3 giây. Nếu bạn không thể truy cập bảng điều khiển sau khi khởi động lại, vui lòng xem thông tin nhật ký của bảng điều khiển trên máy chủ.", "restartPanelDesc": "Bạn có chắc chắn muốn khởi động lại bảng điều khiển? Nhấn OK để khởi động lại sau 3 giây. Nếu bạn không thể truy cập bảng điều khiển sau khi khởi động lại, vui lòng xem thông tin nhật ký của bảng điều khiển trên máy chủ.",
@ -478,6 +479,8 @@
"privateKeyPathDesc": "Điền vào đường dẫn đầy đủ (bắt đầu từ '/')", "privateKeyPathDesc": "Điền vào đường dẫn đầy đủ (bắt đầu từ '/')",
"panelUrlPath": "Đường dẫn gốc URL bảng điều khiển", "panelUrlPath": "Đường dẫn gốc URL bảng điều khiển",
"panelUrlPathDesc": "Phải bắt đầu và kết thúc bằng '/'", "panelUrlPathDesc": "Phải bắt đầu và kết thúc bằng '/'",
"panelFavicon": "Favicon",
"panelFaviconDesc": "Tải lên favicon PNG hoặc ICO cho bảng điều khiển",
"pageSize": "Kích thước phân trang", "pageSize": "Kích thước phân trang",
"pageSizeDesc": "Xác định kích thước trang cho bảng gửi đến. Đặt 0 để tắt", "pageSizeDesc": "Xác định kích thước trang cho bảng gửi đến. Đặt 0 để tắt",
"remarkModel": "Ghi chú mô hình và ký tự phân tách", "remarkModel": "Ghi chú mô hình và ký tự phân tách",
@ -611,7 +614,11 @@
"originalUserPassIncorrect": "Tên người dùng hoặc mật khẩu gốc không đúng", "originalUserPassIncorrect": "Tên người dùng hoặc mật khẩu gốc không đúng",
"userPassMustBeNotEmpty": "Tên người dùng mới và mật khẩu mới không thể để trống", "userPassMustBeNotEmpty": "Tên người dùng mới và mật khẩu mới không thể để trống",
"getOutboundTrafficError": "Lỗi khi lấy lưu lượng truy cập đi", "getOutboundTrafficError": "Lỗi khi lấy lưu lượng truy cập đi",
"resetOutboundTrafficError": "Lỗi khi đặt lại lưu lượng truy cập đi" "resetOutboundTrafficError": "Lỗi khi đặt lại lưu lượng truy cập đi",
"faviconUploadUnsupported": "Chỉ hỗ trợ tệp favicon PNG và ICO",
"faviconUploadTooLarge": "Tệp favicon phải có dung lượng từ 256 KB trở xuống",
"faviconUploadReadError": "Không thể đọc tệp favicon",
"faviconUploadFailed": "Không thể tải favicon lên"
} }
}, },
"xray": { "xray": {

View file

@ -457,6 +457,7 @@
"settings": { "settings": {
"title": "面板设置", "title": "面板设置",
"save": "保存", "save": "保存",
"upload": "上传",
"infoDesc": "此处的所有更改都需要保存并重启面板才能生效", "infoDesc": "此处的所有更改都需要保存并重启面板才能生效",
"restartPanel": "重启面板", "restartPanel": "重启面板",
"restartPanelDesc": "确定要重启面板吗?若重启后无法访问面板,请前往服务器查看面板日志信息", "restartPanelDesc": "确定要重启面板吗?若重启后无法访问面板,请前往服务器查看面板日志信息",
@ -478,6 +479,8 @@
"privateKeyPathDesc": "填写一个 '/' 开头的绝对路径", "privateKeyPathDesc": "填写一个 '/' 开头的绝对路径",
"panelUrlPath": "面板 url 根路径", "panelUrlPath": "面板 url 根路径",
"panelUrlPathDesc": "必须以 '/' 开头,以 '/' 结尾", "panelUrlPathDesc": "必须以 '/' 开头,以 '/' 结尾",
"panelFavicon": "网站图标",
"panelFaviconDesc": "为面板上传 PNG 或 ICO 格式的网站图标",
"pageSize": "分页大小", "pageSize": "分页大小",
"pageSizeDesc": "定义入站表的页面大小。设置 0 表示禁用", "pageSizeDesc": "定义入站表的页面大小。设置 0 表示禁用",
"remarkModel": "备注模型和分隔符", "remarkModel": "备注模型和分隔符",
@ -611,7 +614,11 @@
"originalUserPassIncorrect": "原用户名或原密码错误", "originalUserPassIncorrect": "原用户名或原密码错误",
"userPassMustBeNotEmpty": "新用户名和新密码不能为空", "userPassMustBeNotEmpty": "新用户名和新密码不能为空",
"getOutboundTrafficError": "获取出站流量错误", "getOutboundTrafficError": "获取出站流量错误",
"resetOutboundTrafficError": "重置出站流量错误" "resetOutboundTrafficError": "重置出站流量错误",
"faviconUploadUnsupported": "仅支持 PNG 和 ICO 网站图标文件",
"faviconUploadTooLarge": "网站图标文件必须不超过 256 KB",
"faviconUploadReadError": "读取网站图标文件失败",
"faviconUploadFailed": "上传网站图标失败"
} }
}, },
"xray": { "xray": {

View file

@ -457,6 +457,7 @@
"settings": { "settings": {
"title": "面板設定", "title": "面板設定",
"save": "儲存", "save": "儲存",
"upload": "上傳",
"infoDesc": "此處的所有更改都需要儲存並重啟面板才能生效", "infoDesc": "此處的所有更改都需要儲存並重啟面板才能生效",
"restartPanel": "重啟面板", "restartPanel": "重啟面板",
"restartPanelDesc": "確定要重啟面板嗎?若重啟後無法訪問面板,請前往伺服器檢視面板日誌資訊", "restartPanelDesc": "確定要重啟面板嗎?若重啟後無法訪問面板,請前往伺服器檢視面板日誌資訊",
@ -478,6 +479,8 @@
"privateKeyPathDesc": "填寫一個 '/' 開頭的絕對路徑", "privateKeyPathDesc": "填寫一個 '/' 開頭的絕對路徑",
"panelUrlPath": "面板 url 根路徑", "panelUrlPath": "面板 url 根路徑",
"panelUrlPathDesc": "必須以 '/' 開頭,以 '/' 結尾", "panelUrlPathDesc": "必須以 '/' 開頭,以 '/' 結尾",
"panelFavicon": "網站圖示",
"panelFaviconDesc": "為面板上傳 PNG 或 ICO 格式的網站圖示",
"pageSize": "分頁大小", "pageSize": "分頁大小",
"pageSizeDesc": "定義入站表的頁面大小。設定 0 表示禁用", "pageSizeDesc": "定義入站表的頁面大小。設定 0 表示禁用",
"remarkModel": "備註模型和分隔符", "remarkModel": "備註模型和分隔符",
@ -611,7 +614,11 @@
"originalUserPassIncorrect": "原使用者名稱或原密碼錯誤", "originalUserPassIncorrect": "原使用者名稱或原密碼錯誤",
"userPassMustBeNotEmpty": "新使用者名稱和新密碼不能為空", "userPassMustBeNotEmpty": "新使用者名稱和新密碼不能為空",
"getOutboundTrafficError": "取得出站流量錯誤", "getOutboundTrafficError": "取得出站流量錯誤",
"resetOutboundTrafficError": "重設出站流量錯誤" "resetOutboundTrafficError": "重設出站流量錯誤",
"faviconUploadUnsupported": "僅支援 PNG 和 ICO 網站圖示檔案",
"faviconUploadTooLarge": "網站圖示檔案必須不超過 256 KB",
"faviconUploadReadError": "讀取網站圖示檔案失敗",
"faviconUploadFailed": "上傳網站圖示失敗"
} }
}, },
"xray": { "xray": {