Przeglądaj źródła

i18n: localize sidebar theme toggle, xray-status badge, and nodes menu

The sidebar theme submenu (Theme / Dark / Ultra dark) and the dashboard's
Xray status badge ("Xray is running" etc.) were hardcoded English strings.
Wire them through vue-i18n: ThemeSwitch.vue uses menu.theme/dark/ultraDark,
and XrayStatusCard.vue derives the badge text from the existing
pages.index.xrayStatus{Running,Stop,Error,Unknown} keys (status.js no
longer carries an English stateMsg field).

The "Nodes" menu item was already keyed as menu.nodes but only en-US and
fa-IR had a translation; add it to the other 11 languages, matching the
wording each file already uses for pages.nodes.title.
#4201
MHSanaei 1 dzień temu
rodzic
commit
cf5767acd1

+ 6 - 3
frontend/src/components/ThemeSwitch.vue

@@ -1,8 +1,11 @@
 <script setup>
 import { computed } from 'vue';
+import { useI18n } from 'vue-i18n';
 import { BulbFilled, BulbOutlined } from '@ant-design/icons-vue';
 import { theme, currentTheme, toggleTheme, toggleUltra, pauseAnimationsUntilLeave } from '@/composables/useTheme.js';
 
+const { t } = useI18n();
+
 const BulbIcon = computed(() => (theme.isDark ? BulbFilled : BulbOutlined));
 
 function onDarkChange() {
@@ -22,17 +25,17 @@ function onUltraClick() {
       <template #title>
         <span>
           <component :is="BulbIcon" />
-          <span class="theme-label">Theme</span>
+          <span class="theme-label">{{ t('menu.theme') }}</span>
         </span>
       </template>
 
       <a-menu-item id="change-theme" class="ant-menu-theme-switch">
-        <span>Dark</span>
+        <span>{{ t('menu.dark') }}</span>
         <a-switch :style="{ marginLeft: '2px' }" size="small" :checked="theme.isDark" @change="onDarkChange" />
       </a-menu-item>
 
       <a-menu-item v-if="theme.isDark" id="change-theme-ultra" class="ant-menu-theme-switch">
-        <span>Ultra dark</span>
+        <span>{{ t('menu.ultraDark') }}</span>
         <a-checkbox :style="{ marginLeft: '2px' }" :checked="theme.isUltra" @click="onUltraClick" />
       </a-menu-item>
     </a-sub-menu>

+ 1 - 8
frontend/src/models/status.js

@@ -27,12 +27,6 @@ const XRAY_STATE_COLORS = {
   error: 'red',
 };
 
-const XRAY_STATE_MESSAGES = {
-  running: 'Xray is running',
-  stop: 'Xray is stopped',
-  error: 'Xray error',
-};
-
 export class Status {
   constructor(data) {
     this.cpu = new CurTotal(0, 0);
@@ -51,7 +45,7 @@ export class Status {
     this.uptime = 0;
     this.appUptime = 0;
     this.appStats = { threads: 0, mem: 0, uptime: 0 };
-    this.xray = { state: 'stop', stateMsg: '', errorMsg: '', version: '', color: '' };
+    this.xray = { state: 'stop', errorMsg: '', version: '', color: '' };
 
     if (data == null) return;
 
@@ -73,6 +67,5 @@ export class Status {
     this.appStats = data.appStats ?? this.appStats;
     this.xray = { ...this.xray, ...(data.xray || {}) };
     this.xray.color = XRAY_STATE_COLORS[this.xray.state] ?? 'gray';
-    this.xray.stateMsg = XRAY_STATE_MESSAGES[this.xray.state] ?? 'Unknown';
   }
 }

+ 14 - 7
frontend/src/pages/index/XrayStatusCard.vue

@@ -1,4 +1,5 @@
 <script setup>
+import { computed } from 'vue';
 import { useI18n } from 'vue-i18n';
 import {
   BarsOutlined,
@@ -9,7 +10,7 @@ import {
 
 const { t } = useI18n();
 
-defineProps({
+const props = defineProps({
   status: { type: Object, required: true },
   isMobile: { type: Boolean, default: false },
   ipLimitEnable: { type: Boolean, default: false },
@@ -17,10 +18,16 @@ defineProps({
 
 defineEmits(['stop-xray', 'restart-xray', 'open-logs', 'open-xray-logs', 'open-version-switch']);
 
-// Map xray.color → which animation class to apply on the badge dot.
-// The legacy .xray-*-animation classes only override the badge ring
-// color; the actual pulsing comes from .xray-processing-animation
-// (which animates .ant-badge-status-dot via @keyframes runningAnimation).
+const XRAY_STATE_KEYS = {
+  running: 'pages.index.xrayStatusRunning',
+  stop: 'pages.index.xrayStatusStop',
+  error: 'pages.index.xrayStatusError',
+};
+
+const stateText = computed(() =>
+  t(XRAY_STATE_KEYS[props.status.xray.state] ?? 'pages.index.xrayStatusUnknown'),
+);
+
 function badgeAnimationClass(color) {
   if (color === 'green') return 'xray-running-animation';
   if (color === 'orange') return 'xray-stop-animation';
@@ -43,7 +50,7 @@ function badgeAnimationClass(color) {
     <template #extra>
       <template v-if="status.xray.state !== 'error'">
         <a-badge status="processing" :class="['xray-processing-animation', badgeAnimationClass(status.xray.color)]"
-          :text="status.xray.stateMsg" :color="status.xray.color" />
+          :text="stateText" :color="status.xray.color" />
       </template>
       <template v-else>
         <a-popover>
@@ -60,7 +67,7 @@ function badgeAnimationClass(color) {
               {{ line }}
             </span>
           </template>
-          <a-badge status="processing" :text="status.xray.stateMsg" :color="status.xray.color"
+          <a-badge status="processing" :text="stateText" :color="status.xray.color"
             :class="['xray-processing-animation', 'xray-error-animation']" />
         </a-popover>
       </template>

+ 1 - 0
web/translation/ar-EG.json

@@ -94,6 +94,7 @@
     "ultraDark": "داكن جدًا",
     "dashboard": "نظرة عامة",
     "inbounds": "الإدخالات",
+    "nodes": "النودز",
     "settings": "إعدادات البانل",
     "xray": "إعدادات Xray",
     "logout": "تسجيل خروج",

+ 1 - 0
web/translation/es-ES.json

@@ -94,6 +94,7 @@
     "ultraDark": "Ultra Oscuro",
     "dashboard": "Estado del Sistema",
     "inbounds": "Entradas",
+    "nodes": "Nodos",
     "settings": "Configuraciones",
     "xray": "Ajustes Xray",
     "logout": "Cerrar Sesión",

+ 1 - 0
web/translation/id-ID.json

@@ -94,6 +94,7 @@
     "ultraDark": "Sangat Gelap",
     "dashboard": "Ikhtisar",
     "inbounds": "Masuk",
+    "nodes": "Node",
     "settings": "Pengaturan Panel",
     "xray": "Konfigurasi Xray",
     "logout": "Keluar",

+ 1 - 0
web/translation/ja-JP.json

@@ -94,6 +94,7 @@
     "ultraDark": "ウルトラダーク",
     "dashboard": "ダッシュボード",
     "inbounds": "インバウンド一覧",
+    "nodes": "ノード",
     "settings": "パネル設定",
     "xray": "Xray設定",
     "logout": "ログアウト",

+ 1 - 0
web/translation/pt-BR.json

@@ -94,6 +94,7 @@
     "ultraDark": "Ultra Escuro",
     "dashboard": "Visão Geral",
     "inbounds": "Inbounds",
+    "nodes": "Nós",
     "settings": "Panel Settings",
     "xray": "Xray Configs",
     "logout": "Sair",

+ 1 - 0
web/translation/ru-RU.json

@@ -94,6 +94,7 @@
     "ultraDark": "Очень темная",
     "dashboard": "Дашборд",
     "inbounds": "Подключения",
+    "nodes": "Узлы",
     "settings": "Настройки",
     "xray": "Настройки Xray",
     "logout": "Выход",

+ 1 - 0
web/translation/tr-TR.json

@@ -94,6 +94,7 @@
     "ultraDark": "Ultra Koyu",
     "dashboard": "Genel Bakış",
     "inbounds": "Gelenler",
+    "nodes": "Düğümler",
     "settings": "Panel Ayarları",
     "xray": "Xray Yapılandırmaları",
     "logout": "Çıkış Yap",

+ 1 - 0
web/translation/uk-UA.json

@@ -94,6 +94,7 @@
     "ultraDark": "Ультра темна",
     "dashboard": "Огляд",
     "inbounds": "Вхідні",
+    "nodes": "Вузли",
     "settings": "Параметри панелі",
     "xray": "Конфігурації Xray",
     "logout": "Вийти",

+ 1 - 0
web/translation/vi-VN.json

@@ -94,6 +94,7 @@
     "ultraDark": "Siêu tối",
     "dashboard": "Trạng thái hệ thống",
     "inbounds": "Đầu vào khách hàng",
+    "nodes": "Nút",
     "settings": "Cài đặt bảng điều khiển",
     "logout": "Đăng xuất",
     "xray": "Cài đặt Xray",

+ 1 - 0
web/translation/zh-CN.json

@@ -94,6 +94,7 @@
     "ultraDark": "超暗色",
     "dashboard": "系统状态",
     "inbounds": "入站列表",
+    "nodes": "节点",
     "settings": "面板设置",
     "xray": "Xray 设置",
     "logout": "退出登录",

+ 1 - 0
web/translation/zh-TW.json

@@ -94,6 +94,7 @@
     "ultraDark": "超深色",
     "dashboard": "系統狀態",
     "inbounds": "入站列表",
+    "nodes": "節點",
     "settings": "面板設定",
     "xray": "Xray 設定",
     "logout": "退出登入",

+ 1 - 2
web/web.go

@@ -280,8 +280,7 @@ func (s *Server) startTask() {
 
 	go func() {
 		time.Sleep(time.Second * 5)
-		// Statistics every 10 seconds, start the delay for 5 seconds for the first time, and staggered with the time to restart xray
-		s.cron.AddJob("@every 10s", job.NewXrayTrafficJob())
+		s.cron.AddJob("@every 5s", job.NewXrayTrafficJob())
 	}()
 
 	// check client ips from log file every 10 sec