diff --git a/luci-app-bandix/Makefile b/luci-app-bandix/Makefile
index 54f8fcb..debd21d 100644
--- a/luci-app-bandix/Makefile
+++ b/luci-app-bandix/Makefile
@@ -10,7 +10,7 @@ LUCI_DEPENDS:=+luci-base +luci-lib-jsonc +curl +bandix
PKG_MAINTAINER:=timsaya
-PKG_VERSION:=0.8.1
+PKG_VERSION:=0.8.2
PKG_RELEASE:=1
include $(TOPDIR)/feeds/luci/luci.mk
diff --git a/luci-app-bandix/htdocs/luci-static/resources/view/bandix/connection.js b/luci-app-bandix/htdocs/luci-static/resources/view/bandix/connection.js
index 4e7bb39..a7d396f 100644
--- a/luci-app-bandix/htdocs/luci-static/resources/view/bandix/connection.js
+++ b/luci-app-bandix/htdocs/luci-static/resources/view/bandix/connection.js
@@ -5,331 +5,33 @@
'require rpc';
'require poll';
-const translations = {
- 'zh-cn': {
- 'Bandix 连接监控': 'Bandix 连接监控',
- '正在加载数据...': '正在加载数据...',
- '无法获取数据': '无法获取数据',
- '连接监控': '连接监控',
- '设备连接统计': '设备连接统计',
- '全局连接统计': '全局连接统计',
- '设备': '设备',
- 'IP地址': 'IP地址',
- 'MAC地址': 'MAC地址',
- '活跃TCP': '活跃TCP',
- '活跃UDP': '活跃UDP',
- '已关闭TCP': '已关闭TCP',
- '总连接数': '总连接数',
- '最后更新': '最后更新',
- '总连接数统计': '总连接数统计',
- 'TCP连接数': 'TCP连接数',
- 'UDP连接数': 'UDP连接数',
- '已建立TCP': '已建立TCP',
- 'TIME_WAIT TCP': 'TIME_WAIT TCP',
- 'CLOSE_WAIT TCP': 'CLOSE_WAIT TCP',
- '设备总数': '设备总数',
- '连接监控未启用': '连接监控未启用',
- '请在设置中启用连接监控功能': '请在设置中启用连接监控功能',
- '前往设置': '前往设置',
- '无数据': '无数据',
- '未知设备': '未知设备',
- '在线设备': '在线设备',
- '从未上线': '从未上线',
- '刚刚': '刚刚',
- '分钟前': '分钟前',
- '小时前': '小时前',
- '天前': '天前',
- '个月前': '个月前',
- '年前': '年前',
- '最后上线': '最后上线',
- '列表只显示局域网设备连接,数据可能和总连接数不一致。': '列表只显示局域网设备连接,数据可能和总连接数不一致。',
- 'TCP 状态详情': 'TCP 状态详情'
- },
- 'zh-tw': {
- 'Bandix 连接监控': 'Bandix 連接監控',
- '正在加载数据...': '正在載入資料...',
- '无法获取数据': '無法獲取資料',
- '连接监控': '連接監控',
- '设备连接统计': '設備連接統計',
- '全局连接统计': '全局連接統計',
- '设备': '設備',
- 'IP地址': 'IP地址',
- 'MAC地址': 'MAC地址',
- '活跃TCP': '活躍TCP',
- '活跃UDP': '活躍UDP',
- '已关闭TCP': '已關閉TCP',
- '总连接数': '總連接數',
- '最后更新': '最後更新',
- '总连接数统计': '總連接數統計',
- 'TCP连接数': 'TCP連接數',
- 'UDP连接数': 'UDP連接數',
- '已建立TCP': '已建立TCP',
- 'TIME_WAIT TCP': 'TIME_WAIT TCP',
- 'CLOSE_WAIT TCP': 'CLOSE_WAIT TCP',
- '设备总数': '設備總數',
- '连接监控未启用': '連接監控未啟用',
- '请在设置中启用连接监控功能': '請在設置中啟用連接監控功能',
- '前往设置': '前往設置',
- '无数据': '無數據',
- '未知设备': '未知設備',
- '在线设备': '在線設備',
- '从未上线': '從未上線',
- '刚刚': '剛剛',
- '分钟前': '分鐘前',
- '小时前': '小時前',
- '天前': '天前',
- '个月前': '個月前',
- '年前': '年前',
- '最后上线': '最後上線',
- '列表只显示局域网设备连接,数据可能和总连接数不一致。': '列表只顯示局域網設備連接,數據可能和總連接數不一致。',
- 'TCP 状态详情': 'TCP 狀態詳情'
- },
- 'en': {
- 'Bandix 连接监控': 'Bandix Connection Monitor',
- '正在加载数据...': 'Loading data...',
- '无法获取数据': 'Unable to fetch data',
- '连接监控': 'Connection Monitor',
- '设备连接统计': 'Device Connection Statistics',
- '全局连接统计': 'Global Connection Statistics',
- '设备': 'Device',
- 'IP地址': 'IP Address',
- 'MAC地址': 'MAC Address',
- '活跃TCP': 'Active TCP',
- '活跃UDP': 'Active UDP',
- '已关闭TCP': 'Closed TCP',
- '总连接数': 'Total Connections',
- '最后更新': 'Last Updated',
- '总连接数统计': 'Total Connections',
- 'TCP连接数': 'TCP Connections',
- 'UDP连接数': 'UDP Connections',
- '已建立TCP': 'Established TCP',
- 'TIME_WAIT TCP': 'TIME_WAIT TCP',
- 'CLOSE_WAIT TCP': 'CLOSE_WAIT TCP',
- '设备总数': 'Total Devices',
- '连接监控未启用': 'Connection Monitor Disabled',
- '请在设置中启用连接监控功能': 'Please enable connection monitoring in settings',
- '前往设置': 'Go to Settings',
- '无数据': 'No Data',
- '未知设备': 'Unknown Device',
- '在线设备': 'Online Devices',
- '从未上线': 'Never Online',
- '刚刚': 'Just now',
- '分钟前': 'minutes ago',
- '小时前': 'hours ago',
- '天前': 'days ago',
- '个月前': 'months ago',
- '年前': 'years ago',
- '最后上线': 'Last seen',
- '列表只显示局域网设备连接,数据可能和总连接数不一致。': 'List only shows LAN device connections, data may differ from total connections.',
- 'TCP 状态详情': 'TCP Status Details'
- },
- 'fr': {
- 'Bandix 连接监控': 'Surveillance de Connexion Bandix',
- '正在加载数据...': 'Chargement des données...',
- '无法获取数据': 'Impossible de récupérer les données',
- '连接监控': 'Surveillance des Connexions',
- '设备连接统计': 'Statistiques de Connexion des Appareils',
- '全局连接统计': 'Statistiques de Connexion Globales',
- '设备': 'Appareil',
- 'IP地址': 'Adresse IP',
- 'MAC地址': 'Adresse MAC',
- '活跃TCP': 'TCP Actif',
- '活跃UDP': 'UDP Actif',
- '已关闭TCP': 'TCP Fermé',
- '总连接数': 'Total des Connexions',
- '最后更新': 'Dernière Mise à Jour',
- '总连接数统计': 'Total des Connexions',
- 'TCP连接数': 'Connexions TCP',
- 'UDP连接数': 'Connexions UDP',
- '已建立TCP': 'TCP Établi',
- 'TIME_WAIT TCP': 'TCP TIME_WAIT',
- 'CLOSE_WAIT TCP': 'TCP CLOSE_WAIT',
- '设备总数': 'Total des Appareils',
- '连接监控未启用': 'Surveillance des Connexions Désactivée',
- '请在设置中启用连接监控功能': 'Veuillez activer la surveillance des connexions dans les paramètres',
- '前往设置': 'Aller aux Paramètres',
- '无数据': 'Aucune Donnée',
- '未知设备': 'Appareil Inconnu',
- '在线设备': 'Appareils En Ligne',
- '从未上线': 'Jamais En Ligne',
- '刚刚': 'À l\'instant',
- '分钟前': 'il y a minutes',
- '小时前': 'il y a heures',
- '天前': 'il y a jours',
- '个月前': 'il y a mois',
- '年前': 'il y a années',
- '最后上线': 'Dernier Vue',
- '列表只显示局域网设备连接,数据可能和总连接数不一致。': 'La liste ne montre que les connexions des appareils LAN, les données peuvent différer du total des connexions.',
- 'TCP 状态详情': 'Détails du Statut TCP'
- },
- 'ja': {
- 'Bandix 连接监控': 'Bandix 接続監視',
- '正在加载数据...': 'データを読み込み中...',
- '无法获取数据': 'データを取得できません',
- '连接监控': '接続監視',
- '设备连接统计': 'デバイス接続統計',
- '全局连接统计': 'グローバル接続統計',
- '设备': 'デバイス',
- 'IP地址': 'IPアドレス',
- 'MAC地址': 'MACアドレス',
- '活跃TCP': 'アクティブTCP',
- '活跃UDP': 'アクティブUDP',
- '已关闭TCP': 'クローズドTCP',
- '总连接数': '総接続数',
- '最后更新': '最終更新',
- '总连接数统计': '総接続数',
- 'TCP连接数': 'TCP接続数',
- 'UDP连接数': 'UDP接続数',
- '已建立TCP': 'TCP確立済',
- 'TIME_WAIT TCP': 'TCP TIME_WAIT',
- 'CLOSE_WAIT TCP': 'TCP CLOSE_WAIT',
- '设备总数': '総デバイス数',
- '连接监控未启用': '接続監視が無効',
- '请在设置中启用连接监控功能': '設定で接続監視機能を有効にしてください',
- '前往设置': '設定に移動',
- '无数据': 'データなし',
- '未知设备': '不明なデバイス',
- '在线设备': 'オンラインデバイス',
- '从未上线': '未接続',
- '刚刚': 'たった今',
- '分钟前': '分前',
- '小时前': '時間前',
- '天前': '日前',
- '个月前': 'ヶ月前',
- '年前': '年前',
- '最后上线': '最後の接続',
- '列表只显示局域网设备连接,数据可能和总连接数不一致。': 'リストはLANデバイスの接続のみを表示し、データは総接続数と異なる場合があります。',
- 'TCP 状态详情': 'TCP状態詳細'
- },
- 'ru': {
- 'Bandix 连接监控': 'Мониторинг Соединений Bandix',
- '正在加载数据...': 'Загрузка данных...',
- '无法获取数据': 'Не удалось получить данные',
- '连接监控': 'Мониторинг Соединений',
- '设备连接统计': 'Статистика Соединений Устройств',
- '全局连接统计': 'Глобальная Статистика Соединений',
- '设备': 'Устройство',
- 'IP地址': 'IP-адрес',
- 'MAC地址': 'MAC-адрес',
- '活跃TCP': 'Активные TCP',
- '活跃UDP': 'Активные UDP',
- '已关闭TCP': 'Закрытые TCP',
- '总连接数': 'Всего Соединений',
- '最后更新': 'Последнее Обновление',
- '总连接数统计': 'Всего Соединений',
- 'TCP连接数': 'TCP Соединения',
- 'UDP连接数': 'UDP Соединения',
- '已建立TCP': 'Установленные TCP',
- 'TIME_WAIT TCP': 'TCP TIME_WAIT',
- 'CLOSE_WAIT TCP': 'TCP CLOSE_WAIT',
- '设备总数': 'Всего Устройств',
- '连接监控未启用': 'Мониторинг Соединений Отключен',
- '请在设置中启用连接监控功能': 'Пожалуйста, включите мониторинг соединений в настройках',
- '前往设置': 'Перейти к Настройкам',
- '无数据': 'Нет Данных',
- '未知设备': 'Неизвестное Устройство',
- '在线设备': 'Устройства Онлайн',
- '从未上线': 'Никогда Не Было Онлайн',
- '刚刚': 'Только что',
- '分钟前': 'минут назад',
- '小时前': 'часов назад',
- '天前': 'дней назад',
- '个月前': 'месяцев назад',
- '年前': 'лет назад',
- '最后上线': 'Последний раз видели',
- '列表只显示局域网设备连接,数据可能和总连接数不一致。': 'Список показывает только соединения LAN-устройств, данные могут отличаться от общего количества соединений.',
- 'TCP 状态详情': 'Детали Статуса TCP'
- }
-};
-function getTranslation(key, language) {
- return translations[language]?.[key] || key;
-}
-
-// 获取系统语言
-function getSystemLanguage() {
- var luciLang = uci.get('luci', 'main', 'lang');
- if (luciLang && translations[luciLang]) {
- return luciLang;
- }
- var systemLang = document.documentElement.lang || 'en';
- if (translations[systemLang]) {
- return systemLang;
- }
- return 'en';
-}
-
-// 检查是否为暗黑模式
-function isDarkMode() {
- // 首先检查用户设置的主题
- var userTheme = uci.get('bandix', 'general', 'theme');
- if (userTheme) {
- if (userTheme === 'dark') {
- return true;
- } else if (userTheme === 'light') {
- return false;
- }
- // 如果是 'auto',继续检查系统主题
- }
-
- // 获取 LuCI 主题设置
- var mediaUrlBase = uci.get('luci', 'main', 'mediaurlbase');
- if (mediaUrlBase && mediaUrlBase.toLowerCase().includes('dark')) {
- return true;
- }
-
- // 如果是 argon 主题,检查 argon 配置
- if (mediaUrlBase && mediaUrlBase.toLowerCase().includes('argon')) {
- var argonMode = uci.get('argon', '@global[0]', 'mode');
- if (argonMode) {
- if (argonMode.toLowerCase() === 'dark') {
- return true;
- } else if (argonMode.toLowerCase() === 'light') {
- return false;
- }
- // 如果是 'normal' 或 'auto',使用浏览器检测系统颜色偏好
- if (argonMode.toLowerCase() === 'normal' || argonMode.toLowerCase() === 'auto') {
- if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
- return true;
- }
- return false;
- }
- }
- }
-
- // 默认情况下也使用浏览器检测系统颜色偏好
- if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
- return true;
- }
-
- return false;
-}
+// 暗色模式检测已改为使用 CSS 媒体查询 @media (prefers-color-scheme: dark)
// 格式化时间戳
function formatTimestamp(timestamp) {
- if (!timestamp) return getTranslation('从未上线', getSystemLanguage());
+ if (!timestamp) return _('Never Online');
var now = Math.floor(Date.now() / 1000);
var diff = now - timestamp;
- var language = getSystemLanguage();
if (diff < 60) {
- return getTranslation('刚刚', language);
+ return _('Just now');
} else if (diff < 3600) {
var minutes = Math.floor(diff / 60);
- return minutes + ' ' + getTranslation('分钟前', language);
+ return minutes + ' ' + _('minutes ago');
} else if (diff < 86400) {
var hours = Math.floor(diff / 3600);
- return hours + ' ' + getTranslation('小时前', language);
+ return hours + ' ' + _('hours ago');
} else if (diff < 2592000) {
var days = Math.floor(diff / 86400);
- return days + ' ' + getTranslation('天前', language);
+ return days + ' ' + _('days ago');
} else if (diff < 31536000) {
var months = Math.floor(diff / 2592000);
- return months + ' ' + getTranslation('个月前', language);
+ return months + ' ' + _('months ago');
} else {
var years = Math.floor(diff / 31536000);
- return years + ' ' + getTranslation('年前', language);
+ return years + ' ' + _('years ago');
}
}
@@ -338,7 +40,7 @@ function formatDeviceName(device) {
if (device.hostname && device.hostname !== '') {
return device.hostname;
}
- return device.ip_address || device.mac_address || getTranslation('未知设备', getSystemLanguage());
+ return device.ip_address || device.mac_address || _('Unknown Device');
}
// RPC调用
@@ -360,63 +62,42 @@ return view.extend({
},
render: function (data) {
- var language = uci.get('bandix', 'general', 'language');
- if (!language || language === 'auto') {
- language = getSystemLanguage();
- }
- var darkMode = isDarkMode();
var connectionEnabled = uci.get('bandix', 'connections', 'enabled') === '1';
// 创建样式
var style = E('style', {}, `
.bandix-connection-container {
- margin: 0;
- padding: 16px;
- background-color: ${darkMode ? '#1a1a1a' : '#f8fafc'};
- min-height: calc(100vh - 100px);
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
- color: ${darkMode ? '#e2e8f0' : '#1f2937'};
- border-radius: 8px;
}
.bandix-header {
display: flex;
align-items: center;
justify-content: space-between;
- margin-bottom: 20px;
}
.bandix-title {
font-size: 1.5rem;
font-weight: 600;
- color: ${darkMode ? '#f1f5f9' : '#1f2937'};
margin: 0;
}
.bandix-badge {
- background-color: ${darkMode ? '#2a2a2a' : '#f8fafc'};
- border: 1px solid ${darkMode ? '#444444' : '#cbd5e1'};
border-radius: 4px;
padding: 4px 10px;
font-size: 0.875rem;
- color: ${darkMode ? '#d0d0d0' : '#475569'};
}
.bandix-alert {
- background-color: ${darkMode ? '#2a2a2a' : '#eff6ff'};
- border-left: 3px solid ${darkMode ? '#3b82f6' : '#2563eb'};
border-radius: 4px;
padding: 10px 12px;
- margin-bottom: 16px;
display: flex;
align-items: center;
gap: 10px;
- color: ${darkMode ? '#d0d0d0' : '#1e293b'};
font-size: 0.875rem;
}
.bandix-alert-icon {
- color: ${darkMode ? '#60a5fa' : '#2563eb'};
font-size: 0.875rem;
font-weight: 700;
width: 18px;
@@ -424,72 +105,62 @@ return view.extend({
display: flex;
align-items: center;
justify-content: center;
- border: 2px solid currentColor;
border-radius: 50%;
flex-shrink: 0;
}
- .bandix-card {
- background-color: ${darkMode ? '#2a2a2a' : 'white'};
- border-radius: 8px;
- border: 1px solid ${darkMode ? '#444444' : '#e2e8f0'};
- box-shadow: 0 2px 8px rgba(0, 0, 0, ${darkMode ? '0.3' : '0.08'});
- margin-bottom: 24px;
- overflow: hidden;
- }
-
- .bandix-card-header {
- padding: 16px;
- border-bottom: 1px solid ${darkMode ? '#444444' : '#e2e8f0'};
- background-color: ${darkMode ? '#2a2a2a' : '#f8fafc'};
- }
-
- .bandix-card-title {
- font-size: 1.125rem;
- font-weight: 600;
- color: ${darkMode ? '#f1f5f9' : '#1f2937'};
- margin: 0;
- display: flex;
- align-items: center;
- gap: 8px;
- }
-
- .bandix-card-body {
- padding: 0;
- }
-
.stats-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16px;
- margin-bottom: 24px;
+ margin-top: 0;
}
- .stats-card {
- background-color: ${darkMode ? '#2a2a2a' : 'white'};
- border-radius: 8px;
- padding: 20px;
- border: 1px solid ${darkMode ? '#444444' : '#e2e8f0'};
- box-shadow: 0 2px 8px rgba(0, 0, 0, ${darkMode ? '0.3' : '0.08'});
- display: flex;
- flex-direction: column;
- align-items: center;
- text-align: center;
+
+ .bandix-connection-container > .cbi-section:first-of-type {
+ margin-top: 0;
+ }
+
+ .bandix-connection-container > .cbi-section:last-of-type {
+ margin-bottom: 0;
}
.stats-card-title {
font-size: 0.875rem;
font-weight: 600;
- color: ${darkMode ? '#94a3b8' : '#64748b'};
+ opacity: 0.7;
margin: 0 0 12px 0;
text-transform: uppercase;
letter-spacing: 0.025em;
}
+ .stats-grid .cbi-section {
+ padding: 16px;
+ border: 1px solid rgba(0, 0, 0, 0.1);
+ border-radius: 8px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
+ transition: box-shadow 0.2s ease, transform 0.2s ease, border-color 0.2s ease;
+ }
+
+ .stats-grid .cbi-section:hover {
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
+ transform: translateY(-2px);
+ }
+
+ @media (prefers-color-scheme: dark) {
+ .stats-grid .cbi-section {
+ border-color: rgba(255, 255, 255, 0.15);
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
+ }
+
+ .stats-grid .cbi-section:hover {
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
+ }
+ }
+
.stats-card-main-value {
font-size: 2.25rem;
font-weight: 700;
- color: ${darkMode ? '#f1f5f9' : '#1f2937'};
margin: 0 0 8px 0;
line-height: 1;
}
@@ -510,28 +181,23 @@ return view.extend({
}
.stats-detail-label {
- color: ${darkMode ? '#9ca3af' : '#6b7280'};
+ opacity: 0.7;
font-weight: 500;
}
.stats-detail-value {
font-weight: 600;
- color: ${darkMode ? '#e2e8f0' : '#374151'};
}
.bandix-table {
width: 100%;
- border-collapse: collapse;
- background-color: transparent;
table-layout: fixed;
}
.bandix-table th {
- background-color: ${darkMode ? '#2a2a2a' : '#f8fafc'};
padding: 12px 16px;
text-align: left;
font-weight: 600;
- color: ${darkMode ? '#d0d0d0' : '#475569'};
border: none;
font-size: 0.875rem;
white-space: nowrap;
@@ -545,11 +211,9 @@ return view.extend({
.bandix-table td {
padding: 12px 16px;
- border-bottom: 1px solid ${darkMode ? '#333333' : '#f1f5f9'};
vertical-align: middle;
word-break: break-word;
overflow-wrap: break-word;
- color: ${darkMode ? '#d0d0d0' : '#334155'};
}
@@ -581,7 +245,6 @@ return view.extend({
.device-name {
font-weight: 600;
- color: ${darkMode ? '#f1f5f9' : '#1f2937'};
display: flex;
align-items: center;
gap: 8px;
@@ -589,12 +252,12 @@ return view.extend({
}
.device-ip {
- color: ${darkMode ? '#94a3b8' : '#6b7280'};
+ opacity: 0.7;
font-size: 0.875rem;
}
.device-mac {
- color: ${darkMode ? '#64748b' : '#9ca3af'};
+ opacity: 0.6;
font-size: 0.75rem;
}
@@ -637,43 +300,18 @@ return view.extend({
.tcp-status-value {
font-weight: 600;
- color: ${darkMode ? '#e2e8f0' : '#374151'};
}
.loading-state {
text-align: center;
padding: 40px;
- color: ${darkMode ? '#94a3b8' : '#6b7280'};
+ opacity: 0.7;
font-style: italic;
}
.error-state {
text-align: center;
padding: 40px;
- color: ${darkMode ? '#f87171' : '#ef4444'};
- }
-
- .btn {
- display: inline-flex;
- align-items: center;
- gap: 8px;
- padding: 8px 16px;
- border-radius: 4px;
- font-size: 0.875rem;
- font-weight: 500;
- text-decoration: none;
- border: none;
- cursor: pointer;
- transition: all 0.15s ease;
- }
-
- .btn-primary {
- background-color: #3b82f6;
- color: white;
- }
-
- .btn-primary:hover {
- background-color: #2563eb;
}
`);
document.head.appendChild(style);
@@ -682,28 +320,27 @@ return view.extend({
// 页面标题
var header = E('div', { 'class': 'bandix-header' }, [
- E('h1', { 'class': 'bandix-title' }, getTranslation('Bandix 连接监控', language))
+ E('h1', { 'class': 'bandix-title' }, _('Bandix Connection Monitor'))
]);
container.appendChild(header);
// 检查连接监控是否启用
if (!connectionEnabled) {
var alertDiv = E('div', { 'class': 'bandix-alert' }, [
- E('span', { 'class': 'bandix-alert-icon' }, '!'),
E('div', {}, [
- E('strong', {}, getTranslation('连接监控未启用', language)),
+ E('strong', {}, _('Connection Monitor Disabled')),
E('p', { 'style': 'margin: 4px 0 0 0;' },
- getTranslation('请在设置中启用连接监控功能', language))
+ _('Please enable connection monitoring in settings'))
])
]);
container.appendChild(alertDiv);
- var settingsCard = E('div', { 'class': 'bandix-card' }, [
- E('div', { 'class': 'bandix-card-body', 'style': 'text-align: center;' }, [
+ var settingsCard = E('div', { 'class': 'cbi-section' }, [
+ E('div', { 'style': 'text-align: center; padding: 16px;' }, [
E('a', {
'href': '/cgi-bin/luci/admin/network/bandix/settings',
- 'class': 'btn btn-primary'
- }, getTranslation('前往设置', language))
+ 'class': 'cbi-button cbi-button-positive'
+ }, _('Go to Settings'))
])
]);
container.appendChild(settingsCard);
@@ -712,19 +349,18 @@ return view.extend({
// 添加提示信息
var infoAlert = E('div', { 'class': 'bandix-alert' }, [
- E('span', { 'class': 'bandix-alert-icon' }, '!'),
- E('span', {}, getTranslation('列表只显示局域网设备连接,数据可能和总连接数不一致。', language))
+ E('span', {}, _('List only shows LAN device connections, data may differ from total connections.'))
]);
container.appendChild(infoAlert);
// 全局统计卡片
var statsGrid = E('div', { 'class': 'stats-grid' }, [
- E('div', { 'class': 'stats-card' }, [
- E('div', { 'class': 'stats-card-title' }, getTranslation('总连接数统计', language)),
+ E('div', { 'class': 'cbi-section' }, [
+ E('div', { 'class': 'stats-card-title' }, _('Total Connections')),
E('div', { 'class': 'stats-card-main-value', 'id': 'total-connections' }, '-')
]),
- E('div', { 'class': 'stats-card' }, [
- E('div', { 'class': 'stats-card-title' }, getTranslation('TCP连接数', language)),
+ E('div', { 'class': 'cbi-section' }, [
+ E('div', { 'class': 'stats-card-title' }, _('TCP Connections')),
E('div', { 'class': 'stats-card-main-value', 'id': 'tcp-connections' }, '-'),
E('div', { 'class': 'stats-card-details' }, [
E('div', { 'class': 'stats-detail-row' }, [
@@ -741,25 +377,26 @@ return view.extend({
])
])
]),
- E('div', { 'class': 'stats-card' }, [
- E('div', { 'class': 'stats-card-title' }, getTranslation('UDP连接数', language)),
+ E('div', { 'class': 'cbi-section' }, [
+ E('div', { 'class': 'stats-card-title' }, _('UDP Connections')),
E('div', { 'class': 'stats-card-main-value', 'id': 'udp-connections' }, '-')
])
]);
container.appendChild(statsGrid);
// 设备连接统计表格
- var deviceCard = E('div', { 'class': 'bandix-card' }, [
- E('div', { 'class': 'bandix-card-body' }, [
+ var deviceCard = E('div', { 'class': 'cbi-section' }, [
+ E('h3', {}, _('Device Connection Statistics')),
+ E('div', {}, [
E('div', { 'id': 'device-table-container' }, [
E('table', { 'class': 'bandix-table' }, [
E('thead', {}, [
E('tr', {}, [
- E('th', {}, getTranslation('设备', language)),
+ E('th', {}, _('Device')),
E('th', {}, 'TCP'),
E('th', {}, 'UDP'),
- E('th', {}, getTranslation('TCP 状态详情', language)),
- E('th', {}, getTranslation('总连接数', language))
+ E('th', {}, _('TCP Status Details')),
+ E('th', {}, _('Total Connections'))
])
]),
E('tbody', {})
@@ -788,18 +425,18 @@ return view.extend({
if (!devices || devices.length === 0) {
container.innerHTML = '';
container.appendChild(E('div', { 'class': 'loading-state' },
- getTranslation('无数据', language)));
+ _('No Data')));
return;
}
var table = E('table', { 'class': 'bandix-table' }, [
E('thead', {}, [
E('tr', {}, [
- E('th', {}, getTranslation('设备', language)),
+ E('th', {}, _('Device')),
E('th', {}, 'TCP'),
E('th', {}, 'UDP'),
- E('th', {}, getTranslation('TCP 状态详情', language)),
- E('th', {}, getTranslation('总连接数', language))
+ E('th', {}, _('TCP Status Details')),
+ E('th', {}, _('Total Connections'))
])
]),
E('tbody', {}, devices.map(function (device) {
@@ -855,11 +492,11 @@ return view.extend({
updateGlobalStats(result.data.global_stats);
updateDeviceTable(result.data.devices);
} else {
- showError(getTranslation('无法获取数据', language));
+ showError(_('Unable to fetch data'));
}
}).catch(function (error) {
console.error('Failed to load connection data:', error);
- showError(getTranslation('无法获取数据', language));
+ showError(_('Unable to fetch data'));
});
}
@@ -869,6 +506,61 @@ return view.extend({
// 立即执行一次,不等待轮询
updateConnectionData();
+ // 自动适应主题背景色和文字颜色的函数
+ function applyThemeColors() {
+ try {
+ var mainElement = document.querySelector('.main') || document.body;
+ var computedStyle = window.getComputedStyle(mainElement);
+ var bgColor = computedStyle.backgroundColor;
+
+ // 如果父元素有背景色,应用到容器和卡片
+ if (bgColor && bgColor !== 'rgba(0, 0, 0, 0)' && bgColor !== 'transparent') {
+ var containerEl = document.querySelector('.bandix-connection-container');
+ if (containerEl) {
+ containerEl.style.backgroundColor = bgColor;
+ }
+
+ // 应用到表格表头
+ var tableHeaders = document.querySelectorAll('.bandix-table th');
+ tableHeaders.forEach(function(th) {
+ th.style.backgroundColor = bgColor;
+ });
+ }
+
+ // 检测文字颜色并应用
+ var textColor = computedStyle.color;
+ if (textColor && textColor !== 'rgba(0, 0, 0, 0)') {
+ var containerEl = document.querySelector('.bandix-connection-container');
+ if (containerEl) {
+ containerEl.style.color = textColor;
+ }
+ }
+ } catch (e) {
+ // 如果检测失败,使用默认值
+ console.log('Theme adaptation:', e);
+ }
+ }
+
+ // 初始应用主题颜色
+ setTimeout(applyThemeColors, 100);
+
+ // 监听 DOM 变化,自动应用到新创建的元素
+ if (typeof MutationObserver !== 'undefined') {
+ var observer = new MutationObserver(function(mutations) {
+ applyThemeColors();
+ });
+
+ setTimeout(function() {
+ var container = document.querySelector('.bandix-connection-container');
+ if (container) {
+ observer.observe(container, {
+ childList: true,
+ subtree: true
+ });
+ }
+ }, 200);
+ }
+
return container;
}
});
\ No newline at end of file
diff --git a/luci-app-bandix/htdocs/luci-static/resources/view/bandix/dns.js b/luci-app-bandix/htdocs/luci-static/resources/view/bandix/dns.js
index 8ef7423..f539dc9 100644
--- a/luci-app-bandix/htdocs/luci-static/resources/view/bandix/dns.js
+++ b/luci-app-bandix/htdocs/luci-static/resources/view/bandix/dns.js
@@ -5,507 +5,8 @@
'require rpc';
'require poll';
-const translations = {
- 'zh-cn': {
- 'Bandix DNS 监控': 'Bandix DNS 监控',
- '正在加载数据...': '正在加载数据...',
- '无法获取数据': '无法获取数据',
- 'DNS 监控': 'DNS 监控',
- 'DNS 查询记录': 'DNS 查询记录',
- 'DNS 统计信息': 'DNS 统计信息',
- 'DNS监控未启用': 'DNS监控未启用',
- '请在设置中启用DNS监控功能': '请在设置中启用DNS监控功能',
- '前往设置': '前往设置',
- '无数据': '无数据',
- '时间': '时间',
- '域名': '域名',
- '查询类型': '查询类型',
- '类型': '类型',
- '响应码': '响应码',
- '响应时间': '响应时间',
- '源IP': '源IP',
- '目标IP': '目标IP',
- '设备': '设备',
- '响应IP': '响应IP',
- '响应结果': '响应结果',
- 'DNS服务器': 'DNS服务器',
- '查询': '查询',
- '响应': '响应',
- '过滤': '过滤',
- '域名过滤': '域名过滤',
- '设备过滤': '设备过滤',
- 'DNS服务器过滤': 'DNS服务器过滤',
- '类型过滤': '类型过滤',
- '全部': '全部',
- '仅查询': '仅查询',
- '仅响应': '仅响应',
- '搜索': '搜索',
- '搜索域名': '搜索域名',
- '搜索设备': '搜索设备',
- '搜索DNS服务器': '搜索DNS服务器',
- '清除': '清除',
- '上一页': '上一页',
- '下一页': '下一页',
- '第': '第',
- '页,共': '页,共',
- '共': '共',
- '条记录': '条记录',
- '每页显示': '每页显示',
- '条': '条',
- '总查询数': '总查询数',
- '总响应数': '总响应数',
- '有响应查询': '有响应查询',
- '无响应查询': '无响应查询',
- '平均响应时间': '平均响应时间',
- '最快响应时间': '最快响应时间',
- '最慢响应时间': '最慢响应时间',
- '响应时间': '响应时间',
- '成功率': '成功率',
- '成功': '成功',
- '失败': '失败',
- '最常查询域名': '最常查询域名',
- '最常用查询类型': '最常用查询类型',
- '最活跃设备': '最活跃设备',
- '最常用DNS服务器': '最常用DNS服务器',
- '唯一设备数': '唯一设备数',
- '时间范围': '时间范围',
- '毫秒': '毫秒',
- '分钟': '分钟',
- '刷新': '刷新',
- '未知设备': '未知设备',
- '成功': '成功',
- '域名未找到': '域名未找到',
- '服务器错误': '服务器错误',
- '格式错误': '格式错误',
- '拒绝': '拒绝',
- '其他': '其他'
- },
- 'zh-tw': {
- 'Bandix DNS 监控': 'Bandix DNS 監控',
- '正在加载数据...': '正在載入資料...',
- '无法获取数据': '無法獲取資料',
- 'DNS 监控': 'DNS 監控',
- 'DNS 查询记录': 'DNS 查詢記錄',
- 'DNS 统计信息': 'DNS 統計資訊',
- 'DNS监控未启用': 'DNS監控未啟用',
- '请在设置中启用DNS监控功能': '請在設置中啟用DNS監控功能',
- '前往设置': '前往設置',
- '无数据': '無數據',
- '时间': '時間',
- '域名': '域名',
- '查询类型': '查詢類型',
- '类型': '類型',
- '响应码': '響應碼',
- '响应时间': '響應時間',
- '源IP': '源IP',
- '目标IP': '目標IP',
- '设备': '設備',
- '响应IP': '響應IP',
- '响应结果': '響應結果',
- 'DNS服务器': 'DNS伺服器',
- '查询': '查詢',
- '响应': '響應',
- '过滤': '過濾',
- '域名过滤': '域名過濾',
- '设备过滤': '設備過濾',
- 'DNS服务器过滤': 'DNS伺服器過濾',
- '类型过滤': '類型過濾',
- '全部': '全部',
- '仅查询': '僅查詢',
- '仅响应': '僅響應',
- '搜索': '搜尋',
- '搜索域名': '搜尋域名',
- '搜索设备': '搜尋設備',
- '搜索DNS服务器': '搜尋DNS伺服器',
- '清除': '清除',
- '上一页': '上一頁',
- '下一页': '下一頁',
- '第': '第',
- '页,共': '頁,共',
- '共': '共',
- '条记录': '條記錄',
- '每页显示': '每頁顯示',
- '条': '條',
- '总查询数': '總查詢數',
- '总响应数': '總響應數',
- '有响应查询': '有響應查詢',
- '无响应查询': '無響應查詢',
- '平均响应时间': '平均響應時間',
- '最快响应时间': '最快響應時間',
- '最慢响应时间': '最慢響應時間',
- '响应时间': '響應時間',
- '成功率': '成功率',
- '成功': '成功',
- '失败': '失敗',
- '最常查询域名': '最常查詢域名',
- '最常用查询类型': '最常用查詢類型',
- '最活跃设备': '最活躍設備',
- '最常用DNS服务器': '最常用DNS伺服器',
- '唯一设备数': '唯一設備數',
- '时间范围': '時間範圍',
- '毫秒': '毫秒',
- '分钟': '分鐘',
- '刷新': '重新整理',
- '未知设备': '未知設備',
- '成功': '成功',
- '域名未找到': '域名未找到',
- '服务器错误': '伺服器錯誤',
- '格式错误': '格式錯誤',
- '拒绝': '拒絕',
- '其他': '其他'
- },
- 'en': {
- 'Bandix DNS 监控': 'Bandix DNS Monitor',
- '正在加载数据...': 'Loading data...',
- '无法获取数据': 'Unable to fetch data',
- 'DNS 监控': 'DNS Monitor',
- 'DNS 查询记录': 'DNS Query Records',
- 'DNS 统计信息': 'DNS Statistics',
- 'DNS监控未启用': 'DNS Monitoring Disabled',
- '请在设置中启用DNS监控功能': 'Please enable DNS monitoring in settings',
- '前往设置': 'Go to Settings',
- '无数据': 'No Data',
- '时间': 'Time',
- '域名': 'Domain',
- '查询类型': 'Query Type',
- '类型': 'Type',
- '响应码': 'Response Code',
- '响应时间': 'Response Time',
- '源IP': 'Source IP',
- '目标IP': 'Destination IP',
- '设备': 'Device',
- '响应IP': 'Response IPs',
- '响应结果': 'Response Result',
- 'DNS服务器': 'DNS Server',
- '查询': 'Query',
- '响应': 'Response',
- '过滤': 'Filter',
- '域名过滤': 'Domain Filter',
- '设备过滤': 'Device Filter',
- 'DNS服务器过滤': 'DNS Server Filter',
- '类型过滤': 'Type Filter',
- '全部': 'All',
- '仅查询': 'Queries Only',
- '仅响应': 'Responses Only',
- '搜索': 'Search',
- '搜索域名': 'Search Domain',
- '搜索设备': 'Search Device',
- '搜索DNS服务器': 'Search DNS Server',
- '清除': 'Clear',
- '上一页': 'Previous',
- '下一页': 'Next',
- '第': 'Page',
- '页,共': 'of',
- '共': 'Total',
- '条记录': 'records',
- '每页显示': 'Per Page',
- '条': '',
- '总查询数': 'Total Queries',
- '总响应数': 'Total Responses',
- '有响应查询': 'Queries with Response',
- '无响应查询': 'Queries without Response',
- '平均响应时间': 'Avg Response Time',
- '最快响应时间': 'Min Response Time',
- '最慢响应时间': 'Max Response Time',
- '响应时间': 'Response Time',
- '成功率': 'Success Rate',
- '成功': 'Success',
- '失败': 'Failure',
- '最常查询域名': 'Top Domains',
- '最常用查询类型': 'Top Query Types',
- '最活跃设备': 'Top Devices',
- '最常用DNS服务器': 'Top DNS Servers',
- '唯一设备数': 'Unique Devices',
- '时间范围': 'Time Range',
- '毫秒': 'ms',
- '分钟': 'minutes',
- '刷新': 'Refresh',
- '未知设备': 'Unknown Device',
- '成功': 'Success',
- '域名未找到': 'Domain not found',
- '服务器错误': 'Server error',
- '格式错误': 'Format error',
- '拒绝': 'Refused',
- '其他': 'Other'
- },
- 'fr': {
- 'Bandix DNS 监控': 'Bandix Surveillance DNS',
- '正在加载数据...': 'Chargement des données...',
- '无法获取数据': 'Impossible de récupérer les données',
- 'DNS 监控': 'Surveillance DNS',
- 'DNS 查询记录': 'Enregistrements de Requêtes DNS',
- 'DNS 统计信息': 'Statistiques DNS',
- 'DNS监控未启用': 'Surveillance DNS désactivée',
- '请在设置中启用DNS监控功能': 'Veuillez activer la surveillance DNS dans les paramètres',
- '前往设置': 'Aller aux Paramètres',
- '无数据': 'Aucune Donnée',
- '时间': 'Heure',
- '域名': 'Domaine',
- '查询类型': 'Type de Requête',
- '类型': 'Type',
- '响应码': 'Code de Réponse',
- '响应时间': 'Temps de Réponse',
- '源IP': 'IP Source',
- '目标IP': 'IP de Destination',
- '设备': 'Appareil',
- '响应IP': 'IPs de Réponse',
- '响应结果': 'Résultat de Réponse',
- 'DNS服务器': 'Serveur DNS',
- '查询': 'Requête',
- '响应': 'Réponse',
- '过滤': 'Filtre',
- '域名过滤': 'Filtre de Domaine',
- '设备过滤': 'Filtre d\'Appareil',
- 'DNS服务器过滤': 'Filtre de Serveur DNS',
- '类型过滤': 'Filtre de Type',
- '全部': 'Tous',
- '仅查询': 'Requêtes Seulement',
- '仅响应': 'Réponses Seulement',
- '搜索': 'Rechercher',
- '搜索域名': 'Rechercher un Domaine',
- '搜索设备': 'Rechercher un Appareil',
- '搜索DNS服务器': 'Rechercher un Serveur DNS',
- '清除': 'Effacer',
- '上一页': 'Précédent',
- '下一页': 'Suivant',
- '第': 'Page',
- '页,共': 'sur',
- '共': 'Total',
- '条记录': 'enregistrements',
- '每页显示': 'Par Page',
- '条': '',
- '总查询数': 'Total des Requêtes',
- '总响应数': 'Total des Réponses',
- '有响应查询': 'Requêtes avec Réponse',
- '无响应查询': 'Requêtes sans Réponse',
- '平均响应时间': 'Temps de Réponse Moyen',
- '最快响应时间': 'Temps de Réponse Minimum',
- '最慢响应时间': 'Temps de Réponse Maximum',
- '响应时间': 'Temps de Réponse',
- '成功率': 'Taux de Réussite',
- '成功': 'Succès',
- '失败': 'Échec',
- '最常查询域名': 'Domaines les Plus Consultés',
- '最常用查询类型': 'Types de Requêtes les Plus Utilisés',
- '最活跃设备': 'Appareils les Plus Actifs',
- '最常用DNS服务器': 'Serveurs DNS les Plus Utilisés',
- '唯一设备数': 'Appareils Uniques',
- '时间范围': 'Plage de Temps',
- '毫秒': 'ms',
- '分钟': 'minutes',
- '刷新': 'Actualiser',
- '未知设备': 'Appareil Inconnu',
- '成功': 'Succès',
- '域名未找到': 'Domaine introuvable',
- '服务器错误': 'Erreur serveur',
- '格式错误': 'Erreur de format',
- '拒绝': 'Refusé',
- '其他': 'Autre'
- },
- 'ja': {
- 'Bandix DNS 监控': 'Bandix DNS監視',
- '正在加载数据...': 'データを読み込み中...',
- '无法获取数据': 'データを取得できません',
- 'DNS 监控': 'DNS監視',
- 'DNS 查询记录': 'DNSクエリ記録',
- 'DNS 统计信息': 'DNS統計情報',
- 'DNS监控未启用': 'DNS監視が無効です',
- '请在设置中启用DNS监控功能': '設定でDNS監視機能を有効にしてください',
- '前往设置': '設定へ',
- '无数据': 'データなし',
- '时间': '時刻',
- '域名': 'ドメイン',
- '查询类型': 'クエリタイプ',
- '类型': 'タイプ',
- '响应码': '応答コード',
- '响应时间': '応答時間',
- '源IP': '送信元IP',
- '目标IP': '宛先IP',
- '设备': 'デバイス',
- '响应IP': '応答IP',
- '响应结果': '応答結果',
- 'DNS服务器': 'DNSサーバー',
- '查询': 'クエリ',
- '响应': '応答',
- '过滤': 'フィルター',
- '域名过滤': 'ドメインフィルター',
- '设备过滤': 'デバイスフィルター',
- 'DNS服务器过滤': 'DNSサーバーフィルター',
- '类型过滤': 'タイプフィルター',
- '全部': 'すべて',
- '仅查询': 'クエリのみ',
- '仅响应': '応答のみ',
- '搜索': '検索',
- '搜索域名': 'ドメインを検索',
- '搜索设备': 'デバイスを検索',
- '搜索DNS服务器': 'DNSサーバーを検索',
- '清除': 'クリア',
- '上一页': '前へ',
- '下一页': '次へ',
- '第': 'ページ',
- '页,共': '/',
- '共': '合計',
- '条记录': '件の記録',
- '每页显示': 'ページあたり',
- '条': '',
- '总查询数': '総クエリ数',
- '总响应数': '総応答数',
- '有响应查询': '応答ありのクエリ',
- '无响应查询': '応答なしのクエリ',
- '平均响应时间': '平均応答時間',
- '最快响应时间': '最小応答時間',
- '最慢响应时间': '最大応答時間',
- '响应时间': '応答時間',
- '成功率': '成功率',
- '成功': '成功',
- '失败': '失敗',
- '最常查询域名': '最も頻繁にクエリされるドメイン',
- '最常用查询类型': '最も使用されるクエリタイプ',
- '最活跃设备': '最もアクティブなデバイス',
- '最常用DNS服务器': '最も使用されるDNSサーバー',
- '唯一设备数': 'ユニークデバイス数',
- '时间范围': '時間範囲',
- '毫秒': 'ミリ秒',
- '分钟': '分',
- '刷新': '更新',
- '未知设备': '不明なデバイス',
- '成功': '成功',
- '域名未找到': 'ドメインが見つかりません',
- '服务器错误': 'サーバーエラー',
- '格式错误': 'フォーマットエラー',
- '拒绝': '拒否',
- '其他': 'その他'
- },
- 'ru': {
- 'Bandix DNS 监控': 'Bandix Мониторинг DNS',
- '正在加载数据...': 'Загрузка данных...',
- '无法获取数据': 'Не удалось получить данные',
- 'DNS 监控': 'Мониторинг DNS',
- 'DNS 查询记录': 'Записи DNS-запросов',
- 'DNS 统计信息': 'Статистика DNS',
- 'DNS监控未启用': 'Мониторинг DNS отключен',
- '请在设置中启用DNS监控功能': 'Пожалуйста, включите мониторинг DNS в настройках',
- '前往设置': 'Перейти в Настройки',
- '无数据': 'Нет Данных',
- '时间': 'Время',
- '域名': 'Домен',
- '查询类型': 'Тип Запроса',
- '类型': 'Тип',
- '响应码': 'Код Ответа',
- '响应时间': 'Время Ответа',
- '源IP': 'Исходный IP',
- '目标IP': 'IP Назначения',
- '设备': 'Устройство',
- '响应IP': 'IP Ответов',
- '响应结果': 'Результат Ответа',
- 'DNS服务器': 'DNS Сервер',
- '查询': 'Запрос',
- '响应': 'Ответ',
- '过滤': 'Фильтр',
- '域名过滤': 'Фильтр Домена',
- '设备过滤': 'Фильтр Устройства',
- 'DNS服务器过滤': 'Фильтр DNS Сервера',
- '类型过滤': 'Фильтр Типа',
- '全部': 'Все',
- '仅查询': 'Только Запросы',
- '仅响应': 'Только Ответы',
- '搜索': 'Поиск',
- '搜索域名': 'Поиск Домена',
- '搜索设备': 'Поиск Устройства',
- '搜索DNS服务器': 'Поиск DNS Сервера',
- '清除': 'Очистить',
- '上一页': 'Предыдущая',
- '下一页': 'Следующая',
- '第': 'Страница',
- '页,共': 'из',
- '共': 'Всего',
- '条记录': 'записей',
- '每页显示': 'На Странице',
- '条': '',
- '总查询数': 'Всего Запросов',
- '总响应数': 'Всего Ответов',
- '有响应查询': 'Запросы с Ответом',
- '无响应查询': 'Запросы без Ответа',
- '平均响应时间': 'Среднее Время Ответа',
- '最快响应时间': 'Минимальное Время Ответа',
- '最慢响应时间': 'Максимальное Время Ответа',
- '响应时间': 'Время Ответа',
- '成功率': 'Процент Успеха',
- '成功': 'Успех',
- '失败': 'Неудача',
- '最常查询域名': 'Наиболее Запрашиваемые Домены',
- '最常用查询类型': 'Наиболее Используемые Типы Запросов',
- '最活跃设备': 'Наиболее Активные Устройства',
- '最常用DNS服务器': 'Наиболее Используемые DNS Серверы',
- '唯一设备数': 'Уникальных Устройств',
- '时间范围': 'Временной Диапазон',
- '毫秒': 'мс',
- '分钟': 'минут',
- '刷新': 'Обновить',
- '未知设备': 'Неизвестное Устройство',
- '成功': 'Успех',
- '域名未找到': 'Домен не найден',
- '服务器错误': 'Ошибка сервера',
- '格式错误': 'Ошибка формата',
- '拒绝': 'Отклонено',
- '其他': 'Другое'
- }
-};
-function getTranslation(key, language) {
- return translations[language]?.[key] || key;
-}
-
-function getSystemLanguage() {
- var luciLang = uci.get('luci', 'main', 'lang');
- if (luciLang && translations[luciLang]) {
- return luciLang;
- }
- var systemLang = document.documentElement.lang || 'en';
- if (translations[systemLang]) {
- return systemLang;
- }
- return 'en';
-}
-
-function isDarkMode() {
- var userTheme = uci.get('bandix', 'general', 'theme');
- if (userTheme) {
- if (userTheme === 'dark') {
- return true;
- } else if (userTheme === 'light') {
- return false;
- }
- }
-
- var mediaUrlBase = uci.get('luci', 'main', 'mediaurlbase');
- if (mediaUrlBase && mediaUrlBase.toLowerCase().includes('dark')) {
- return true;
- }
-
- if (mediaUrlBase && mediaUrlBase.toLowerCase().includes('argon')) {
- var argonMode = uci.get('argon', '@global[0]', 'mode');
- if (argonMode) {
- if (argonMode.toLowerCase() === 'dark') {
- return true;
- } else if (argonMode.toLowerCase() === 'light') {
- return false;
- }
- if (argonMode.toLowerCase() === 'normal' || argonMode.toLowerCase() === 'auto') {
- if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
- return true;
- }
- return false;
- }
- }
- }
-
- if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
- return true;
- }
-
- return false;
-}
+// 暗色模式检测已改为使用 CSS 媒体查询 @media (prefers-color-scheme: dark)
function formatTimestamp(timestamp) {
if (!timestamp) return '-';
@@ -522,17 +23,15 @@ function formatTimestamp(timestamp) {
}
function formatResponseCode(code) {
- var language = getSystemLanguage();
- if (code === 'Success') return getTranslation('成功', language);
- if (code === 'Domain not found') return getTranslation('域名未找到', language);
- if (code === 'Server error') return getTranslation('服务器错误', language);
- if (code === 'Format error') return getTranslation('格式错误', language);
- if (code === 'Refused') return getTranslation('拒绝', language);
- return code || getTranslation('其他', language);
+ if (code === 'Success' || code === 'NOERROR') return _('Success');
+ if (code === 'Domain not found' || code === 'NXDomain' || code === 'NXDOMAIN') return _('Domain not found');
+ if (code === 'Server error' || code === 'ServFail' || code === 'SERVFAIL') return _('Server error');
+ if (code === 'Format error' || code === 'FormErr' || code === 'FORMERR') return _('Format error');
+ if (code === 'Refused' || code === 'Refused' || code === 'REFUSED') return _('Refused');
+ return code || _('Other');
}
function formatDeviceName(device) {
- var language = getSystemLanguage();
var parts = [];
if (device && device.device_name && device.device_name !== '') {
parts.push(device.device_name);
@@ -552,7 +51,7 @@ function formatDeviceName(device) {
parts.push(ip);
}
if (parts.length === 0) {
- return getTranslation('未知设备', language);
+ return _('Unknown Device');
}
return parts.join(' / ');
}
@@ -611,53 +110,35 @@ return view.extend({
},
render: function (data) {
- var language = uci.get('bandix', 'general', 'language');
- if (!language || language === 'auto') {
- language = getSystemLanguage();
- }
- var darkMode = isDarkMode();
var dnsEnabled = uci.get('bandix', 'dns', 'enabled') === '1';
var style = E('style', {}, `
.bandix-dns-container {
- margin: 0;
- padding: 16px;
- background-color: ${darkMode ? '#1a1a1a' : '#f8fafc'};
- min-height: calc(100vh - 100px);
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
- color: ${darkMode ? '#e2e8f0' : '#1f2937'};
- border-radius: 8px;
}
.bandix-header {
display: flex;
align-items: center;
justify-content: space-between;
- margin-bottom: 20px;
}
.bandix-title {
font-size: 1.5rem;
font-weight: 600;
- color: ${darkMode ? '#f1f5f9' : '#1f2937'};
margin: 0;
}
.bandix-alert {
- background-color: ${darkMode ? '#2a2a2a' : '#eff6ff'};
- border-left: 3px solid ${darkMode ? '#3b82f6' : '#2563eb'};
border-radius: 4px;
padding: 10px 12px;
- margin-bottom: 16px;
display: flex;
align-items: center;
gap: 10px;
- color: ${darkMode ? '#d0d0d0' : '#1e293b'};
font-size: 0.875rem;
}
.bandix-alert-icon {
- color: ${darkMode ? '#60a5fa' : '#2563eb'};
font-size: 0.875rem;
font-weight: 700;
width: 18px;
@@ -665,45 +146,16 @@ return view.extend({
display: flex;
align-items: center;
justify-content: center;
- border: 2px solid currentColor;
border-radius: 50%;
flex-shrink: 0;
}
- .bandix-card {
- background-color: ${darkMode ? '#2a2a2a' : 'white'};
- border-radius: 8px;
- border: 1px solid ${darkMode ? '#444444' : '#e2e8f0'};
- box-shadow: 0 2px 8px rgba(0, 0, 0, ${darkMode ? '0.3' : '0.08'});
- margin-bottom: 24px;
- overflow: hidden;
- }
-
- .bandix-card-header {
- padding: 16px;
- border-bottom: 1px solid ${darkMode ? '#444444' : '#e2e8f0'};
- background-color: ${darkMode ? '#2a2a2a' : '#f8fafc'};
- }
-
- .bandix-card-title {
- font-size: 1.125rem;
- font-weight: 600;
- color: ${darkMode ? '#f1f5f9' : '#1f2937'};
- margin: 0;
- display: flex;
- align-items: center;
- gap: 8px;
- }
-
- .bandix-card-body {
- padding: 16px;
- }
.filter-section {
display: flex;
flex-wrap: wrap;
gap: 12px;
- margin-bottom: 16px;
+ padding: 16px;
align-items: center;
}
@@ -716,83 +168,32 @@ return view.extend({
.filter-label {
font-size: 0.875rem;
font-weight: 500;
- color: ${darkMode ? '#94a3b8' : '#64748b'};
white-space: nowrap;
}
.filter-input {
padding: 6px 12px;
- border: 1px solid ${darkMode ? '#444444' : '#cbd5e1'};
border-radius: 4px;
- background-color: ${darkMode ? '#1a1a1a' : 'white'};
- color: ${darkMode ? '#e2e8f0' : '#1f2937'};
font-size: 0.875rem;
min-width: 150px;
- }
-
- .filter-select {
- padding: 6px 12px;
- border: 1px solid ${darkMode ? '#444444' : '#cbd5e1'};
- border-radius: 4px;
- background-color: ${darkMode ? '#1a1a1a' : 'white'};
- color: ${darkMode ? '#e2e8f0' : '#1f2937'};
- font-size: 0.875rem;
- cursor: pointer;
- }
-
- .btn {
- display: inline-flex;
- align-items: center;
- gap: 6px;
- padding: 6px 12px;
- border-radius: 4px;
- font-size: 0.875rem;
- font-weight: 500;
- text-decoration: none;
- border: none;
- cursor: pointer;
- transition: all 0.15s ease;
- }
-
- .btn-primary {
- background-color: #3b82f6;
- color: white;
- }
-
- .btn-primary:hover {
- background-color: #2563eb;
- }
-
- .btn-secondary {
- background-color: ${darkMode ? '#3a3a3a' : '#e5e7eb'};
- color: ${darkMode ? '#d0d0d0' : '#374151'};
- }
-
- .btn-secondary:hover {
- background-color: ${darkMode ? '#4a4a4a' : '#d1d5db'};
+ opacity: 1;
}
.bandix-table {
width: 100%;
- border-collapse: collapse;
- background-color: transparent;
font-size: 0.875rem;
}
.bandix-table th {
- background-color: ${darkMode ? '#2a2a2a' : '#f8fafc'};
padding: 10px 12px;
text-align: left;
font-weight: 600;
- color: ${darkMode ? '#d0d0d0' : '#475569'};
- border-bottom: 2px solid ${darkMode ? '#444444' : '#e2e8f0'};
+ opacity: 1;
white-space: nowrap;
}
.bandix-table td {
padding: 10px 12px;
- border-bottom: 1px solid ${darkMode ? '#333333' : '#f1f5f9'};
- color: ${darkMode ? '#d0d0d0' : '#334155'};
word-break: break-word;
}
@@ -841,14 +242,14 @@ return view.extend({
display: flex;
align-items: center;
justify-content: space-between;
- margin-top: 16px;
+ padding: 16px;
flex-wrap: wrap;
gap: 12px;
}
.pagination-info {
- color: ${darkMode ? '#94a3b8' : '#64748b'};
font-size: 0.875rem;
+ opacity: 0.7;
}
.pagination-controls {
@@ -857,110 +258,31 @@ return view.extend({
gap: 8px;
}
- .stats-grid {
- display: grid;
- grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
- gap: 16px;
- margin-bottom: 24px;
- }
-
- .stats-card {
- background-color: ${darkMode ? '#2a2a2a' : 'white'};
- border-radius: 8px;
- padding: 16px;
- border: 1px solid ${darkMode ? '#444444' : '#e2e8f0'};
- box-shadow: 0 2px 8px rgba(0, 0, 0, ${darkMode ? '0.3' : '0.08'});
- }
-
- .stats-card-title {
- font-size: 0.75rem;
- font-weight: 600;
- color: ${darkMode ? '#94a3b8' : '#64748b'};
- margin: 0 0 8px 0;
- text-transform: uppercase;
- letter-spacing: 0.025em;
- }
-
- .stats-card-value {
- font-size: 1.5rem;
- font-weight: 700;
- color: ${darkMode ? '#f1f5f9' : '#1f2937'};
- margin: 0;
- }
-
- .stats-card-unit {
- font-size: 0.875rem;
- color: ${darkMode ? '#94a3b8' : '#64748b'};
- margin-left: 4px;
- }
-
- .stats-card-details {
- margin-top: 12px;
- display: flex;
- flex-direction: column;
- gap: 8px;
- }
-
- .stats-detail-row {
- display: flex;
- justify-content: space-between;
- align-items: center;
- font-size: 0.875rem;
- }
-
- .stats-detail-label {
- color: ${darkMode ? '#9ca3af' : '#6b7280'};
- font-weight: 500;
- }
-
- .stats-detail-value {
- font-weight: 600;
- color: ${darkMode ? '#e2e8f0' : '#374151'};
- }
-
- .top-list {
- list-style: none;
- padding: 0;
- margin: 0;
- }
-
- .top-list-item {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 8px 0;
- border-bottom: 1px solid ${darkMode ? '#333333' : '#f1f5f9'};
- }
-
- .top-list-item:last-child {
- border-bottom: none;
- }
-
- .top-list-name {
- flex: 1;
- color: ${darkMode ? '#e2e8f0' : '#334155'};
- font-size: 0.875rem;
- word-break: break-word;
- }
-
- .top-list-count {
- font-weight: 600;
- color: ${darkMode ? '#f1f5f9' : '#1f2937'};
- font-size: 0.875rem;
- margin-left: 12px;
- }
.loading-state {
text-align: center;
padding: 40px;
- color: ${darkMode ? '#94a3b8' : '#6b7280'};
+ opacity: 0.7;
font-style: italic;
}
.error-state {
text-align: center;
padding: 40px;
- color: ${darkMode ? '#f87171' : '#ef4444'};
+ }
+
+ .refresh-overlay {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ z-index: 10;
+ backdrop-filter: blur(2px);
+ -webkit-backdrop-filter: blur(2px);
}
.response-ips {
@@ -972,10 +294,113 @@ return view.extend({
.response-ip-badge {
display: inline-block;
padding: 2px 6px;
- background-color: ${darkMode ? '#3a3a3a' : '#e5e7eb'};
border-radius: 4px;
font-size: 0.75rem;
- color: ${darkMode ? '#d0d0d0' : '#374151'};
+ }
+
+ .stats-grid {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: 16px;
+ margin-bottom: 16px;
+ }
+
+ @media (max-width: 1200px) {
+ .stats-grid {
+ grid-template-columns: repeat(2, 1fr);
+ }
+ }
+
+ @media (max-width: 768px) {
+ .stats-grid {
+ grid-template-columns: 1fr;
+ }
+ }
+
+ .stats-card-title {
+ font-size: 0.875rem;
+ font-weight: 600;
+ opacity: 0.7;
+ margin: 0 0 12px 0;
+ text-transform: uppercase;
+ letter-spacing: 0.025em;
+ }
+
+ .stats-grid .cbi-section {
+ padding: 16px;
+ border: 1px solid rgba(0, 0, 0, 0.1);
+ border-radius: 8px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
+ transition: box-shadow 0.2s ease, transform 0.2s ease, border-color 0.2s ease;
+ }
+
+ .stats-grid .cbi-section:hover {
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
+ transform: translateY(-2px);
+ }
+
+ @media (prefers-color-scheme: dark) {
+ .stats-grid .cbi-section {
+ border-color: rgba(255, 255, 255, 0.15);
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
+ }
+
+ .stats-grid .cbi-section:hover {
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
+ }
+ }
+
+ .stats-card-main-value {
+ font-size: 2.25rem;
+ font-weight: 700;
+ margin: 0 0 8px 0;
+ line-height: 1;
+ }
+
+ .stats-card-sub-value {
+ font-size: 0.875rem;
+ opacity: 0.7;
+ margin: 0;
+ }
+
+ .stats-card-details {
+ margin-top: 16px;
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ }
+
+ .stats-detail-row {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ font-size: 0.875rem;
+ }
+
+ .stats-detail-label {
+ opacity: 0.7;
+ font-weight: 500;
+ }
+
+ .stats-detail-value {
+ font-weight: 600;
+ }
+
+ .stats-list-item {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 4px 0;
+ font-size: 0.875rem;
+ }
+
+ .stats-list-name {
+ opacity: 0.8;
+ }
+
+ .stats-list-count {
+ font-weight: 600;
+ opacity: 0.9;
}
`);
document.head.appendChild(style);
@@ -983,98 +408,95 @@ return view.extend({
var container = E('div', { 'class': 'bandix-dns-container' });
var header = E('div', { 'class': 'bandix-header' }, [
- E('h1', { 'class': 'bandix-title' }, getTranslation('Bandix DNS 监控', language))
+ E('h1', { 'class': 'bandix-title' }, _('Bandix DNS Monitor'))
]);
container.appendChild(header);
if (!dnsEnabled) {
var alertDiv = E('div', { 'class': 'bandix-alert' }, [
- E('span', { 'class': 'bandix-alert-icon' }, '!'),
E('div', {}, [
- E('strong', {}, getTranslation('DNS监控未启用', language)),
+ E('strong', {}, _('DNS Monitoring Disabled')),
E('p', { 'style': 'margin: 4px 0 0 0;' },
- getTranslation('请在设置中启用DNS监控功能', language))
+ _('Please enable DNS monitoring in settings'))
])
]);
container.appendChild(alertDiv);
- var settingsCard = E('div', { 'class': 'bandix-card' }, [
- E('div', { 'class': 'bandix-card-body', 'style': 'text-align: center;' }, [
+ var settingsCard = E('div', { 'class': 'cbi-section' }, [
+ E('div', { 'style': 'text-align: center; padding: 16px;' }, [
E('a', {
'href': '/cgi-bin/luci/admin/network/bandix/settings',
'class': 'btn btn-primary'
- }, getTranslation('前往设置', language))
+ }, _('Go to Settings'))
])
]);
container.appendChild(settingsCard);
return container;
}
- // DNS 统计信息卡片
- var statsCard = E('div', { 'class': 'bandix-card' }, [
- E('div', { 'class': 'bandix-card-body' }, [
- E('div', { 'id': 'dns-stats-container' }, [
- E('div', { 'class': 'loading-state' }, getTranslation('正在加载数据...', language))
- ])
- ])
+ // 添加提示信息
+ var infoAlert = E('div', { 'class': 'bandix-alert' }, [
+ E('span', {}, _('Does not include DoH and DoT'))
]);
- container.appendChild(statsCard);
+ container.appendChild(infoAlert);
- // DNS 查询记录卡片
- var queriesCard = E('div', { 'class': 'bandix-card' }, [
- E('div', { 'class': 'bandix-card-header' }, [
- E('h2', { 'class': 'bandix-card-title' }, getTranslation('DNS 查询记录', language))
- ]),
- E('div', { 'class': 'bandix-card-body' }, [
- E('div', { 'class': 'filter-section' }, [
- E('div', { 'class': 'filter-group' }, [
- E('label', { 'class': 'filter-label' }, getTranslation('类型过滤', language) + ':'),
- E('select', { 'class': 'filter-select', 'id': 'type-filter' }, [
- E('option', { 'value': '' }, getTranslation('全部', language)),
- E('option', { 'value': 'true' }, getTranslation('仅查询', language)),
- E('option', { 'value': 'false' }, getTranslation('仅响应', language))
- ])
- ]),
- E('div', { 'class': 'filter-group' }, [
- E('label', { 'class': 'filter-label' }, getTranslation('域名过滤', language) + ':'),
- E('input', {
- 'type': 'text',
- 'class': 'filter-input',
- 'id': 'domain-filter',
- 'placeholder': getTranslation('搜索域名', language)
- })
- ]),
- E('div', { 'class': 'filter-group' }, [
- E('label', { 'class': 'filter-label' }, getTranslation('设备过滤', language) + ':'),
- E('input', {
- 'type': 'text',
- 'class': 'filter-input',
- 'id': 'device-filter',
- 'placeholder': getTranslation('搜索设备', language)
- })
- ]),
- E('div', { 'class': 'filter-group' }, [
- E('label', { 'class': 'filter-label' }, getTranslation('DNS服务器过滤', language) + ':'),
- E('input', {
- 'type': 'text',
- 'class': 'filter-input',
- 'id': 'dns-server-filter',
- 'placeholder': getTranslation('搜索DNS服务器', language)
- })
- ]),
- E('div', { 'class': 'filter-group', 'style': 'margin-left: auto;' }, [
- E('button', {
- 'class': 'btn btn-primary',
- 'id': 'refresh-queries-btn'
- }, getTranslation('刷新', language))
+ // DNS 统计信息卡片
+ var statsGrid = E('div', { 'class': 'stats-grid', 'id': 'dns-stats-grid' });
+ container.appendChild(statsGrid);
+
+ // DNS 查询记录
+ var queriesSection = E('div', { 'class': 'cbi-section' }, [
+ E('h3', {}, _('DNS Query Records')),
+ E('div', {}, [
+ E('div', { 'class': 'filter-section' }, [
+ E('div', { 'class': 'filter-group' }, [
+ E('label', { 'class': 'filter-label' }, _('Type Filter') + ':'),
+ E('select', { 'class': 'cbi-select', 'id': 'type-filter' }, [
+ E('option', { 'value': '' }, _('All')),
+ E('option', { 'value': 'true' }, _('Queries Only')),
+ E('option', { 'value': 'false' }, _('Responses Only'))
])
]),
- E('div', { 'id': 'dns-queries-container' }, [
- E('div', { 'class': 'loading-state' }, getTranslation('正在加载数据...', language))
+ E('div', { 'class': 'filter-group' }, [
+ E('label', { 'class': 'filter-label' }, _('Domain Filter') + ':'),
+ E('input', {
+ 'type': 'text',
+ 'class': 'filter-input',
+ 'id': 'domain-filter',
+ 'placeholder': _('Search Domain')
+ })
+ ]),
+ E('div', { 'class': 'filter-group' }, [
+ E('label', { 'class': 'filter-label' }, _('Device Filter') + ':'),
+ E('input', {
+ 'type': 'text',
+ 'class': 'filter-input',
+ 'id': 'device-filter',
+ 'placeholder': _('Search Device')
+ })
+ ]),
+ E('div', { 'class': 'filter-group' }, [
+ E('label', { 'class': 'filter-label' }, _('DNS Server Filter') + ':'),
+ E('input', {
+ 'type': 'text',
+ 'class': 'filter-input',
+ 'id': 'dns-server-filter',
+ 'placeholder': _('Search DNS Server')
+ })
+ ]),
+ E('div', { 'class': 'filter-group', 'style': 'margin-left: auto;' }, [
+ E('button', {
+ 'class': 'cbi-button cbi-button-action',
+ 'id': 'refresh-queries-btn'
+ }, _('Refresh'))
])
+ ]),
+ E('div', { 'id': 'dns-queries-container' }, [
+ E('div', { 'class': 'loading-state' }, _('Loading data...'))
+ ])
])
]);
- container.appendChild(queriesCard);
+ container.appendChild(queriesSection);
// 状态变量
var currentPage = 1;
@@ -1086,174 +508,11 @@ return view.extend({
dns_server: ''
};
- // 更新统计信息
- var statsInitialized = false;
- function updateStats() {
- callGetDnsStats().then(function (result) {
- var container = document.getElementById('dns-stats-container');
- if (!container) return;
- if (!result || result.status !== 'success' || !result.data || !result.data.stats) {
- if (!statsInitialized) {
- container.innerHTML = '';
- container.appendChild(E('div', { 'class': 'error-state' },
- getTranslation('无法获取数据', language)));
- }
- return;
- }
-
- var stats = result.data.stats;
-
- // 如果还没有初始化,创建完整的 UI 结构
- if (!statsInitialized) {
- var statsHtml = E('div', {}, [
- E('div', { 'class': 'stats-grid' }, [
- E('div', { 'class': 'stats-card' }, [
- E('div', { 'class': 'stats-card-title' }, getTranslation('总查询数', language)),
- E('div', { 'class': 'stats-card-value', 'id': 'stat-total-queries' }, stats.total_queries || 0)
- ]),
- E('div', { 'class': 'stats-card' }, [
- E('div', { 'class': 'stats-card-title' }, getTranslation('响应时间', language)),
- E('div', { 'class': 'stats-card-value', 'id': 'stat-latest-response-time', 'style': 'margin-bottom: 12px;' }, [
- E('span', {}, (stats.latest_response_time_ms || 0).toFixed(1)),
- E('span', { 'class': 'stats-card-unit' }, ' ' + getTranslation('毫秒', language))
- ]),
- E('div', { 'class': 'stats-card-details', 'id': 'stat-response-time-details' }, [
- E('div', { 'class': 'stats-detail-row' }, [
- E('span', { 'class': 'stats-detail-label' }, getTranslation('平均响应时间', language) + ':'),
- E('span', { 'class': 'stats-detail-value', 'id': 'stat-avg-response-time' },
- (stats.avg_response_time_ms || 0).toFixed(1) + ' ' + getTranslation('毫秒', language))
- ]),
- E('div', { 'class': 'stats-detail-row' }, [
- E('span', { 'class': 'stats-detail-label' }, getTranslation('最快响应时间', language) + ':'),
- E('span', { 'class': 'stats-detail-value', 'id': 'stat-min-response-time' },
- (stats.min_response_time_ms || 0).toFixed(1) + ' ' + getTranslation('毫秒', language))
- ]),
- E('div', { 'class': 'stats-detail-row' }, [
- E('span', { 'class': 'stats-detail-label' }, getTranslation('最慢响应时间', language) + ':'),
- E('span', { 'class': 'stats-detail-value', 'id': 'stat-max-response-time' },
- (stats.max_response_time_ms || 0).toFixed(1) + ' ' + getTranslation('毫秒', language))
- ])
- ])
- ]),
- E('div', { 'class': 'stats-card' }, [
- E('div', { 'class': 'stats-card-title' }, getTranslation('成功率', language)),
- E('div', { 'class': 'stats-card-value', 'id': 'stat-success-rate' }, [
- E('span', {}, ((stats.success_rate || 0) * 100).toFixed(1)),
- E('span', { 'class': 'stats-card-unit' }, '%')
- ])
- ])
- ]),
- E('div', { 'style': 'display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 16px; margin-top: 24px;' }, [
- E('div', { 'class': 'stats-card', 'id': 'top-domains-card' }),
- E('div', { 'class': 'stats-card', 'id': 'top-query-types-card' }),
- E('div', { 'class': 'stats-card', 'id': 'top-devices-card' }),
- E('div', { 'class': 'stats-card', 'id': 'top-dns-servers-card' })
- ])
- ]);
-
- container.innerHTML = '';
- container.appendChild(statsHtml);
- statsInitialized = true;
- }
-
- // 只更新数字内容
- var totalQueriesEl = document.getElementById('stat-total-queries');
- if (totalQueriesEl) {
- totalQueriesEl.textContent = stats.total_queries || 0;
- }
-
- var latestResponseTimeEl = document.getElementById('stat-latest-response-time');
- if (latestResponseTimeEl && latestResponseTimeEl.firstChild) {
- latestResponseTimeEl.firstChild.textContent = (stats.latest_response_time_ms || 0).toFixed(1);
- }
-
- var avgResponseTimeEl = document.getElementById('stat-avg-response-time');
- if (avgResponseTimeEl) {
- avgResponseTimeEl.textContent = (stats.avg_response_time_ms || 0).toFixed(1) + ' ' + getTranslation('毫秒', language);
- }
-
- var minResponseTimeEl = document.getElementById('stat-min-response-time');
- if (minResponseTimeEl) {
- minResponseTimeEl.textContent = (stats.min_response_time_ms || 0).toFixed(1) + ' ' + getTranslation('毫秒', language);
- }
-
- var maxResponseTimeEl = document.getElementById('stat-max-response-time');
- if (maxResponseTimeEl) {
- maxResponseTimeEl.textContent = (stats.max_response_time_ms || 0).toFixed(1) + ' ' + getTranslation('毫秒', language);
- }
-
- var successRateEl = document.getElementById('stat-success-rate');
- if (successRateEl && successRateEl.firstChild) {
- successRateEl.firstChild.textContent = ((stats.success_rate || 0) * 100).toFixed(1);
- }
-
- // 更新 Top 列表
- function updateTopList(cardId, titleKey, items, maxItems) {
- var card = document.getElementById(cardId);
- if (!card) return;
-
- if (!items || items.length === 0) {
- card.style.display = 'none';
- return;
- }
-
- card.style.display = '';
- var list = card.querySelector('.top-list');
-
- if (!list) {
- // 创建列表
- card.innerHTML = '';
- card.appendChild(E('div', { 'class': 'stats-card-title' }, getTranslation(titleKey, language)));
- list = E('ul', { 'class': 'top-list' });
- card.appendChild(list);
- }
-
- var itemsToShow = items.slice(0, maxItems || 10);
- var listItems = list.querySelectorAll('.top-list-item');
-
- // 如果列表项数量不匹配,重新创建列表
- if (listItems.length !== itemsToShow.length) {
- list.innerHTML = '';
- itemsToShow.forEach(function (item) {
- list.appendChild(E('li', { 'class': 'top-list-item' }, [
- E('span', { 'class': 'top-list-name' }, item.name || '-'),
- E('span', { 'class': 'top-list-count' }, item.count || 0)
- ]));
- });
- } else {
- // 只更新文本内容
- itemsToShow.forEach(function (item, index) {
- var listItem = listItems[index];
- if (listItem) {
- var nameEl = listItem.querySelector('.top-list-name');
- var countEl = listItem.querySelector('.top-list-count');
- if (nameEl) nameEl.textContent = item.name || '-';
- if (countEl) countEl.textContent = item.count || 0;
- }
- });
- }
- }
-
- updateTopList('top-domains-card', '最常查询域名', stats.top_domains, 10);
- updateTopList('top-query-types-card', '最常用查询类型', stats.top_query_types);
- updateTopList('top-devices-card', '最活跃设备', stats.top_devices, 10);
- updateTopList('top-dns-servers-card', '最常用DNS服务器', stats.top_dns_servers, 5);
- }).catch(function (error) {
- console.error('Failed to load DNS stats:', error);
- var container = document.getElementById('dns-stats-container');
- if (!container) return;
- if (!statsInitialized) {
- container.innerHTML = '';
- container.appendChild(E('div', { 'class': 'error-state' },
- getTranslation('无法获取数据', language)));
- }
- });
- }
// 更新查询记录
function updateQueries() {
var container = document.getElementById('dns-queries-container');
- if (!container) return;
+ if (!container) return Promise.resolve();
// 检查是否有现有内容
var hasContent = container.querySelector('.bandix-table') || container.querySelector('.loading-state') || container.querySelector('.error-state');
@@ -1268,13 +527,16 @@ return view.extend({
var loadingDiv = container.querySelector('.loading-overlay');
if (hasContent) {
if (!loadingDiv) {
- var overlayBg = darkMode ? 'rgba(42, 42, 42, 0.9)' : 'rgba(255, 255, 255, 0.9)';
loadingDiv = E('div', {
'class': 'loading-overlay',
- 'style': 'position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: ' + overlayBg + '; display: flex; align-items: center; justify-content: center; z-index: 10; color: ' + (darkMode ? '#e2e8f0' : '#1f2937') + ';'
- }, getTranslation('正在加载数据...', language));
+ 'style': 'position: absolute; top: 0; left: 0; right: 0; bottom: 0; display: flex; align-items: center; justify-content: center; z-index: 10;'
+ }, _('Loading data...'));
container.style.position = 'relative';
container.appendChild(loadingDiv);
+ // 应用主题背景色
+ setTimeout(function() {
+ applyThemeColors();
+ }, 50);
} else {
loadingDiv.style.display = 'flex';
}
@@ -1282,10 +544,14 @@ return view.extend({
// 如果没有内容,使用简单的加载状态
container.innerHTML = '';
container.appendChild(E('div', { 'class': 'loading-state' },
- getTranslation('正在加载数据...', language)));
+ _('Loading data...')));
+ // 应用主题背景色
+ setTimeout(function() {
+ applyThemeColors();
+ }, 50);
}
- callGetDnsQueries(
+ return callGetDnsQueries(
currentFilters.domain,
currentFilters.device,
currentFilters.is_query,
@@ -1298,6 +564,12 @@ return view.extend({
loadingDiv.remove();
}
+ // 移除刷新蒙版
+ var refreshOverlay = container.querySelector('.refresh-overlay');
+ if (refreshOverlay) {
+ refreshOverlay.remove();
+ }
+
// 恢复最小高度和定位
container.style.minHeight = '';
if (!hasContent) {
@@ -1307,7 +579,11 @@ return view.extend({
if (!result || result.status !== 'success' || !result.data) {
container.innerHTML = '';
container.appendChild(E('div', { 'class': 'error-state' },
- getTranslation('无法获取数据', language)));
+ _('Unable to fetch data')));
+ // 应用主题背景色
+ setTimeout(function() {
+ applyThemeColors();
+ }, 50);
return;
}
@@ -1318,7 +594,11 @@ return view.extend({
if (queries.length === 0) {
container.innerHTML = '';
container.appendChild(E('div', { 'class': 'loading-state' },
- getTranslation('无数据', language)));
+ _('No Data')));
+ // 应用主题背景色
+ setTimeout(function() {
+ applyThemeColors();
+ }, 50);
return;
}
@@ -1336,14 +616,14 @@ return view.extend({
var table = E('table', { 'class': 'bandix-table' }, [
E('thead', {}, [
E('tr', {}, [
- E('th', { 'style': 'width: 180px;' }, getTranslation('时间', language)),
- E('th', { 'style': 'width: 200px;' }, getTranslation('域名', language)),
- E('th', { 'style': 'width: 100px;' }, getTranslation('查询类型', language)),
- E('th', { 'style': 'width: 100px;' }, getTranslation('类型', language)),
- E('th', { 'style': 'width: 100px;' }, getTranslation('响应时间', language)),
- E('th', { 'style': 'width: 200px;' }, getTranslation('设备', language)),
- E('th', { 'style': 'width: 140px;' }, getTranslation('DNS服务器', language)),
- E('th', { 'style': 'width: 200px;' }, getTranslation('响应结果', language))
+ E('th', { 'style': 'width: 180px;' }, _('Time')),
+ E('th', { 'style': 'width: 200px;' }, _('Domain')),
+ E('th', { 'style': 'width: 100px;' }, _('Query Type')),
+ E('th', { 'style': 'width: 100px;' }, _('Type')),
+ E('th', { 'style': 'width: 100px;' }, _('Response Time')),
+ E('th', { 'style': 'width: 200px;' }, _('Device')),
+ E('th', { 'style': 'width: 140px;' }, _('DNS Server')),
+ E('th', { 'style': 'width: 200px;' }, _('Response Result'))
])
]),
E('tbody', {}, queries.map(function (query) {
@@ -1354,9 +634,9 @@ return view.extend({
E('td', {}, [
E('span', {
'class': 'query-badge ' + (query.is_query ? 'query' : 'response')
- }, query.is_query ? getTranslation('查询', language) : getTranslation('响应', language))
+ }, query.is_query ? _('Query') : _('Response'))
]),
- E('td', {}, query.response_time_ms ? query.response_time_ms + ' ' + getTranslation('毫秒', language) : '-'),
+ E('td', {}, query.response_time_ms ? query.response_time_ms + ' ' + _('ms') : '-'),
E('td', {}, formatDeviceName(query)),
E('td', {}, formatDnsServer(query)),
E('td', {}, [
@@ -1389,11 +669,11 @@ return view.extend({
var pagination = E('div', { 'class': 'pagination' }, [
E('div', { 'class': 'pagination-info' },
- getTranslation('第', language) + ' ' + currentPage + ' ' + getTranslation('页,共', language) + ' ' + totalPages + ',' + getTranslation('共', language) + ' ' + total + ' ' + getTranslation('条记录', language)
+ _('Page') + ' ' + currentPage + ' ' + _('of') + ' ' + totalPages + ',' + _('Total') + ' ' + total + ' ' + _('records')
),
E('div', { 'class': 'pagination-controls' }, [
E('select', {
- 'class': 'filter-select',
+ 'class': 'cbi-select',
'id': 'page-size-select',
'style': 'margin-right: 8px;'
}, [
@@ -1403,15 +683,15 @@ return view.extend({
E('option', { 'value': '100', 'selected': pageSize === 100 }, '100')
]),
E('button', {
- 'class': 'btn btn-secondary',
+ 'class': 'cbi-button cbi-button-action',
'id': 'prev-page-btn',
'disabled': currentPage <= 1 ? 'disabled' : null
- }, getTranslation('上一页', language)),
+ }, _('Previous')),
E('button', {
- 'class': 'btn btn-secondary',
+ 'class': 'cbi-button cbi-button-action',
'id': 'next-page-btn',
'disabled': currentPage >= totalPages ? 'disabled' : null
- }, getTranslation('下一页', language))
+ }, _('Next'))
])
]);
@@ -1470,16 +750,193 @@ return view.extend({
console.error('Failed to load DNS queries:', error);
var container = document.getElementById('dns-queries-container');
if (!container) return;
+
+ // 移除刷新蒙版
+ var refreshOverlay = container.querySelector('.refresh-overlay');
+ if (refreshOverlay) {
+ refreshOverlay.remove();
+ }
+
container.innerHTML = '';
container.appendChild(E('div', { 'class': 'error-state' },
- getTranslation('无法获取数据', language)));
+ _('Unable to fetch data')));
+ // 应用主题背景色
+ setTimeout(function() {
+ applyThemeColors();
+ }, 50);
});
}
+ // 更新统计信息卡片
+ function updateStats() {
+ var statsGrid = document.getElementById('dns-stats-grid');
+ if (!statsGrid) return Promise.resolve();
+
+ return callGetDnsStats().then(function (result) {
+ if (!result || result.status !== 'success' || !result.data || !result.data.stats) {
+ statsGrid.innerHTML = '';
+ return;
+ }
+
+ var stats = result.data.stats;
+ statsGrid.innerHTML = '';
+
+ // 格式化时间范围
+ function formatTimeRange(start, end, durationMinutes) {
+ if (!start || !end) return '-';
+ var startDate = new Date(start);
+ var endDate = new Date(end);
+ var startStr = startDate.toLocaleString('zh-CN', {
+ month: '2-digit',
+ day: '2-digit',
+ hour: '2-digit',
+ minute: '2-digit'
+ });
+ var endStr = endDate.toLocaleString('zh-CN', {
+ month: '2-digit',
+ day: '2-digit',
+ hour: '2-digit',
+ minute: '2-digit'
+ });
+ return startStr + ' - ' + endStr + ' (' + durationMinutes + ' ' + _('minutes') + ')';
+ }
+
+ // 格式化百分比
+ function formatPercent(value) {
+ if (typeof value !== 'number') return '-';
+ return (value * 100).toFixed(2) + '%';
+ }
+
+ // 总查询数卡片(合并成功率)
+ var totalQueriesCard = E('div', { 'class': 'cbi-section' }, [
+ E('div', { 'class': 'stats-card-title' }, _('Total Queries')),
+ E('div', { 'class': 'stats-card-main-value' }, (stats.total_queries || 0).toLocaleString()),
+ E('div', { 'class': 'stats-card-details' }, [
+ E('div', { 'class': 'stats-detail-row' }, [
+ E('span', { 'class': 'stats-detail-label' }, _('Success Rate') + ':'),
+ E('span', { 'class': 'stats-detail-value' }, formatPercent(stats.success_rate || 0))
+ ]),
+ E('div', { 'class': 'stats-detail-row' }, [
+ E('span', { 'class': 'stats-detail-label' }, _('Success') + ':'),
+ E('span', { 'class': 'stats-detail-value' }, (stats.success_count || 0).toLocaleString())
+ ]),
+ E('div', { 'class': 'stats-detail-row' }, [
+ E('span', { 'class': 'stats-detail-label' }, _('Failure') + ':'),
+ E('span', { 'class': 'stats-detail-value' }, (stats.failure_count || 0).toLocaleString())
+ ])
+ ])
+ ]);
+
+ statsGrid.appendChild(totalQueriesCard);
+
+ // 响应时间卡片
+ statsGrid.appendChild(E('div', { 'class': 'cbi-section' }, [
+ E('div', { 'class': 'stats-card-title' }, _('Response Time')),
+ E('div', { 'class': 'stats-card-main-value' }, (stats.avg_response_time_ms || 0).toFixed(2) + ' ' + _('ms')),
+ E('div', { 'class': 'stats-card-details' }, [
+ E('div', { 'class': 'stats-detail-row' }, [
+ E('span', { 'class': 'stats-detail-label' }, _('Min Response Time') + ':'),
+ E('span', { 'class': 'stats-detail-value' }, (stats.min_response_time_ms || 0) + ' ' + _('ms'))
+ ]),
+ E('div', { 'class': 'stats-detail-row' }, [
+ E('span', { 'class': 'stats-detail-label' }, _('Max Response Time') + ':'),
+ E('span', { 'class': 'stats-detail-value' }, (stats.max_response_time_ms || 0) + ' ' + _('ms'))
+ ]),
+ E('div', { 'class': 'stats-detail-row' }, [
+ E('span', { 'class': 'stats-detail-label' }, _('Latest Response Time') + ':'),
+ E('span', { 'class': 'stats-detail-value' }, (stats.latest_response_time_ms || 0) + ' ' + _('ms'))
+ ])
+ ])
+ ]));
+
+ // 最常用查询类型卡片
+ if (stats.top_query_types && stats.top_query_types.length > 0) {
+ var queryTypesList = stats.top_query_types.map(function(item) {
+ return E('div', { 'class': 'stats-list-item' }, [
+ E('span', { 'class': 'stats-list-name' }, item.name || '-'),
+ E('span', { 'class': 'stats-list-count' }, (item.count || 0).toLocaleString())
+ ]);
+ });
+
+ var queryTypesCard = E('div', { 'class': 'cbi-section' }, [
+ E('div', { 'class': 'stats-card-title' }, _('Top Query Types')),
+ E('div', { 'class': 'stats-card-details' })
+ ]);
+
+ queryTypesList.forEach(function(item) {
+ queryTypesCard.querySelector('.stats-card-details').appendChild(item);
+ });
+
+ statsGrid.appendChild(queryTypesCard);
+ }
+
+ // 最常查询域名卡片
+ if (stats.top_domains && stats.top_domains.length > 0) {
+ var domainsList = stats.top_domains.map(function(item) {
+ return E('div', { 'class': 'stats-list-item' }, [
+ E('span', { 'class': 'stats-list-name' }, item.name || '-'),
+ E('span', { 'class': 'stats-list-count' }, (item.count || 0).toLocaleString())
+ ]);
+ });
+
+ statsGrid.appendChild(E('div', { 'class': 'cbi-section' }, [
+ E('div', { 'class': 'stats-card-title' }, _('Top Domains')),
+ E('div', { 'class': 'stats-card-details' }, domainsList)
+ ]));
+ }
+
+ // 最活跃设备卡片
+ if (stats.top_devices && stats.top_devices.length > 0) {
+ var devicesList = stats.top_devices.map(function(item) {
+ return E('div', { 'class': 'stats-list-item' }, [
+ E('span', { 'class': 'stats-list-name' }, item.name || '-'),
+ E('span', { 'class': 'stats-list-count' }, (item.count || 0).toLocaleString())
+ ]);
+ });
+
+ statsGrid.appendChild(E('div', { 'class': 'cbi-section' }, [
+ E('div', { 'class': 'stats-card-title' }, _('Top Devices')),
+ E('div', { 'class': 'stats-card-details' }, devicesList)
+ ]));
+ }
+
+ // 最常用DNS服务器卡片
+ if (stats.top_dns_servers && stats.top_dns_servers.length > 0) {
+ var serversList = stats.top_dns_servers.map(function(item) {
+ return E('div', { 'class': 'stats-list-item' }, [
+ E('span', { 'class': 'stats-list-name' }, item.name || '-'),
+ E('span', { 'class': 'stats-list-count' }, (item.count || 0).toLocaleString())
+ ]);
+ });
+
+ statsGrid.appendChild(E('div', { 'class': 'cbi-section' }, [
+ E('div', { 'class': 'stats-card-title' }, _('Top DNS Servers')),
+ E('div', { 'class': 'stats-card-details' }, serversList)
+ ]));
+ }
+
+ // 应用主题颜色
+ setTimeout(function() {
+ applyThemeColors();
+ }, 50);
+ }).catch(function (error) {
+ console.error('Failed to load DNS stats:', error);
+ var statsGrid = document.getElementById('dns-stats-grid');
+ if (statsGrid) {
+ statsGrid.innerHTML = '';
+ }
+ });
+ }
+
// 初始化数据加载 - 延迟执行确保 DOM 元素已添加
setTimeout(function () {
updateStats();
updateQueries();
+
+ // 轮询更新统计数据(每5秒)
+ poll.add(function() {
+ return updateStats();
+ }, 1);
// 实时搜索功能(带防抖)
var domainFilter = document.getElementById('domain-filter');
@@ -1519,14 +976,152 @@ return view.extend({
// 刷新按钮
if (refreshBtn) {
refreshBtn.addEventListener('click', function () {
+ // 同时刷新统计数据和查询记录
+ updateStats();
+
+ var container = document.getElementById('dns-queries-container');
+ if (!container) {
+ updateQueries();
+ return;
+ }
+
+ // 确保容器是相对定位
+ container.style.position = 'relative';
+
+ // 移除旧的蒙版
+ var oldOverlay = container.querySelector('.refresh-overlay');
+ if (oldOverlay) {
+ oldOverlay.remove();
+ }
+
+ // 创建新的刷新蒙版
+ var overlay = E('div', {
+ 'class': 'refresh-overlay'
+ });
+ container.appendChild(overlay);
+
+ // 应用主题背景色到蒙版
+ setTimeout(function() {
+ applyThemeColors();
+ }, 50);
+
+ // 刷新数据(蒙版会在 updateQueries 中自动移除)
updateQueries();
});
}
}
- }, 100);
+ }, 10);
- // 轮询更新统计信息(每1秒),查询记录不自动刷新
- poll.add(updateStats, 1);
+ // 自动适应主题背景色和文字颜色的函数
+ function applyThemeColors() {
+ try {
+ var mainElement = document.querySelector('.main') || document.body;
+ var computedStyle = window.getComputedStyle(mainElement);
+ var bgColor = computedStyle.backgroundColor;
+
+ // 如果父元素有背景色,应用到容器和卡片
+ if (bgColor && bgColor !== 'rgba(0, 0, 0, 0)' && bgColor !== 'transparent') {
+ var containerEl = document.querySelector('.bandix-dns-container');
+ if (containerEl) {
+ containerEl.style.backgroundColor = bgColor;
+ }
+
+
+ // 应用到表格表头
+ var tableHeaders = document.querySelectorAll('.bandix-table th');
+ tableHeaders.forEach(function(th) {
+ th.style.backgroundColor = bgColor;
+ });
+
+ // 应用到 Response Result 字段的 badge
+ var badges = document.querySelectorAll('.response-ip-badge');
+ badges.forEach(function(badge) {
+ badge.style.backgroundColor = bgColor;
+ });
+
+ // 应用到搜索框(不包括 cbi-select,因为它使用官方样式)
+ var inputs = document.querySelectorAll('.filter-input');
+ inputs.forEach(function(input) {
+ input.style.backgroundColor = bgColor;
+ });
+
+ // 应用到统计卡片
+ var statsCards = document.querySelectorAll('.stats-grid .cbi-section');
+ statsCards.forEach(function(card) {
+ card.style.backgroundColor = bgColor;
+ });
+
+ // 应用到加载状态和错误状态
+ var loadingStates = document.querySelectorAll('.loading-state');
+ loadingStates.forEach(function(el) {
+ el.style.backgroundColor = bgColor;
+ });
+
+ var errorStates = document.querySelectorAll('.error-state');
+ errorStates.forEach(function(el) {
+ el.style.backgroundColor = bgColor;
+ });
+
+ // 应用到加载遮罩层(使用半透明背景)
+ var loadingOverlays = document.querySelectorAll('.loading-overlay');
+ loadingOverlays.forEach(function(el) {
+ // 将背景色转换为 rgba,并添加透明度
+ var rgb = bgColor.match(/\d+/g);
+ if (rgb && rgb.length >= 3) {
+ el.style.backgroundColor = 'rgba(' + rgb[0] + ', ' + rgb[1] + ', ' + rgb[2] + ', 0.8)';
+ }
+ });
+
+ // 应用到刷新蒙版(使用半透明背景和模糊效果)
+ var refreshOverlays = document.querySelectorAll('.refresh-overlay');
+ refreshOverlays.forEach(function(el) {
+ // 将背景色转换为 rgba,并添加透明度
+ var rgb = bgColor.match(/\d+/g);
+ if (rgb && rgb.length >= 3) {
+ el.style.backgroundColor = 'rgba(' + rgb[0] + ', ' + rgb[1] + ', ' + rgb[2] + ', 0.6)';
+ }
+ });
+ }
+
+ // 检测文字颜色并应用
+ var textColor = computedStyle.color;
+ if (textColor && textColor !== 'rgba(0, 0, 0, 0)') {
+ var containerEl = document.querySelector('.bandix-dns-container');
+ if (containerEl) {
+ containerEl.style.color = textColor;
+ }
+
+ // 应用到搜索框的文字颜色(不包括 cbi-select)
+ var inputs = document.querySelectorAll('.filter-input');
+ inputs.forEach(function(input) {
+ input.style.color = textColor;
+ });
+ }
+ } catch (e) {
+ // 如果检测失败,使用默认值
+ console.log('Theme adaptation:', e);
+ }
+ }
+
+ // 初始应用主题颜色
+ setTimeout(applyThemeColors, 100);
+
+ // 监听 DOM 变化,自动应用到新创建的元素
+ if (typeof MutationObserver !== 'undefined') {
+ var observer = new MutationObserver(function(mutations) {
+ applyThemeColors();
+ });
+
+ setTimeout(function() {
+ var container = document.querySelector('.bandix-dns-container');
+ if (container) {
+ observer.observe(container, {
+ childList: true,
+ subtree: true
+ });
+ }
+ }, 200);
+ }
return container;
}
diff --git a/luci-app-bandix/htdocs/luci-static/resources/view/bandix/index.js b/luci-app-bandix/htdocs/luci-static/resources/view/bandix/index.js
index 0cda0ea..3a2f108 100644
--- a/luci-app-bandix/htdocs/luci-static/resources/view/bandix/index.js
+++ b/luci-app-bandix/htdocs/luci-static/resources/view/bandix/index.js
@@ -4,663 +4,41 @@
'require uci';
'require rpc';
'require poll';
-'use strict';
+// 暗色模式检测已改为使用 CSS 媒体查询 @media (prefers-color-scheme: dark)
-const translations = {
- 'zh-cn': {
- 'Bandix 流量监控': 'Bandix 流量监控',
- '正在加载数据...': '正在加载数据...',
- '无法获取数据': '无法获取数据',
- '无法获取历史数据': '无法获取历史数据',
- '主机名': '主机名',
- 'IP地址': 'IP地址',
- 'MAC地址': 'MAC地址',
- '下载速度': '下载速度',
- '上传速度': '上传速度',
- '总下载量': '总下载量',
- '总上传量': '总上传量',
- '下载限速': '下载限速',
- '上传限速': '上传限速',
- '界面语言': '界面语言',
- '选择 Bandix 流量监控的显示语言': '选择 Bandix 流量监控的显示语言',
- '设备信息': '设备信息',
- '设备列表': '设备列表',
- 'LAN 流量': 'LAN 流量',
- 'WAN 流量': 'WAN 流量',
- '限速设置': '限速设置',
- '操作': '操作',
- '在线设备': '在线设备',
- '仅限WAN 流量': '仅限WAN 流量',
- '设置': '设置',
- '设备设置': '设备设置',
- '限速设置': '限速设置',
- '取消限速': '取消限速',
- '保存': '保存',
- '取消': '取消',
- '设置限速': '设置限速',
- '设备': '设备',
- '上传限速': '上传限速',
- '下载限速': '下载限速',
- '主机名': '主机名',
- '设置主机名': '设置主机名',
- '请输入主机名': '请输入主机名',
- '主机名设置成功': '主机名设置成功',
- '主机名设置失败': '主机名设置失败',
- '无限制': '无限制',
- '设置成功': '设置成功',
- '设置失败': '设置失败',
- '请输入有效的速度值': '请输入有效的速度值',
- '速度值必须大于0': '速度值必须大于0',
- '保存中...': '保存中...',
- '限速功能仅对 WAN 流量生效。': '限速功能仅对 WAN 流量生效。',
- '提示:输入 0 表示无限制': '提示:输入 0 表示无限制',
- '历史流量趋势': '历史流量趋势',
- '选择设备': '选择设备',
- '所有设备': '所有设备',
- '时间范围': '时间范围',
- '最近5分钟': '最近5分钟',
- '最近30分钟': '最近30分钟',
- '最近2小时': '最近2小时',
- '类型': '类型',
- '总流量': '总流量',
- 'LAN 流量': 'LAN 流量',
- 'WAN 流量': 'WAN 流量',
- '刷新': '刷新',
- '上传速率': '上传速率',
- '下载速率': '下载速率',
- '最近': '最近',
- '秒': '秒',
- '分钟': '分钟',
- '小时': '小时',
- '天': '天',
- '周': '周',
- '其他速率': '其他速率',
- '累计流量': '累计流量',
- '总上传': '总上传',
- '总下载': '总下载',
- 'LAN 已上传': 'LAN 已上传',
- 'LAN 已下载': 'LAN 已下载',
- 'WAN 已上传': 'WAN 已上传',
- 'WAN 已下载': 'WAN 已下载',
- '总上传速率': '总上传速率',
- '总下载速率': '总下载速率',
- 'LAN 上传速率': 'LAN 上传速率',
- 'LAN 下载速率': 'LAN 下载速率',
- 'WAN 上传速率': 'WAN 上传速率',
- 'WAN 下载速率': 'WAN 下载速率',
- '从未上线': '从未上线',
- '刚刚': '刚刚',
- '分钟前': '分钟前',
- '小时前': '小时前',
- '天前': '天前',
- '个月前': '个月前',
- '年前': '年前',
- '最后上线': '最后上线',
- '缩放': '缩放',
- '排序方式': '排序方式',
- '在线状态': '在线状态',
- '总流量': '总流量',
- '升序': '升序',
- '降序': '降序',
- '按速度排序': '按速度排序',
- '按用量排序': '按用量排序',
- '简易模式': '简易模式',
- '详细模式': '详细模式'
- },
- 'zh-tw': {
- 'Bandix 流量监控': 'Bandix 流量監控',
- '正在加载数据...': '正在載入資料...',
- '无法获取数据': '無法獲取資料',
- '无法获取历史数据': '無法獲取歷史資料',
- '主机名': '主機名',
- 'IP地址': 'IP地址',
- 'MAC地址': 'MAC地址',
- '下载速度': '下載速度',
- '上传速度': '上傳速度',
- '总下载量': '總下載量',
- '总上传量': '總上傳量',
- '下载限速': '下載限速',
- '上传限速': '上傳限速',
- '界面语言': '介面語言',
- '选择 Bandix 流量监控的显示语言': '選擇 Bandix 流量監控的顯示語言',
- '设备信息': '設備資訊',
- '设备列表': '設備列表',
- 'LAN 流量': '局域網流量',
- 'WAN 流量': '跨網路流量',
- '限速设置': '限速設定',
- '操作': '操作',
- '在线设备': '線上設備',
- '仅限WAN 流量': '僅限跨網路',
- '设置': '設定',
- '设备设置': '設備設定',
- '限速设置': '限速設定',
- '取消限速': '取消限速',
- '保存': '儲存',
- '取消': '取消',
- '设置限速': '設定限速',
- '设备': '設備',
- '上传限速': '上傳限速',
- '下载限速': '下載限速',
- '主机名': '主機名',
- '设置主机名': '設定主機名',
- '请输入主机名': '請輸入主機名',
- '主机名设置成功': '主機名設定成功',
- '主机名设置失败': '主機名設定失敗',
- '无限制': '無限制',
- '设置成功': '設定成功',
- '设置失败': '設定失敗',
- '请输入有效的速度值': '請輸入有效的速度值',
- '速度值必须大于0': '速度值必須大於0',
- '保存中...': '儲存中...',
- '限速功能仅对 WAN 流量生效。': '限速功能僅對跨網路流量生效。',
- '提示:输入 0 表示无限制': '提示:輸入 0 表示無限制',
- '历史流量趋势': '歷史流量趨勢',
- '选择设备': '選擇設備',
- '所有设备': '所有設備',
- '时间范围': '時間範圍',
- '最近5分钟': '最近5分鐘',
- '最近30分钟': '最近30分鐘',
- '最近2小时': '最近2小時',
- '类型': '類型',
- '总流量': '總流量',
- 'LAN 流量': '局域網',
- 'WAN 流量': '跨網路',
- '刷新': '重新整理',
- '上传速率': '上傳速率',
- '下载速率': '下載速率',
- '最近': '最近',
- '秒': '秒',
- '分钟': '分鐘',
- '小时': '小時',
- '天': '天',
- '周': '週',
- '其他速率': '其他速率',
- '累计流量': '累計流量',
- '总上传': '總上傳',
- '总下载': '總下載',
- 'LAN 已上传': 'LAN 已上傳',
- 'LAN 已下载': 'LAN 已下載',
- 'WAN 已上传': 'WAN 已上傳',
- 'WAN 已下载': 'WAN 已下載',
- '总上传速率': '總上傳速率',
- '总下载速率': '總下載速率',
- 'LAN 上传速率': '局域上傳速率',
- 'LAN 下载速率': '局域下載速率',
- 'WAN 上传速率': '跨網上傳速率',
- 'WAN 下载速率': '跨網下載速率',
- '从未上线': '從未上線',
- '刚刚': '剛剛',
- '分钟前': '分鐘前',
- '小时前': '小時前',
- '天前': '天前',
- '个月前': '個月前',
- '年前': '年前',
- '最后上线': '最後上線',
- '缩放': '縮放',
- '排序方式': '排序方式',
- '在线状态': '線上狀態',
- '总流量': '總流量',
- '升序': '升序',
- '降序': '降序',
- '按速度排序': '按速度排序',
- '按用量排序': '按用量排序',
- '简易模式': '簡易模式',
- '详细模式': '詳細模式'
- },
- 'en': {
- 'Bandix 流量监控': 'Bandix Traffic Monitor',
- '正在加载数据...': 'Loading data...',
- '无法获取数据': 'Unable to fetch data',
- '无法获取历史数据': 'Unable to fetch history data',
- '主机名': 'Hostname',
- 'IP地址': 'IP Address',
- 'MAC地址': 'MAC Address',
- '下载速度': 'Download Speed',
- '上传速度': 'Upload Speed',
- '总下载量': 'Total Download',
- '总上传量': 'Total Upload',
- '下载限速': 'Download Limit',
- '上传限速': 'Upload Limit',
- '界面语言': 'Interface Language',
- '选择 Bandix 流量监控的显示语言': 'Select the display language for Bandix Traffic Monitor',
- '设备信息': 'Device Info',
- '设备列表': 'Device List',
- 'LAN 流量': 'LAN Traffic',
- 'WAN 流量': 'WAN Traffic',
- '限速设置': 'Rate Limit',
- '操作': 'Actions',
- '在线设备': 'Online Devices',
- '仅限WAN 流量': 'WAN Only',
- '设置': 'Settings',
- '设备设置': 'Device Settings',
- '限速设置': 'Rate Limit Settings',
- '取消限速': 'Remove Rate Limit',
- '保存': 'Save',
- '取消': 'Cancel',
- '设置限速': 'Set Rate Limit',
- '设备': 'Device',
- '上传限速': 'Upload Limit',
- '下载限速': 'Download Limit',
- '主机名': 'Hostname',
- '设置主机名': 'Set Hostname',
- '请输入主机名': 'Please enter hostname',
- '主机名设置成功': 'Hostname set successfully',
- '主机名设置失败': 'Failed to set hostname',
- '无限制': 'Unlimited',
- '设置成功': 'Settings saved successfully',
- '设置失败': 'Failed to save settings',
- '请输入有效的速度值': 'Please enter a valid speed value',
- '速度值必须大于0': 'Speed value must be greater than 0',
- '保存中...': 'Saving...',
- '限速功能仅对 WAN 流量生效。': 'Rate limiting only applies to WAN traffic.',
- '提示:输入 0 表示无限制': 'Tip: Enter 0 for unlimited',
- '历史流量趋势': 'Traffic History',
- '选择设备': 'Select Device',
- '所有设备': 'All Devices',
- '时间范围': 'Time Range',
- '最近5分钟': 'Last 5 minutes',
- '最近30分钟': 'Last 30 minutes',
- '最近2小时': 'Last 2 hours',
- '类型': 'Type',
- '总流量': 'Total',
- 'LAN 流量': 'LAN',
- 'WAN 流量': 'WAN',
- '刷新': 'Refresh',
- '上传速率': 'Upload Rate',
- '下载速率': 'Download Rate',
- '最近': 'Last',
- '秒': 'second',
- '分钟': 'minute',
- '小时': 'hour',
- '天': 'day',
- '周': 'week',
- '其他速率': 'Other Rates',
- '累计流量': 'Cumulative',
- '总上传': 'Total Uploaded',
- '总下载': 'Total Downloaded',
- 'LAN 已上传': 'LAN Uploaded',
- 'LAN 已下载': 'LAN Downloaded',
- 'WAN 已上传': 'WAN Uploaded',
- 'WAN 已下载': 'WAN Downloaded',
- '总上传速率': 'Total Upload',
- '总下载速率': 'Total Download',
- 'LAN 上传速率': 'LAN Upload',
- 'LAN 下载速率': 'LAN Download',
- 'WAN 上传速率': 'WAN Upload',
- 'WAN 下载速率': 'WAN Download',
- '从未上线': 'Never Online',
- '刚刚': 'Just Now',
- '分钟前': 'min ago',
- '小时前': 'h ago',
- '天前': 'days ago',
- '个月前': 'months ago',
- '年前': 'years ago',
- '最后上线': 'Last Online',
- '缩放': 'Zoom',
- '排序方式': 'Sort By',
- '在线状态': 'Online Status',
- '总流量': 'Total Traffic',
- '升序': 'Ascending',
- '降序': 'Descending',
- '按速度排序': 'Sort by Speed',
- '按用量排序': 'Sort by Traffic',
- '简易模式': 'Simple Mode',
- '详细模式': 'Detailed Mode'
- },
- 'fr': {
- 'Bandix 流量监控': 'Moniteur de Trafic Bandix',
- '正在加载数据...': 'Chargement des données...',
- '无法获取数据': 'Impossible de récupérer les données',
- '无法获取历史数据': 'Impossible de récupérer les données historiques',
- '主机名': 'Nom d\'hôte',
- 'IP地址': 'Adresse IP',
- 'MAC地址': 'Adresse MAC',
- '下载速度': 'Vitesse de téléchargement',
- '上传速度': 'Vitesse de téléversement',
- '总下载量': 'Téléchargement total',
- '总上传量': 'Téléversement total',
- '下载限速': 'Limite de téléchargement',
- '上传限速': 'Limite de téléversement',
- '界面语言': 'Langue de l\'interface',
- '选择 Bandix 流量监控的显示语言': 'Sélectionner la langue d\'affichage pour le Moniteur de Trafic Bandix',
- '设备信息': 'Informations sur l\'appareil',
- '设备列表': 'Liste des appareils',
- 'LAN 流量': 'Trafic LAN',
- 'WAN 流量': 'Trafic WAN',
- '限速设置': 'Limitation de débit',
- '操作': 'Actions',
- '在线设备': 'Appareils en ligne',
- '仅限WAN 流量': 'WAN uniquement',
- '设置': 'Paramètres',
- '设备设置': 'Paramètres de l\'appareil',
- '限速设置': 'Paramètres de limitation',
- '取消限速': 'Supprimer la limitation',
- '保存': 'Enregistrer',
- '取消': 'Annuler',
- '设置限速': 'Définir la limitation',
- '设备': 'Appareil',
- '上传限速': 'Limite de téléversement',
- '下载限速': 'Limite de téléchargement',
- '无限制': 'Illimité',
- '设置成功': 'Paramètres enregistrés avec succès',
- '设置失败': 'Échec de l\'enregistrement des paramètres',
- '请输入有效的速度值': 'Veuillez entrer une valeur de vitesse valide',
- '速度值必须大于0': 'La valeur de vitesse doit être supérieure à 0',
- '保存中...': 'Enregistrement...',
- '限速功能仅对 WAN 流量生效。': 'La limitation de débit ne s\'applique qu\'au trafic WAN.',
- '提示:输入 0 表示无限制': 'Conseil : Entrez 0 pour illimité',
- '历史流量趋势': 'Historique du trafic',
- '选择设备': 'Sélectionner l\'appareil',
- '所有设备': 'Tous les appareils',
- '时间范围': 'Plage de temps',
- '最近5分钟': '5 dernières minutes',
- '最近30分钟': '30 dernières minutes',
- '最近2小时': '2 dernières heures',
- '类型': 'Type',
- '总流量': 'Total',
- 'LAN 流量': 'LAN',
- 'WAN 流量': 'WAN',
- '刷新': 'Actualiser',
- '上传速率': 'Débit montant',
- '下载速率': 'Débit descendant',
- '最近': 'Dernières',
- '秒': 'seconde',
- '分钟': 'minute',
- '小时': 'heure',
- '天': 'jour',
- '周': 'semaine',
- '其他速率': 'Autres débits',
- '累计流量': 'Trafic cumulé',
- '总上传': 'Total téléversé',
- '总下载': 'Total téléchargé',
- 'LAN 已上传': 'LAN Téléversé',
- 'LAN 已下载': 'LAN Téléchargé',
- 'WAN 已上传': 'WAN Téléversé',
- 'WAN 已下载': 'WAN Téléchargé',
- '总上传速率': 'Vitesse de téléversement totale',
- '总下载速率': 'Vitesse de téléchargement totale',
- 'LAN 上传速率': 'Vitesse de téléversement LAN',
- 'LAN 下载速率': 'Vitesse de téléchargement LAN',
- 'WAN 上传速率': 'Vitesse de téléversement WAN',
- 'WAN 下载速率': 'Vitesse de téléchargement WAN',
- '从未上线': 'Jamais en ligne',
- '刚刚': 'À l\'instant',
- '分钟前': 'min',
- '小时前': 'h',
- '天前': 'j',
- '个月前': 'mois',
- '年前': 'an',
- '最后上线': 'Dernière connexion',
- '缩放': 'Zoom',
- '排序方式': 'Trier par',
- '在线状态': 'Statut en ligne',
- '总流量': 'Trafic total',
- '升序': 'Croissant',
- '降序': 'Décroissant',
- '按速度排序': 'Trier par vitesse',
- '按用量排序': 'Trier par volume',
- '简易模式': 'Mode simple',
- '详细模式': 'Mode détaillé'
- },
- 'ja': {
- 'Bandix 流量监控': 'Bandix トラフィックモニター',
- '正在加载数据...': 'データを読み込み中...',
- '无法获取数据': 'データを取得できません',
- '无法获取历史数据': '履歴データを取得できません',
- '主机名': 'ホスト名',
- 'IP地址': 'IPアドレス',
- 'MAC地址': 'MACアドレス',
- '下载速度': 'ダウンロード速度',
- '上传速度': 'アップロード速度',
- '总下载量': '総ダウンロード量',
- '总上传量': '総アップロード量',
- '下载限速': 'ダウンロード制限',
- '上传限速': 'アップロード制限',
- '界面语言': 'インターフェース言語',
- '选择 Bandix 流量监控的显示语言': 'Bandix トラフィックモニターの表示言語を選択',
- '设备信息': 'デバイス情報',
- '设备列表': 'デバイスリスト',
- 'LAN 流量': 'LAN トラフィック',
- 'WAN 流量': 'WAN トラフィック',
- '限速设置': '速度制限',
- '操作': '操作',
- '在线设备': 'オンラインデバイス',
- '仅限WAN 流量': 'WAN のみ',
- '设置': '設定',
- '设备设置': 'デバイス設定',
- '限速设置': '速度制限設定',
- '取消限速': '速度制限を削除',
- '保存': '保存',
- '取消': 'キャンセル',
- '设置限速': '速度制限を設定',
- '设备': 'デバイス',
- '上传限速': 'アップロード制限',
- '下载限速': 'ダウンロード制限',
- '无限制': '無制限',
- '设置成功': '設定が正常に保存されました',
- '设置失败': '設定の保存に失敗しました',
- '请输入有效的速度值': '有効な速度値を入力してください',
- '速度值必须大于0': '速度値は0より大きい必要があります',
- '保存中...': '保存中...',
- '限速功能仅对 WAN 流量生效。': '速度制限はWANトラフィックにのみ適用されます。',
- '提示:输入 0 表示无限制': 'ヒント:0を入力すると無制限になります',
- '历史流量趋势': 'トラフィック履歴',
- '选择设备': 'デバイスを選択',
- '所有设备': 'すべてのデバイス',
- '时间范围': '時間範囲',
- '最近5分钟': '最近5分',
- '最近30分钟': '最近30分',
- '最近2小时': '最近2時間',
- '类型': 'タイプ',
- '总流量': '合計',
- 'LAN 流量': 'LAN',
- 'WAN 流量': 'WAN',
- '刷新': '更新',
- '上传速率': 'アップロードレート',
- '下载速率': 'ダウンロードレート',
- '最近': '直近',
- '秒': '秒',
- '分钟': '分',
- '小时': '時間',
- '天': '日',
- '周': '週間',
- '其他速率': 'その他の速度',
- '累计流量': '累計トラフィック',
- '总上传': '総アップロード',
- '总下载': '総ダウンロード',
- 'LAN 已上传': 'LAN アップロード済み',
- 'LAN 已下载': 'LAN ダウンロード済み',
- 'WAN 已上传': 'WAN アップロード済み',
- 'WAN 已下载': 'WAN ダウンロード済み',
- '总上传速率': '総アップロード速度',
- '总下载速率': '総ダウンロード速度',
- 'LAN 上传速率': 'LAN アップロード速度',
- 'LAN 下载速率': 'LAN ダウンロード速度',
- 'WAN 上传速率': 'WAN アップロード速度',
- 'WAN 下载速率': 'WAN ダウンロード速度',
- '从未上线': 'オンライン未経験',
- '刚刚': '今',
- '分钟前': '分前',
- '小时前': '時間前',
- '天前': '日前',
- '个月前': 'ヶ月前',
- '年前': '年前',
- '最后上线': '最終オンライン',
- '缩放': 'ズーム',
- '排序方式': '並び順',
- '在线状态': 'オンライン状態',
- '总流量': '総トラフィック',
- '升序': '昇順',
- '降序': '降順',
- '按速度排序': '速度順',
- '按用量排序': '使用量順',
- '简易模式': 'シンプルモード',
- '详细模式': '詳細モード'
- },
- 'ru': {
- 'Bandix 流量监控': 'Монитор Трафика Bandix',
- '正在加载数据...': 'Загрузка данных...',
- '无法获取数据': 'Не удалось получить данные',
- '无法获取历史数据': 'Не удалось получить исторические данные',
- '主机名': 'Имя хоста',
- 'IP地址': 'IP-адрес',
- 'MAC地址': 'MAC-адрес',
- '下载速度': 'Скорость загрузки',
- '上传速度': 'Скорость выгрузки',
- '总下载量': 'Общая загрузка',
- '总上传量': 'Общая выгрузка',
- '下载限速': 'Ограничение загрузки',
- '上传限速': 'Ограничение выгрузки',
- '界面语言': 'Язык интерфейса',
- '选择 Bandix 流量监控的显示语言': 'Выберите язык отображения для Монитора Трафика Bandix',
- '设备信息': 'Информация об устройстве',
- '设备列表': 'Список устройств',
- 'LAN 流量': 'Трафик LAN',
- 'WAN 流量': 'Трафик WAN',
- '限速设置': 'Ограничение скорости',
- '操作': 'Действия',
- '在线设备': 'Онлайн устройства',
- '仅限WAN 流量': 'Только WAN',
- '设置': 'Настройки',
- '设备设置': 'Настройки устройства',
- '限速设置': 'Настройки ограничения',
- '取消限速': 'Удалить ограничение',
- '保存': 'Сохранить',
- '取消': 'Отмена',
- '设置限速': 'Установить ограничение',
- '设备': 'Устройство',
- '上传限速': 'Ограничение выгрузки',
- '下载限速': 'Ограничение загрузки',
- '无限制': 'Без ограничений',
- '设置成功': 'Настройки успешно сохранены',
- '设置失败': 'Не удалось сохранить настройки',
- '请输入有效的速度值': 'Пожалуйста, введите допустимое значение скорости',
- '速度值必须大于0': 'Значение скорости должно быть больше 0',
- '保存中...': 'Сохранение...',
- '限速功能仅对 WAN 流量生效。': 'Ограничение скорости применяется только к WAN-трафику.',
- '提示:输入 0 表示无限制': 'Совет: Введите 0 для снятия ограничений',
- '历史流量趋势': 'История трафика',
- '选择设备': 'Выбрать устройство',
- '所有设备': 'Все устройства',
- '时间范围': 'Временной диапазон',
- '最近5分钟': 'Последние 5 минут',
- '最近30分钟': 'Последние 30 минут',
- '最近2小时': 'Последние 2 часа',
- '类型': 'Тип',
- '总流量': 'Общий',
- 'LAN 流量': 'LAN',
- 'WAN 流量': 'WAN',
- '刷新': 'Обновить',
- '上传速率': 'Скорость отправки',
- '下载速率': 'Скорость загрузки',
- '最近': 'За последние',
- '秒': 'сек.',
- '分钟': 'мин.',
- '小时': 'ч.',
- '天': 'дн.',
- '周': 'нед.',
- '其他速率': 'Другие скорости',
- '累计流量': 'Суммарный трафик',
- '总上传': 'Всего отправлено',
- '总下载': 'Всего получено',
- 'LAN 已上传': 'LAN Отправлено',
- 'LAN 已下载': 'LAN Получено',
- 'WAN 已上传': 'WAN Отправлено',
- 'WAN 已下载': 'WAN Получено',
- '总上传速率': 'Общая скорость отправки',
- '总下载速率': 'Общая скорость загрузки',
- 'LAN 上传速率': 'Скорость отправки LAN',
- 'LAN 下载速率': 'Скорость загрузки LAN',
- 'WAN 上传速率': 'Скорость отправки WAN',
- 'WAN 下载速率': 'Скорость загрузки WAN',
- '从未上线': 'Никогда не был онлайн',
- '刚刚': 'Только что',
- '分钟前': 'мин назад',
- '小时前': 'ч назад',
- '天前': 'дн назад',
- '个月前': 'мес назад',
- '年前': 'лет назад',
- '最后上线': 'Последний онлайн',
- '缩放': 'Масштаб',
- '排序方式': 'Сортировка',
- '在线状态': 'Статус онлайн',
- '总流量': 'Общий трафик',
- '升序': 'По возрастанию',
- '降序': 'По убыванию',
- '按速度排序': 'По скорости',
- '按用量排序': 'По объёму',
- '简易模式': 'Простой режим',
- '详细模式': 'Подробный режим'
- }
-};
-
-function getTranslation(key, language) {
- return translations[language]?.[key] || key;
-}
-
-function getSystemLanguage() {
- // 尝试获取 LuCI 的语言设置
- var luciLang = uci.get('luci', 'main', 'lang');
-
- if (luciLang && translations[luciLang]) {
- return luciLang;
- }
-
- // 如果没有 LuCI 语言设置,尝试获取浏览器语言作为回退
- var systemLang = document.documentElement.lang || 'en';
-
- if (translations[systemLang]) {
- return systemLang;
- }
-
- // 最终回退到英语
- return 'en';
-}
-
-function isDarkMode() {
- // 首先检查用户设置的主题
- var userTheme = uci.get('bandix', 'general', 'theme');
- if (userTheme) {
- if (userTheme === 'dark') {
- return true;
- } else if (userTheme === 'light') {
- return false;
- }
- // 如果是 'auto',继续检查系统主题
- }
-
+// 检测主题类型:返回 'wide'(宽主题,如 Argon)或 'narrow'(窄主题,如 Bootstrap)
+function getThemeType() {
// 获取 LuCI 主题设置
var mediaUrlBase = uci.get('luci', 'main', 'mediaurlbase');
- if (mediaUrlBase && mediaUrlBase.toLowerCase().includes('dark')) {
- return true;
+
+ if (!mediaUrlBase) {
+ // 如果无法获取,尝试从 DOM 中检测
+ var linkTags = document.querySelectorAll('link[rel="stylesheet"]');
+ for (var i = 0; i < linkTags.length; i++) {
+ var href = linkTags[i].getAttribute('href') || '';
+ if (href.toLowerCase().includes('argon')) {
+ return 'wide';
+ }
+ }
+ // 默认返回窄主题
+ return 'narrow';
}
- // 如果是 argon 主题,检查 argon 配置
- if (mediaUrlBase && mediaUrlBase.toLowerCase().includes('argon')) {
- var argonMode = uci.get('argon', '@global[0]', 'mode');
- if (argonMode) {
- if (argonMode.toLowerCase() === 'dark') {
- return true;
- } else if (argonMode.toLowerCase() === 'light') {
- return false;
- }
- // 如果是 'normal' 或 'auto',使用浏览器检测系统颜色偏好
- if (argonMode.toLowerCase() === 'normal' || argonMode.toLowerCase() === 'auto') {
- if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
- return true;
- }
- return false;
- }
+ var mediaUrlBaseLower = mediaUrlBase.toLowerCase();
+
+ // 宽主题关键词列表(可以根据需要扩展)
+ var wideThemeKeywords = ['argon', 'material', 'design', 'edge'];
+
+ // 检查是否是宽主题
+ for (var i = 0; i < wideThemeKeywords.length; i++) {
+ if (mediaUrlBaseLower.includes(wideThemeKeywords[i])) {
+ return 'wide';
}
}
- // 默认情况下也使用浏览器检测系统颜色偏好
- if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
- return true;
- }
-
- return false;
+ // 默认是窄主题(Bootstrap 等)
+ return 'narrow';
}
function formatSize(bytes) {
@@ -790,33 +168,22 @@ return view.extend({
},
render: function (data) {
- var language = uci.get('bandix', 'general', 'language');
- if (!language || language === 'auto') {
- language = getSystemLanguage();
- }
- var darkMode = isDarkMode();
-
- // 添加现代化样式,支持暗黑模式
+
+ // 添加现代化样式
var style = E('style', {}, `
.bandix-container {
- padding: 16px;
- background-color: ${darkMode ? '#1a1a1a' : '#f8fafc'};
- min-height: 100vh;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
- color: ${darkMode ? '#e2e8f0' : '#1f2937'};
}
.bandix-header {
display: flex;
align-items: center;
justify-content: space-between;
- margin-bottom: 20px;
}
.bandix-title {
font-size: 1.5rem;
font-weight: 600;
- color: ${darkMode ? '#f1f5f9' : '#1f2937'};
margin: 0;
}
@@ -830,17 +197,13 @@ return view.extend({
display: inline-flex;
border-radius: 4px;
overflow: hidden;
- border: 1px solid ${darkMode ? '#444444' : '#cbd5e1'};
}
.device-mode-btn {
- background-color: ${darkMode ? '#2a2a2a' : '#ffffff'};
border: none;
- border-right: 1px solid ${darkMode ? '#444444' : '#cbd5e1'};
padding: 0 12px;
font-size: 0.8125rem;
line-height: 1.8;
- color: ${darkMode ? '#a0a0a0' : '#64748b'};
cursor: pointer;
user-select: none;
transition: all 0.15s ease;
@@ -848,13 +211,8 @@ return view.extend({
height: 28px;
}
- .device-mode-btn:last-child {
- border-right: none;
- }
-
.device-mode-btn:hover:not(.active) {
- background-color: ${darkMode ? '#3a3a3a' : '#f1f5f9'};
- color: ${darkMode ? '#d0d0d0' : '#475569'};
+ opacity: 0.7;
}
.device-mode-btn.active {
@@ -863,29 +221,22 @@ return view.extend({
}
.bandix-badge {
- background-color: ${darkMode ? '#2a2a2a' : '#f8fafc'};
- border: 1px solid ${darkMode ? '#444444' : '#cbd5e1'};
border-radius: 4px;
padding: 4px 10px;
font-size: 0.875rem;
- color: ${darkMode ? '#d0d0d0' : '#475569'};
}
.bandix-alert {
- background-color: ${darkMode ? '#2a2a2a' : '#eff6ff'};
- border-left: 3px solid ${darkMode ? '#3b82f6' : '#2563eb'};
border-radius: 4px;
padding: 10px 12px;
- margin-bottom: 16px;
display: flex;
align-items: center;
+ justify-content: space-between;
gap: 10px;
- color: ${darkMode ? '#d0d0d0' : '#1e293b'};
font-size: 0.875rem;
}
.bandix-alert-icon {
- color: ${darkMode ? '#60a5fa' : '#2563eb'};
font-size: 0.875rem;
font-weight: 700;
width: 18px;
@@ -893,48 +244,20 @@ return view.extend({
display: flex;
align-items: center;
justify-content: center;
- border: 2px solid currentColor;
border-radius: 50%;
flex-shrink: 0;
}
- .bandix-card {
- background-color: ${darkMode ? '#2a2a2a' : 'white'};
- border-radius: 8px;
- border: 1px solid ${darkMode ? '#444444' : '#e2e8f0'};
- overflow: hidden;
- margin-bottom: 16px;
- box-shadow: 0 2px 8px rgba(0, 0, 0, ${darkMode ? '0.3' : '0.08'});
- }
-
- .bandix-card-header {
- padding: 16px;
- border-bottom: 1px solid ${darkMode ? '#444444' : '#e2e8f0'};
- background-color: ${darkMode ? '#2a2a2a' : '#f8fafc'};
- }
-
- .bandix-card-title {
- font-size: 1.125rem;
- font-weight: 600;
- color: ${darkMode ? '#f1f5f9' : '#1f2937'};
- margin: 0;
- display: flex;
- align-items: center;
- gap: 8px;
- }
.bandix-table {
width: 100%;
- border-collapse: collapse;
table-layout: fixed;
}
.bandix-table th {
- background-color: ${darkMode ? '#2a2a2a' : '#f8fafc'};
padding: 10px 16px;
text-align: left;
font-weight: 600;
- color: ${darkMode ? '#d0d0d0' : '#475569'};
border: none;
font-size: 0.875rem;
cursor: pointer;
@@ -944,7 +267,7 @@ return view.extend({
}
.bandix-table th:hover {
- background-color: ${darkMode ? '#3a3a3a' : '#f1f5f9'};
+ opacity: 0.7;
}
.bandix-table th.sortable::after {
@@ -985,11 +308,11 @@ return view.extend({
}
.th-split-section:hover {
- background-color: ${darkMode ? '#3a3a3a' : '#e5e7eb'};
+ opacity: 0.7;
}
.th-split-section.active {
- background-color: ${darkMode ? '#3a3a3a' : '#e5e7eb'};
+ opacity: 0.7;
}
.th-split-icon {
@@ -1005,7 +328,7 @@ return view.extend({
.th-split-divider {
width: 1px;
height: 16px;
- background-color: ${darkMode ? '#3a3a3a' : '#d1d5db'};
+ background-color: currentColor;
opacity: 0.5;
}
@@ -1015,12 +338,6 @@ return view.extend({
vertical-align: middle;
word-wrap: break-word;
overflow-wrap: break-word;
- color: ${darkMode ? '#d0d0d0' : '#334155'};
- border-bottom: 1px solid ${darkMode ? '#333333' : '#f1f5f9'};
- }
-
- .bandix-table tr:last-child td {
- border-bottom: none;
}
.bandix-table th:nth-child(1),
@@ -1040,7 +357,7 @@ return view.extend({
.bandix-table th:nth-child(4),
.bandix-table td:nth-child(4) {
- width: 22%;
+ width: 15%;
}
.bandix-table th:nth-child(5),
@@ -1061,7 +378,6 @@ return view.extend({
.device-name {
font-weight: 600;
- color: ${darkMode ? '#f1f5f9' : '#1f2937'};
display: flex;
align-items: center;
gap: 8px;
@@ -1083,18 +399,18 @@ return view.extend({
}
.device-ip {
- color: ${darkMode ? '#94a3b8' : '#6b7280'};
+ opacity: 0.7;
font-size: 0.875rem;
}
.device-ipv6 {
- color: ${darkMode ? '#94a3b8' : '#6b7280'};
+ opacity: 0.7;
font-size: 0.75rem;
font-family: monospace;
}
.device-mac {
- color: ${darkMode ? '#64748b' : '#9ca3af'};
+ opacity: 0.6;
font-size: 0.75rem;
}
@@ -1128,13 +444,9 @@ return view.extend({
font-size: 0.875rem;
}
- .traffic-speed {
- color: ${darkMode ? '#e2e8f0' : '#374151'};
- }
-
.traffic-total {
font-size: 0.75rem;
- color: #64748b;
+ opacity: 0.6;
margin-left: 4px;
}
@@ -1145,61 +457,36 @@ return view.extend({
}
.limit-badge {
- background-color: ${darkMode ? '#1a1a1a' : '#f8fafc'};
- color: ${darkMode ? '#a0a0a0' : '#64748b'};
padding: 3px 8px;
border-radius: 3px;
font-size: 0.75rem;
text-align: center;
margin-top: 4px;
- border: 1px solid ${darkMode ? '#444444' : '#e2e8f0'};
}
- .action-button {
- background-color: ${darkMode ? '#2a2a2a' : '#f8fafc'};
- border: 1px solid ${darkMode ? '#444444' : '#cbd5e1'};
- border-radius: 4px;
- padding: 6px 12px;
- cursor: pointer;
- font-size: 0.875rem;
- color: ${darkMode ? '#d0d0d0' : '#475569'};
- transition: all 0.15s ease;
- }
-
- .action-button:hover {
- background-color: ${darkMode ? '#3a3a3a' : '#e2e8f0'};
- border-color: ${darkMode ? '#555555' : '#94a3b8'};
- }
-
-
.loading {
text-align: center;
padding: 40px;
- color: ${darkMode ? '#94a3b8' : '#6b7280'};
+ opacity: 0.7;
font-style: italic;
}
.error {
text-align: center;
padding: 40px;
- color: ${darkMode ? '#f87171' : '#ef4444'};
}
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 16px;
- margin-bottom: 16px;
+ margin-bottom: 0;
+ margin-top: 0;
}
- .stats-card {
- background-color: ${darkMode ? '#2a2a2a' : 'white'};
- border-radius: 8px;
- padding: 20px;
- border: 1px solid ${darkMode ? '#444444' : '#e2e8f0'};
- position: relative;
- overflow: hidden;
- box-shadow: 0 2px 8px rgba(0, 0, 0, ${darkMode ? '0.3' : '0.08'});
+
+ .bandix-container > .cbi-section:last-of-type {
+ margin-bottom: 0;
}
.stats-card-header {
@@ -1212,12 +499,36 @@ return view.extend({
.stats-card-title {
font-size: 0.875rem;
font-weight: 600;
- color: ${darkMode ? '#94a3b8' : '#64748b'};
- margin: 0;
+ opacity: 0.7;
+ margin: 0 0 12px 0;
text-transform: uppercase;
letter-spacing: 0.025em;
}
+ .stats-grid .cbi-section {
+ padding: 16px;
+ border: 1px solid rgba(0, 0, 0, 0.1);
+ border-radius: 8px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
+ transition: box-shadow 0.2s ease, transform 0.2s ease, border-color 0.2s ease;
+ }
+
+ .stats-grid .cbi-section:hover {
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
+ transform: translateY(-2px);
+ }
+
+ @media (prefers-color-scheme: dark) {
+ .stats-grid .cbi-section {
+ border-color: rgba(255, 255, 255, 0.15);
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
+ }
+
+ .stats-grid .cbi-section:hover {
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
+ }
+ }
+
.stats-card-icon {
font-size: 0.875rem;
font-weight: 600;
@@ -1230,14 +541,13 @@ return view.extend({
.stats-card-main-value {
font-size: 2.25rem;
font-weight: 700;
- color: ${darkMode ? '#f1f5f9' : '#1f2937'};
margin: 0 0 8px 0;
line-height: 1;
}
.stats-card-sub-value {
font-size: 0.875rem;
- color: ${darkMode ? '#9ca3af' : '#6b7280'};
+ opacity: 0.7;
margin: 0;
}
@@ -1256,7 +566,7 @@ return view.extend({
}
.stats-detail-label {
- color: ${darkMode ? '#9ca3af' : '#6b7280'};
+ opacity: 0.7;
font-weight: 500;
}
@@ -1267,7 +577,6 @@ return view.extend({
.stats-title {
font-size: 0.875rem;
font-weight: 600;
- color: ${darkMode ? '#e2e8f0' : '#374151'};
margin-bottom: 8px;
display: flex;
align-items: center;
@@ -1277,7 +586,6 @@ return view.extend({
.stats-value {
font-size: 1.25rem;
font-weight: 700;
- color: ${darkMode ? '#f1f5f9' : '#1f2937'};
}
/* 模态框样式 */
@@ -1304,31 +612,34 @@ return view.extend({
}
.modal {
- background-color: ${darkMode ? '#2a2a2a' : 'white'};
- border-radius: 8px;
- border: 1px solid ${darkMode ? '#444444' : '#e2e8f0'};
max-width: 500px;
width: 90%;
max-height: 90vh;
overflow-y: auto;
opacity: 0;
transition: opacity 0.2s ease;
- box-shadow: 0 10px 40px rgba(0, 0, 0, ${darkMode ? '0.5' : '0.15'});
+ background-color: rgba(255, 255, 255, 0.98);
+ color: #1f2937;
}
.modal-overlay.show .modal {
opacity: 1;
}
+ @media (prefers-color-scheme: dark) {
+ .modal {
+ background-color: rgba(30, 30, 30, 0.98);
+ color: #e5e7eb;
+ }
+ }
+
.modal-header {
padding: 20px;
- border-bottom: 1px solid ${darkMode ? '#444444' : '#e2e8f0'};
}
.modal-title {
font-size: 1.25rem;
font-weight: 600;
- color: ${darkMode ? '#f1f5f9' : '#1f2937'};
margin: 0;
}
@@ -1350,77 +661,24 @@ return view.extend({
.form-label {
display: block;
font-weight: 600;
- color: ${darkMode ? '#e2e8f0' : '#374151'};
margin-bottom: 8px;
font-size: 0.875rem;
}
.form-input {
width: 100%;
- border: 1px solid ${darkMode ? '#444444' : '#cbd5e1'};
border-radius: 4px;
padding: 8px 12px;
font-size: 0.875rem;
transition: border-color 0.15s ease;
box-sizing: border-box;
- background-color: ${darkMode ? '#2a2a2a' : 'white'};
- color: ${darkMode ? '#e2e8f0' : '#1f2937'};
}
.form-input:focus {
outline: none;
- border-color: #3b82f6;
- }
-
- .form-select {
- width: 100%;
- border: 1px solid ${darkMode ? '#444444' : '#cbd5e1'};
- border-radius: 4px;
- padding: 8px 12px;
- font-size: 0.875rem;
- background-color: ${darkMode ? '#2a2a2a' : 'white'};
- transition: border-color 0.15s ease;
- box-sizing: border-box;
- color: ${darkMode ? '#e2e8f0' : '#1f2937'};
- }
-
- .form-select:focus {
- outline: none;
- border-color: #3b82f6;
- }
-
- .btn {
- padding: 8px 16px;
- border-radius: 4px;
- font-size: 0.875rem;
- font-weight: 500;
- cursor: pointer;
- transition: all 0.15s ease;
- border: none;
- }
-
- .btn-primary {
- background-color: #3b82f6;
- color: white;
- }
-
- .btn-primary:hover {
- background-color: #2563eb;
- }
-
- .btn-secondary {
- background-color: ${darkMode ? '#3a3a3a' : '#f1f5f9'};
- color: ${darkMode ? '#d0d0d0' : '#475569'};
- border: 1px solid ${darkMode ? '#555555' : '#cbd5e1'};
- }
-
- .btn-secondary:hover {
- background-color: ${darkMode ? '#4a4a4a' : '#e2e8f0'};
}
.device-summary {
- background-color: ${darkMode ? '#1a1a1a' : '#f8fafc'};
- border: 1px solid ${darkMode ? '#444444' : '#e2e8f0'};
border-radius: 4px;
padding: 12px;
margin-bottom: 16px;
@@ -1428,12 +686,11 @@ return view.extend({
.device-summary-name {
font-weight: 600;
- color: ${darkMode ? '#f1f5f9' : '#1f2937'};
margin-bottom: 4px;
}
.device-summary-details {
- color: ${darkMode ? '#94a3b8' : '#6b7280'};
+ opacity: 0.7;
font-size: 0.875rem;
}
@@ -1442,7 +699,7 @@ return view.extend({
display: inline-block;
width: 16px;
height: 16px;
- border: 2px solid #f3f4f6;
+ border: 2px solid transparent;
border-radius: 50%;
border-top-color: #3b82f6;
animation: spin 1s ease-in-out infinite;
@@ -1470,11 +727,8 @@ return view.extend({
gap: 12px;
align-items: center;
padding: 12px 16px;
- border-bottom: 1px solid ${darkMode ? '#444444' : '#e2e8f0'};
- background-color: ${darkMode ? '#2a2a2a' : '#f8fafc'};
}
- .history-controls .form-select,
- .history-controls .form-input {
+ .history-controls .cbi-select {
width: auto;
min-width: 160px;
}
@@ -1488,7 +742,7 @@ return view.extend({
align-items: center;
gap: 12px;
}
- .legend-item { display: flex; align-items: center; gap: 6px; font-size: 0.875rem; color: ${darkMode ? '#e2e8f0' : '#374151'}; }
+ .legend-item { display: flex; align-items: center; gap: 6px; font-size: 0.875rem; }
.legend-dot { width: 10px; height: 10px; border-radius: 50%; display: inline-block; }
.legend-up { background-color: #f97316; }
.legend-down { background-color: #06b6d4; }
@@ -1498,31 +752,40 @@ return view.extend({
display: none;
width: 320px;
box-sizing: border-box;
- background-color: ${darkMode ? '#2a2a2a' : 'white'};
- color: ${darkMode ? '#e2e8f0' : '#1f2937'};
- border: 1px solid ${darkMode ? '#444444' : '#e2e8f0'};
- border-radius: 6px;
- box-shadow: 0 6px 20px rgba(0,0,0,${darkMode ? '0.4' : '0.15'});
padding: 12px;
z-index: 10;
pointer-events: none;
font-size: 0.8125rem;
line-height: 1.5;
white-space: nowrap;
+ background-color: rgba(255, 255, 255, 0.98);
+ border: 1px solid rgba(0, 0, 0, 0.1);
+ border-radius: 6px;
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
+ color: #1f2937;
+ }
+
+ @media (prefers-color-scheme: dark) {
+ .history-tooltip {
+ background-color: rgba(30, 30, 30, 0.98);
+ border-color: rgba(255, 255, 255, 0.2);
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
+ color: #e5e7eb;
+ }
}
.history-tooltip .ht-title { font-weight: 700; margin-bottom: 6px; }
.history-tooltip .ht-row { display: flex; justify-content: space-between; gap: 12px; }
- .history-tooltip .ht-key { color: ${darkMode ? '#94a3b8' : '#6b7280'}; }
- .history-tooltip .ht-val { color: ${darkMode ? '#e2e8f0' : '#111827'}; }
- .history-tooltip .ht-device { margin-top: 4px; margin-bottom: 6px; color: ${darkMode ? '#94a3b8' : '#6b7280'}; font-size: 0.75rem; }
+ .history-tooltip .ht-key { opacity: 0.7; }
+ .history-tooltip .ht-val { }
+ .history-tooltip .ht-device { margin-top: 4px; margin-bottom: 6px; opacity: 0.7; font-size: 0.75rem; }
/* 强调关键信息的排版 */
.history-tooltip .ht-kpis { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-top: 2px; margin-bottom: 6px; }
- .history-tooltip .ht-kpi .ht-k-label { color: ${darkMode ? '#94a3b8' : '#6b7280'}; font-size: 0.75rem; }
+ .history-tooltip .ht-kpi .ht-k-label { opacity: 0.7; font-size: 0.75rem; }
.history-tooltip .ht-kpi .ht-k-value { font-size: 1rem; font-weight: 700; }
.history-tooltip .ht-kpi.down .ht-k-value { color: #06b6d4; }
.history-tooltip .ht-kpi.up .ht-k-value { color: #f97316; }
- .history-tooltip .ht-divider { height: 1px; background-color: ${darkMode ? '#3a3a3a' : '#e5e7eb'}; margin: 8px 0; }
- .history-tooltip .ht-section-title { font-weight: 600; font-size: 0.75rem; color: ${darkMode ? '#94a3b8' : '#6b7280'}; margin: 4px 0 6px 0; }
+ .history-tooltip .ht-divider { height: 1px; background-color: currentColor; opacity: 0.3; margin: 8px 0; }
+ .history-tooltip .ht-section-title { font-weight: 600; font-size: 0.75rem; opacity: 0.7; margin: 4px 0 6px 0; }
`);
document.head.appendChild(style);
@@ -1530,46 +793,43 @@ return view.extend({
var view = E('div', { 'class': 'bandix-container' }, [
// 头部
E('div', { 'class': 'bandix-header' }, [
- E('h1', { 'class': 'bandix-title' }, getTranslation('Bandix 流量监控', language)),
- E('div', { 'class': 'bandix-badge', 'id': 'device-count' }, getTranslation('在线设备', language) + ': 0 / 0')
+ E('h1', { 'class': 'bandix-title' }, _('Bandix Traffic Monitor'))
]),
- // 警告提示
+ // 警告提示(包含在线设备数)
E('div', { 'class': 'bandix-alert' }, [
- E('span', { 'class': 'bandix-alert-icon' }, '!'),
- E('span', {}, getTranslation('限速功能仅对 WAN 流量生效。', language))
+ E('span', {}, _('Rate limiting only applies to WAN traffic.')),
+ E('div', { 'class': 'bandix-badge', 'id': 'device-count' }, _('Online Devices') + ': 0 / 0')
]),
// 统计卡片
E('div', { 'class': 'stats-grid', 'id': 'stats-grid' }),
// 历史趋势卡片(无时间范围筛选)
- E('div', { 'class': 'bandix-card', 'id': 'history-card' }, [
- E('div', { 'class': 'bandix-card-header history-header' }, [
- E('div', { 'class': 'bandix-card-title' }, [
- getTranslation('历史流量趋势', language)
- ]),
+ E('div', { 'class': 'cbi-section', 'id': 'history-card' }, [
+ E('h3', { 'class': 'history-header', 'style': 'display: flex; align-items: center; justify-content: space-between;' }, [
+ E('span', {}, _('Traffic History')),
E('div', { 'class': 'history-legend' }, [
E('div', { 'class': 'legend-item' }, [
E('span', { 'class': 'legend-dot legend-up' }),
- getTranslation('上传速率', language)
+ _('Upload Rate')
]),
E('div', { 'class': 'legend-item' }, [
E('span', { 'class': 'legend-dot legend-down' }),
- getTranslation('下载速率', language)
+ _('Download Rate')
])
])
]),
E('div', { 'class': 'history-controls' }, [
- E('label', { 'class': 'form-label', 'style': 'margin: 0;' }, getTranslation('选择设备', language)),
- E('select', { 'class': 'form-select', 'id': 'history-device-select' }, [
- E('option', { 'value': '' }, getTranslation('所有设备', language))
+ E('label', { 'class': 'form-label', 'style': 'margin: 0;' }, _('Select Device')),
+ E('select', { 'class': 'cbi-select', 'id': 'history-device-select' }, [
+ E('option', { 'value': '' }, _('All Devices'))
]),
- E('label', { 'class': 'form-label', 'style': 'margin: 0;' }, getTranslation('类型', language)),
- E('select', { 'class': 'form-select', 'id': 'history-type-select' }, [
- E('option', { 'value': 'total' }, getTranslation('总流量', language)),
- E('option', { 'value': 'lan' }, getTranslation('LAN 流量', language)),
- E('option', { 'value': 'wan' }, getTranslation('WAN 流量', language))
+ E('label', { 'class': 'form-label', 'style': 'margin: 0;' }, _('Type')),
+ E('select', { 'class': 'cbi-select', 'id': 'history-type-select' }, [
+ E('option', { 'value': 'total' }, _('Total')),
+ E('option', { 'value': 'lan' }, _('LAN Traffic')),
+ E('option', { 'value': 'wan' }, _('WAN Traffic'))
]),
E('span', { 'class': 'bandix-badge', 'id': 'history-zoom-level', 'style': 'margin-left: 16px; display: none;' }, ''),
E('span', { 'class': 'bandix-badge', 'id': 'history-retention', 'style': 'margin-left: auto;' }, '')
@@ -1581,31 +841,29 @@ return view.extend({
]),
// 主要内容卡片
- E('div', { 'class': 'bandix-card' }, [
- E('div', { 'class': 'bandix-card-header history-header' }, [
- E('div', { 'class': 'bandix-card-title' }, [
- getTranslation('设备列表', language)
- ]),
+ E('div', { 'class': 'cbi-section' }, [
+ E('h3', { 'class': 'history-header', 'style': 'display: flex; align-items: center; justify-content: space-between;' }, [
+ E('span', {}, _('Device List')),
E('div', { 'class': 'device-mode-group' }, [
E('button', {
'class': 'device-mode-btn' + (localStorage.getItem('bandix_device_mode') !== 'detailed' ? ' active' : ''),
'data-mode': 'simple'
- }, getTranslation('简易模式', language)),
+ }, _('Simple Mode')),
E('button', {
'class': 'device-mode-btn' + (localStorage.getItem('bandix_device_mode') === 'detailed' ? ' active' : ''),
'data-mode': 'detailed'
- }, getTranslation('详细模式', language))
+ }, _('Detailed Mode'))
])
]),
E('div', { 'id': 'traffic-status' }, [
E('table', { 'class': 'bandix-table' }, [
E('thead', {}, [
E('tr', {}, [
- E('th', {}, getTranslation('设备信息', language)),
- E('th', {}, getTranslation('LAN 流量', language)),
- E('th', {}, getTranslation('WAN 流量', language)),
- E('th', {}, getTranslation('限速设置', language)),
- E('th', {}, getTranslation('操作', language))
+ E('th', {}, _('Device Info')),
+ E('th', {}, _('LAN Traffic')),
+ E('th', {}, _('WAN Traffic')),
+ E('th', {}, _('Rate Limit')),
+ E('th', {}, _('Actions'))
])
]),
E('tbody', {})
@@ -1644,35 +902,35 @@ return view.extend({
var modal = E('div', { 'class': 'modal-overlay', 'id': 'rate-limit-modal' }, [
E('div', { 'class': 'modal' }, [
E('div', { 'class': 'modal-header' }, [
- E('h3', { 'class': 'modal-title' }, getTranslation('设备设置', language))
+ E('h3', { 'class': 'modal-title' }, _('Device Settings'))
]),
E('div', { 'class': 'modal-body' }, [
E('div', { 'class': 'device-summary', 'id': 'modal-device-summary' }),
E('div', { 'class': 'form-group' }, [
- E('label', { 'class': 'form-label' }, getTranslation('主机名', language)),
- E('input', { 'type': 'text', 'class': 'form-input', 'id': 'device-hostname-input', 'placeholder': getTranslation('请输入主机名', language) }),
- E('div', { 'style': 'font-size: 0.75rem; color: #6b7280; margin-top: 4px;' }, getTranslation('设置主机名', language))
+ E('label', { 'class': 'form-label' }, _('Hostname')),
+ E('input', { 'type': 'text', 'class': 'form-input', 'id': 'device-hostname-input', 'placeholder': _('Please enter hostname') }),
+ E('div', { 'style': 'font-size: 0.75rem; color: #6b7280; margin-top: 4px;' }, _('Set Hostname'))
]),
E('div', { 'class': 'form-group' }, [
- E('label', { 'class': 'form-label' }, getTranslation('上传限速', language)),
+ E('label', { 'class': 'form-label' }, _('Upload Limit')),
E('div', { 'style': 'display: flex; gap: 8px;' }, [
E('input', { 'type': 'number', 'class': 'form-input', 'id': 'upload-limit-value', 'min': '0', 'step': '1', 'placeholder': '0' }),
- E('select', { 'class': 'form-select', 'id': 'upload-limit-unit', 'style': 'width: 100px;' })
+ E('select', { 'class': 'cbi-select', 'id': 'upload-limit-unit', 'style': 'width: 100px;' })
]),
- E('div', { 'style': 'font-size: 0.75rem; color: #6b7280; margin-top: 4px;' }, getTranslation('提示:输入 0 表示无限制', language))
+ E('div', { 'style': 'font-size: 0.75rem; color: #6b7280; margin-top: 4px;' }, _('Tip: Enter 0 for unlimited'))
]),
E('div', { 'class': 'form-group' }, [
- E('label', { 'class': 'form-label' }, getTranslation('下载限速', language)),
+ E('label', { 'class': 'form-label' }, _('Download Limit')),
E('div', { 'style': 'display: flex; gap: 8px;' }, [
E('input', { 'type': 'number', 'class': 'form-input', 'id': 'download-limit-value', 'min': '0', 'step': '1', 'placeholder': '0' }),
- E('select', { 'class': 'form-select', 'id': 'download-limit-unit', 'style': 'width: 100px;' })
+ E('select', { 'class': 'cbi-select', 'id': 'download-limit-unit', 'style': 'width: 100px;' })
]),
- E('div', { 'style': 'font-size: 0.75rem; color: #6b7280; margin-top: 4px;' }, getTranslation('提示:输入 0 表示无限制', language))
+ E('div', { 'style': 'font-size: 0.75rem; color: #6b7280; margin-top: 4px;' }, _('Tip: Enter 0 for unlimited'))
])
]),
E('div', { 'class': 'modal-footer' }, [
- E('button', { 'class': 'btn btn-secondary', 'id': 'modal-cancel' }, getTranslation('取消', language)),
- E('button', { 'class': 'btn btn-primary', 'id': 'modal-save' }, getTranslation('保存', language))
+ E('button', { 'class': 'cbi-button cbi-button-reset', 'id': 'modal-cancel' }, _('Cancel')),
+ E('button', { 'class': 'cbi-button cbi-button-positive', 'id': 'modal-save' }, _('Save'))
])
])
]);
@@ -1805,6 +1063,83 @@ return view.extend({
}
document.getElementById('download-limit-unit').value = downloadUnit;
+ // 应用 cbi-section 的颜色到模态框
+ try {
+ // 优先从 cbi-section 获取颜色
+ var cbiSection = document.querySelector('.cbi-section');
+ var targetElement = cbiSection || document.querySelector('.main') || document.body;
+ var computedStyle = window.getComputedStyle(targetElement);
+ var bgColor = computedStyle.backgroundColor;
+ var textColor = computedStyle.color;
+
+ // 获取模态框元素
+ var modalElement = modal.querySelector('.modal');
+
+ // 确保背景色不透明
+ if (bgColor && bgColor !== 'rgba(0, 0, 0, 0)' && bgColor !== 'transparent') {
+ var rgbaMatch = bgColor.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/);
+ if (rgbaMatch) {
+ var r = parseInt(rgbaMatch[1]);
+ var g = parseInt(rgbaMatch[2]);
+ var b = parseInt(rgbaMatch[3]);
+ var alpha = rgbaMatch[4] ? parseFloat(rgbaMatch[4]) : 1;
+
+ if (alpha < 0.95) {
+ modalElement.style.backgroundColor = 'rgb(' + r + ', ' + g + ', ' + b + ')';
+ } else {
+ modalElement.style.backgroundColor = bgColor;
+ }
+ } else {
+ modalElement.style.backgroundColor = bgColor;
+ }
+ } else {
+ // 如果无法获取背景色,尝试从其他 cbi-section 获取
+ var allCbiSections = document.querySelectorAll('.cbi-section');
+ var foundBgColor = false;
+ for (var i = 0; i < allCbiSections.length; i++) {
+ var sectionStyle = window.getComputedStyle(allCbiSections[i]);
+ var sectionBg = sectionStyle.backgroundColor;
+ if (sectionBg && sectionBg !== 'rgba(0, 0, 0, 0)' && sectionBg !== 'transparent') {
+ var rgbaMatch = sectionBg.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/);
+ if (rgbaMatch) {
+ var r = parseInt(rgbaMatch[1]);
+ var g = parseInt(rgbaMatch[2]);
+ var b = parseInt(rgbaMatch[3]);
+ var alpha = rgbaMatch[4] ? parseFloat(rgbaMatch[4]) : 1;
+ if (alpha < 0.95) {
+ modalElement.style.backgroundColor = 'rgb(' + r + ', ' + g + ', ' + b + ')';
+ } else {
+ modalElement.style.backgroundColor = sectionBg;
+ }
+ } else {
+ modalElement.style.backgroundColor = sectionBg;
+ }
+ foundBgColor = true;
+ break;
+ }
+ }
+ // 如果无法获取背景色,CSS 会通过媒体查询自动处理暗色模式
+ if (!foundBgColor) {
+ // 不设置背景色,让 CSS 媒体查询处理
+ }
+ }
+
+ // 应用文字颜色
+ if (textColor && textColor !== 'rgba(0, 0, 0, 0)') {
+ modalElement.style.color = textColor;
+ } else {
+ if (cbiSection) {
+ var sectionTextColor = window.getComputedStyle(cbiSection).color;
+ if (sectionTextColor && sectionTextColor !== 'rgba(0, 0, 0, 0)') {
+ modalElement.style.color = sectionTextColor;
+ }
+ }
+ }
+ } catch(e) {
+ // 如果出错,CSS 会通过媒体查询自动处理暗色模式
+ // 不设置样式,让 CSS 处理
+ }
+
// 显示模态框并添加动画
modal.classList.add('show');
}
@@ -1828,7 +1163,7 @@ return view.extend({
var originalText = saveButton.textContent;
// 显示加载状态
- saveButton.innerHTML = ' ' + getTranslation('保存中...', language);
+ saveButton.innerHTML = ' ' + _('Saving...');
saveButton.classList.add('btn-loading');
var uploadLimit = 0;
@@ -1891,10 +1226,10 @@ return view.extend({
results.forEach(function(result, index) {
if (result && result.hostnameError) {
hasError = true;
- errorMessages.push(getTranslation('主机名设置失败', language));
+ errorMessages.push(_('Failed to set hostname'));
} else if (result && result.rateLimitError) {
hasError = true;
- errorMessages.push(getTranslation('设置失败', language));
+ errorMessages.push(_('Failed to save settings'));
} else if (result !== true && result !== undefined) {
// 检查是否有其他错误
if (result && result.error) {
@@ -1914,7 +1249,7 @@ return view.extend({
// 恢复按钮状态
saveButton.innerHTML = originalText;
saveButton.classList.remove('btn-loading');
- ui.addNotification(null, E('p', {}, getTranslation('设置失败', language)), 'error');
+ ui.addNotification(null, E('p', {}, _('Failed to save settings')), 'error');
});
}
@@ -1990,7 +1325,7 @@ return view.extend({
var prev = select.value;
// 重建选项
select.innerHTML = '';
- select.appendChild(E('option', { 'value': '' }, getTranslation('所有设备', language)));
+ select.appendChild(E('option', { 'value': '' }, _('All Devices')));
sortedDevices.forEach(function (d) {
var label = (d.hostname || d.ip || d.mac || '-') + (d.ip ? ' (' + d.ip + ')' : '') + (d.mac ? ' [' + d.mac + ']' : '');
select.appendChild(E('option', { 'value': d.mac }, label));
@@ -2020,11 +1355,18 @@ return view.extend({
var zoomLevelElement = document.getElementById('history-zoom-level');
if (!zoomLevelElement) return;
+ // 如果是窄主题,隐藏 zoom 显示
+ var themeType = getThemeType();
+ if (themeType === 'narrow') {
+ zoomLevelElement.style.display = 'none';
+ return;
+ }
+
if (zoomScale <= 1) {
zoomLevelElement.style.display = 'none';
} else {
zoomLevelElement.style.display = 'inline-block';
- zoomLevelElement.textContent = getTranslation('缩放', language) + ': ' + zoomScale.toFixed(1) + 'x';
+ zoomLevelElement.textContent = _('Zoom') + ': ' + zoomScale.toFixed(1) + 'x';
}
}
@@ -2109,7 +1451,7 @@ return view.extend({
// 网格与Y轴刻度(更细更淡)
var gridLines = 4;
- ctx.strokeStyle = (darkMode ? 'rgba(148,163,184,0.06)' : 'rgba(148,163,184,0.08)');
+ ctx.strokeStyle = 'rgba(148,163,184,0.08)';
ctx.lineWidth = 0.8;
for (var g = 0; g <= gridLines; g++) {
var y = padding.top + (innerH * g / gridLines);
@@ -2118,7 +1460,7 @@ return view.extend({
ctx.lineTo(width - padding.right, y);
ctx.stroke();
var val = Math.round(maxVal * (gridLines - g) / gridLines);
- ctx.fillStyle = (darkMode ? 'rgba(148,163,184,0.7)' : '#9ca3af');
+ ctx.fillStyle = '#9ca3af';
ctx.font = '12px sans-serif';
ctx.textAlign = 'right';
ctx.textBaseline = 'middle';
@@ -2213,8 +1555,7 @@ return view.extend({
hoverIdx = Math.max(0, Math.min(n - 1, hoverIdx));
var hoverX = info.padding.left + (n > 1 ? stepX * hoverIdx : innerW / 2);
ctx.save();
- var hoverColor = (typeof darkMode !== 'undefined' && darkMode) ? 'rgba(148,163,184,0.7)' : 'rgba(156,163,175,0.9)';
- ctx.strokeStyle = hoverColor;
+ ctx.strokeStyle = 'rgba(156,163,175,0.9)';
ctx.lineWidth = 1;
ctx.setLineDash([6, 4]);
ctx.beginPath();
@@ -2236,10 +1577,9 @@ return view.extend({
return hh + ':' + mm + ':' + ss;
}
- function buildTooltipHtml(point, language) {
+ function buildTooltipHtml(point) {
if (!point) return '';
var lines = [];
- var zh = (language === 'zh-cn' || language === 'zh-tw');
var typeSel = (typeof document !== 'undefined' ? document.getElementById('history-type-select') : null);
var selType = (typeSel && typeSel.value) ? typeSel.value : 'total';
var speedUnit = uci.get('bandix', 'traffic', 'speed_unit') || 'bytes';
@@ -2257,9 +1597,9 @@ return view.extend({
}
function labelsFor(type) {
- if (type === 'lan') return { up: getTranslation('LAN 上传速率', language), down: getTranslation('LAN 下载速率', language) };
- if (type === 'wan') return { up: getTranslation('WAN 上传速率', language), down: getTranslation('WAN 下载速率', language) };
- return { up: getTranslation('总上传速率', language), down: getTranslation('总下载速率', language) };
+ if (type === 'lan') return { up: _('LAN Upload'), down: _('LAN Download') };
+ if (type === 'wan') return { up: _('WAN Upload'), down: _('WAN Download') };
+ return { up: _('Total Upload'), down: _('Total Download') };
}
function rateKeysFor(type) {
@@ -2289,7 +1629,7 @@ return view.extend({
ipv6Info = ' | IPv6: ' + lanIPv6.join(', ');
}
var devLabel = (dev.hostname || '-') + (dev.ip ? ' (' + dev.ip + ')' : '') + (dev.mac ? ' [' + dev.mac + ']' : '') + ipv6Info;
- lines.push('
' + getTranslation('设备', language) + ': ' + devLabel + '
');
+ lines.push('' + _('Device') + ': ' + devLabel + '
');
}
}
} catch (e) {}
@@ -2313,7 +1653,7 @@ return view.extend({
// 次要信息:其余类型的速率(精简展示)
var otherTypes = ['total', 'lan', 'wan'].filter(function (t) { return t !== selType; });
if (otherTypes.length) {
- lines.push('' + getTranslation('其他速率', language) + '
');
+ lines.push('' + _('Other Rates') + '
');
otherTypes.forEach(function (t) {
var lbs = labelsFor(t);
var ks = rateKeysFor(t);
@@ -2324,13 +1664,13 @@ return view.extend({
// 累计:区分LAN 流量与公网
lines.push('
');
- lines.push('' + getTranslation('累计流量', language) + '
');
- row(getTranslation('总上传', language), bytesValue('total_tx_bytes'));
- row(getTranslation('总下载', language), bytesValue('total_rx_bytes'));
- row(getTranslation('LAN 已上传', language), bytesValue('local_tx_bytes'));
- row(getTranslation('LAN 已下载', language), bytesValue('local_rx_bytes'));
- row(getTranslation('WAN 已上传', language), bytesValue('wide_tx_bytes'));
- row(getTranslation('WAN 已下载', language), bytesValue('wide_rx_bytes'));
+ lines.push('' + _('Cumulative') + '
');
+ row(_('Total Uploaded'), bytesValue('total_tx_bytes'));
+ row(_('Total Downloaded'), bytesValue('total_rx_bytes'));
+ row(_('LAN Uploaded'), bytesValue('local_tx_bytes'));
+ row(_('LAN Downloaded'), bytesValue('local_rx_bytes'));
+ row(_('WAN Uploaded'), bytesValue('wide_tx_bytes'));
+ row(_('WAN Downloaded'), bytesValue('wide_rx_bytes'));
return lines.join('');
}
@@ -2531,9 +1871,9 @@ function isDeviceOnline(device) {
}
// 格式化最后上线时间
-function formatLastOnlineTime(lastOnlineTs, language) {
+function formatLastOnlineTime(lastOnlineTs) {
if (!lastOnlineTs || lastOnlineTs <= 0) {
- return getTranslation('从未上线', language);
+ return _('Never Online');
}
// 如果时间戳小于1000000000000,说明是秒级时间戳,需要转换为毫秒
@@ -2546,12 +1886,12 @@ function formatLastOnlineTime(lastOnlineTs, language) {
// 1分钟以内显示"刚刚"
if (minutesDiff < 1) {
- return getTranslation('刚刚', language);
+ return _('Just Now');
}
// 10分钟以内显示具体的"几分钟前"
if (minutesDiff <= 10) {
- return minutesDiff + getTranslation('分钟前', language);
+ return minutesDiff + _('min ago');
}
// 转换为小时
@@ -2559,7 +1899,7 @@ function formatLastOnlineTime(lastOnlineTs, language) {
// 如果不满1小时,显示分钟
if (hoursDiff < 1) {
- return minutesDiff + getTranslation('分钟前', language);
+ return minutesDiff + _('min ago');
}
// 转换为天
@@ -2567,7 +1907,7 @@ function formatLastOnlineTime(lastOnlineTs, language) {
// 如果不满1天,显示小时(忽略分钟)
if (daysDiff < 1) {
- return hoursDiff + getTranslation('小时前', language);
+ return hoursDiff + _('h ago');
}
// 转换为月(按30天计算)
@@ -2575,7 +1915,7 @@ function formatLastOnlineTime(lastOnlineTs, language) {
// 如果不满1个月,显示天(忽略小时)
if (monthsDiff < 1) {
- return daysDiff + getTranslation('天前', language);
+ return daysDiff + _('days ago');
}
// 转换为年(按365天计算)
@@ -2583,63 +1923,39 @@ function formatLastOnlineTime(lastOnlineTs, language) {
// 如果不满1年,显示月(忽略天)
if (yearsDiff < 1) {
- return monthsDiff + getTranslation('个月前', language);
+ return monthsDiff + _('months ago');
}
// 超过1年,显示年(忽略月)
- return yearsDiff + getTranslation('年前', language);
+ return yearsDiff + _('years ago');
}
-function formatRetentionSeconds(seconds, language) {
+function formatRetentionSeconds(seconds) {
if (!seconds || seconds <= 0) return '';
var value;
var unitKey;
if (seconds < 60) {
value = Math.round(seconds);
- unitKey = '秒';
+ unitKey = _('seconds');
} else if (seconds < 3600) {
value = Math.round(seconds / 60);
if (value < 1) value = 1;
- unitKey = '分钟';
+ unitKey = _('minutes');
} else if (seconds < 86400) {
value = Math.round(seconds / 3600);
if (value < 1) value = 1;
- unitKey = '小时';
+ unitKey = _('hours');
} else if (seconds < 604800) {
value = Math.round(seconds / 86400);
if (value < 1) value = 1;
- unitKey = '天';
+ unitKey = _('days');
} else {
value = Math.round(seconds / 604800);
if (value < 1) value = 1;
- unitKey = '周';
+ unitKey = _('weeks');
}
- // 多语言格式化
- if (language === 'zh-cn' || language === 'zh-tw') {
- return getTranslation('最近', language) + value + getTranslation(unitKey, language);
- }
-
- if (language === 'ja') {
- return getTranslation('最近', language) + value + getTranslation(unitKey, language);
- }
-
- if (language === 'fr') {
- // 法语单复数:值>1 用复数,天/周/小时/分钟/秒分别加 s
- var unitFr = getTranslation(unitKey, 'fr');
- if (value > 1) unitFr = unitFr + 's';
- return getTranslation('最近', 'fr') + ' ' + value + ' ' + unitFr;
- }
-
- if (language === 'ru') {
- // 俄语用缩写,避免复杂变格
- return getTranslation('最近', 'ru') + ' ' + value + ' ' + getTranslation(unitKey, 'ru');
- }
-
- // 英语默认
- var unitEn = getTranslation(unitKey, 'en');
- if (value > 1) unitEn = unitEn + 's';
- return getTranslation('最近', 'en') + ' ' + value + ' ' + unitEn;
+ return _('Last') + ' ' + value + ' ' + unitKey;
}
function refreshHistory() {
@@ -2662,7 +1978,7 @@ function formatRetentionSeconds(seconds, language) {
var retentionBadge = document.getElementById('history-retention');
if (retentionBadge) {
- var text = formatRetentionSeconds(res && res.retention_seconds, language);
+ var text = formatRetentionSeconds(res && res.retention_seconds);
retentionBadge.textContent = text || '';
}
@@ -2729,7 +2045,88 @@ function formatRetentionSeconds(seconds, language) {
historyHoverIndex = idx;
// 立即重绘以显示垂直虚线
try { drawHistoryChart(canvas, canvas.__bandixChart && canvas.__bandixChart.originalLabels ? canvas.__bandixChart.originalLabels : labels, canvas.__bandixChart && canvas.__bandixChart.originalUpSeries ? canvas.__bandixChart.originalUpSeries : upSeries, canvas.__bandixChart && canvas.__bandixChart.originalDownSeries ? canvas.__bandixChart.originalDownSeries : downSeries, zoomScale, zoomOffsetX); } catch(e){}
- tooltip.innerHTML = buildTooltipHtml(point, language);
+ tooltip.innerHTML = buildTooltipHtml(point);
+
+ // 应用主题颜色到 tooltip,使用 cbi-section 的颜色
+ try {
+ // 优先从 cbi-section 获取颜色(历史趋势卡片就是 cbi-section)
+ var cbiSection = document.querySelector('.cbi-section');
+ var targetElement = cbiSection || document.querySelector('.main') || document.body;
+ var computedStyle = window.getComputedStyle(targetElement);
+ var bgColor = computedStyle.backgroundColor;
+ var textColor = computedStyle.color;
+
+ // 确保背景色不透明
+ if (bgColor && bgColor !== 'rgba(0, 0, 0, 0)' && bgColor !== 'transparent') {
+ // 检查是否是 rgba/rgb 格式,如果是半透明则转换为不透明
+ var rgbaMatch = bgColor.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/);
+ if (rgbaMatch) {
+ var r = parseInt(rgbaMatch[1]);
+ var g = parseInt(rgbaMatch[2]);
+ var b = parseInt(rgbaMatch[3]);
+ var alpha = rgbaMatch[4] ? parseFloat(rgbaMatch[4]) : 1;
+
+ // 如果 alpha < 0.95,使用不透明版本
+ if (alpha < 0.95) {
+ tooltip.style.backgroundColor = 'rgb(' + r + ', ' + g + ', ' + b + ')';
+ } else {
+ tooltip.style.backgroundColor = bgColor;
+ }
+ } else {
+ tooltip.style.backgroundColor = bgColor;
+ }
+ } else {
+ // 如果无法获取背景色,尝试从其他 cbi-section 获取
+ var allCbiSections = document.querySelectorAll('.cbi-section');
+ var foundBgColor = false;
+ for (var i = 0; i < allCbiSections.length; i++) {
+ var sectionStyle = window.getComputedStyle(allCbiSections[i]);
+ var sectionBg = sectionStyle.backgroundColor;
+ if (sectionBg && sectionBg !== 'rgba(0, 0, 0, 0)' && sectionBg !== 'transparent') {
+ var rgbaMatch = sectionBg.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/);
+ if (rgbaMatch) {
+ var r = parseInt(rgbaMatch[1]);
+ var g = parseInt(rgbaMatch[2]);
+ var b = parseInt(rgbaMatch[3]);
+ var alpha = rgbaMatch[4] ? parseFloat(rgbaMatch[4]) : 1;
+ if (alpha < 0.95) {
+ tooltip.style.backgroundColor = 'rgb(' + r + ', ' + g + ', ' + b + ')';
+ } else {
+ tooltip.style.backgroundColor = sectionBg;
+ }
+ } else {
+ tooltip.style.backgroundColor = sectionBg;
+ }
+ foundBgColor = true;
+ break;
+ }
+ }
+ // 如果无法获取背景色,CSS 会通过媒体查询自动处理暗色模式
+ if (!foundBgColor) {
+ // 不设置背景色,让 CSS 媒体查询处理
+ }
+ }
+
+ if (textColor && textColor !== 'rgba(0, 0, 0, 0)') {
+ tooltip.style.color = textColor;
+ } else {
+ // 如果无法获取文字颜色,从 cbi-section 获取
+ if (cbiSection) {
+ var sectionTextColor = window.getComputedStyle(cbiSection).color;
+ if (sectionTextColor && sectionTextColor !== 'rgba(0, 0, 0, 0)') {
+ tooltip.style.color = sectionTextColor;
+ }
+ // 否则使用 CSS 默认颜色(已通过媒体查询设置)
+ }
+ // 否则使用 CSS 默认颜色(已通过媒体查询设置)
+ }
+
+ // 边框和阴影由 CSS 媒体查询自动处理
+ } catch(e) {
+ // 如果出错,CSS 会通过媒体查询自动处理暗色模式
+ // 不设置样式,让 CSS 处理
+ }
+
// 先显示以计算尺寸
tooltip.style.display = 'block';
tooltip.style.left = '-9999px';
@@ -2835,7 +2232,7 @@ function formatRetentionSeconds(seconds, language) {
var ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawHistoryChart(canvas, [], [], [], 1, 0);
- // ui.addNotification(null, E('p', {}, getTranslation('无法获取历史数据', language)), 'error');
+ // ui.addNotification(null, E('p', {}, _('Unable to fetch history data')), 'error');
}).finally(function () {
isHistoryLoading = false;
});
@@ -2892,21 +2289,17 @@ function formatRetentionSeconds(seconds, language) {
var trafficDiv = document.getElementById('traffic-status');
var deviceCountDiv = document.getElementById('device-count');
var statsGrid = document.getElementById('stats-grid');
- var language = uci.get('bandix', 'general', 'language');
- if (!language || language === 'auto') {
- language = getSystemLanguage();
- }
var speedUnit = uci.get('bandix', 'traffic', 'speed_unit') || 'bytes';
var stats = result;
if (!stats || !stats.devices) {
- trafficDiv.innerHTML = '' + getTranslation('无法获取数据', language) + '
';
+ trafficDiv.innerHTML = '' + _('Unable to fetch data') + '
';
return;
}
// 更新设备计数
var onlineCount = stats.devices.filter(d => isDeviceOnline(d)).length;
- deviceCountDiv.textContent = getTranslation('在线设备', language) + ': ' + onlineCount + ' / ' + stats.devices.length;
+ deviceCountDiv.textContent = _('Online Devices') + ': ' + onlineCount + ' / ' + stats.devices.length;
// 计算统计数据(包含所有设备)
var totalLanUp = stats.devices.reduce((sum, d) => sum + (d.local_tx_bytes || 0), 0);
@@ -2926,11 +2319,9 @@ function formatRetentionSeconds(seconds, language) {
statsGrid.innerHTML = '';
// LAN 流量卡片
- statsGrid.appendChild(E('div', { 'class': 'stats-card' }, [
- E('div', { 'class': 'stats-card-header' }, [
- E('div', { 'class': 'stats-card-title' }, getTranslation('LAN 流量', language))
- ]),
- E('div', { 'style': 'margin-top: 12px; display: flex; flex-direction: column; gap: 8px;' }, [
+ statsGrid.appendChild(E('div', { 'class': 'cbi-section' }, [
+ E('div', { 'class': 'stats-card-title' }, _('LAN Traffic')),
+ E('div', { 'style': 'display: flex; flex-direction: column; gap: 8px;' }, [
// 上传行
E('div', { 'style': 'display: flex; align-items: center; gap: 4px;' }, [
E('span', { 'style': 'color: #f97316; font-size: 0.75rem; font-weight: bold;' }, '↑'),
@@ -2947,11 +2338,9 @@ function formatRetentionSeconds(seconds, language) {
]));
// WAN 流量卡片
- statsGrid.appendChild(E('div', { 'class': 'stats-card' }, [
- E('div', { 'class': 'stats-card-header' }, [
- E('div', { 'class': 'stats-card-title' }, getTranslation('WAN 流量', language))
- ]),
- E('div', { 'style': 'margin-top: 12px; display: flex; flex-direction: column; gap: 8px;' }, [
+ statsGrid.appendChild(E('div', { 'class': 'cbi-section' }, [
+ E('div', { 'class': 'stats-card-title' }, _('WAN Traffic')),
+ E('div', { 'style': 'display: flex; flex-direction: column; gap: 8px;' }, [
// 上传行
E('div', { 'style': 'display: flex; align-items: center; gap: 4px;' }, [
E('span', { 'style': 'color: #f97316; font-size: 0.75rem; font-weight: bold;' }, '↑'),
@@ -2968,11 +2357,9 @@ function formatRetentionSeconds(seconds, language) {
]));
// 总流量卡片
- statsGrid.appendChild(E('div', { 'class': 'stats-card' }, [
- E('div', { 'class': 'stats-card-header' }, [
- E('div', { 'class': 'stats-card-title' }, getTranslation('总流量', language))
- ]),
- E('div', { 'style': 'margin-top: 12px; display: flex; flex-direction: column; gap: 8px;' }, [
+ statsGrid.appendChild(E('div', { 'class': 'cbi-section' }, [
+ E('div', { 'class': 'stats-card-title' }, _('Total')),
+ E('div', { 'style': 'display: flex; flex-direction: column; gap: 8px;' }, [
// 上传行
E('div', { 'style': 'display: flex; align-items: center; gap: 4px;' }, [
E('span', { 'style': 'color: #f97316; font-size: 0.75rem; font-weight: bold;' }, '↑'),
@@ -3037,7 +2424,7 @@ function formatRetentionSeconds(seconds, language) {
var speedBtn = E('div', {
'class': 'th-split-section' + (currentSortBy === speedKey ? ' active' : ''),
'data-sort': speedKey,
- 'title': getTranslation('按速度排序', language)
+ 'title': _('Sort by Speed')
}, [
E('span', { 'class': 'th-split-icon' }, '⚡'),
E('span', { 'style': 'font-size: 0.75rem;' }, currentSortBy === speedKey ? (currentSortOrder ? '↑' : '↓') : '')
@@ -3050,7 +2437,7 @@ function formatRetentionSeconds(seconds, language) {
var trafficBtn = E('div', {
'class': 'th-split-section' + (currentSortBy === trafficKey ? ' active' : ''),
'data-sort': trafficKey,
- 'title': getTranslation('按用量排序', language)
+ 'title': _('Sort by Traffic')
}, [
E('span', { 'class': 'th-split-icon' }, '∑'),
E('span', { 'style': 'font-size: 0.75rem;' }, currentSortBy === trafficKey ? (currentSortOrder ? '↑' : '↓') : '')
@@ -3103,11 +2490,11 @@ function formatRetentionSeconds(seconds, language) {
var table = E('table', { 'class': 'bandix-table' }, [
E('thead', {}, [
E('tr', {}, [
- createSortableHeader(getTranslation('设备信息', language), 'hostname'),
- createSplitHeader(getTranslation('LAN 流量', language), 'lan_speed', 'lan_traffic'),
- createSplitHeader(getTranslation('WAN 流量', language), 'wan_speed', 'wan_traffic'),
- E('th', {}, getTranslation('限速设置', language)),
- E('th', {}, getTranslation('操作', language))
+ createSortableHeader(_('Device Info'), 'hostname'),
+ createSplitHeader(_('LAN Traffic'), 'lan_speed', 'lan_traffic'),
+ createSplitHeader(_('WAN Traffic'), 'wan_speed', 'wan_traffic'),
+ E('th', {}, _('Rate Limit')),
+ E('th', {}, _('Actions'))
])
]),
E('tbody', {})
@@ -3132,10 +2519,14 @@ function formatRetentionSeconds(seconds, language) {
filteredDevices.forEach(function (device) {
var isOnline = isDeviceOnline(device);
+ // 根据主题类型决定按钮显示内容
+ var themeType = getThemeType();
+ var buttonText = themeType === 'narrow' ? '⚙' : _('Settings');
+
var actionButton = E('button', {
- 'class': 'action-button',
- 'title': getTranslation('设置', language)
- }, getTranslation('设置', language));
+ 'class': 'cbi-button cbi-button-action',
+ 'title': _('Settings')
+ }, buttonText);
// 绑定点击事件
actionButton.addEventListener('click', function () {
@@ -3177,8 +2568,8 @@ function formatRetentionSeconds(seconds, language) {
deviceInfoElements.push(
E('div', { 'class': 'device-mac' }, device.mac),
E('div', { 'class': 'device-last-online' }, [
- E('span', { 'style': 'color: #6b7280; font-size: 0.75rem;' }, getTranslation('最后上线', language) + ': '),
- E('span', { 'style': 'color: #9ca3af; font-size: 0.75rem;' }, formatLastOnlineTime(device.last_online_ts, language))
+ E('span', { 'style': 'color: #6b7280; font-size: 0.75rem;' }, _('Last Online') + ': '),
+ E('span', { 'style': 'color: #9ca3af; font-size: 0.75rem;' }, formatLastOnlineTime(device.last_online_ts))
])
);
}
@@ -3267,6 +2658,111 @@ function formatRetentionSeconds(seconds, language) {
// 立即执行一次,不等待轮询
updateDeviceData();
+ // 自动适应主题背景色和文字颜色的函数(仅应用于弹窗和 tooltip)
+ function applyThemeColors() {
+ try {
+ // 优先从 cbi-section 获取颜色
+ var cbiSection = document.querySelector('.cbi-section');
+ var targetElement = cbiSection || document.querySelector('.main') || document.body;
+ var computedStyle = window.getComputedStyle(targetElement);
+ var bgColor = computedStyle.backgroundColor;
+ var textColor = computedStyle.color;
+
+ // 如果无法获取背景色,尝试从其他 cbi-section 获取
+ if (!bgColor || bgColor === 'rgba(0, 0, 0, 0)' || bgColor === 'transparent') {
+ var allCbiSections = document.querySelectorAll('.cbi-section');
+ for (var i = 0; i < allCbiSections.length; i++) {
+ var sectionStyle = window.getComputedStyle(allCbiSections[i]);
+ var sectionBg = sectionStyle.backgroundColor;
+ if (sectionBg && sectionBg !== 'rgba(0, 0, 0, 0)' && sectionBg !== 'transparent') {
+ bgColor = sectionBg;
+ textColor = sectionStyle.color;
+ break;
+ }
+ }
+ }
+
+ // 只应用到模态框和 tooltip,不修改页面其他元素
+ if (bgColor && bgColor !== 'rgba(0, 0, 0, 0)' && bgColor !== 'transparent') {
+ // 应用到模态框(确保不透明)
+ var modal = document.querySelector('.modal');
+ if (modal) {
+ var rgbaMatch = bgColor.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/);
+ if (rgbaMatch) {
+ var r = parseInt(rgbaMatch[1]);
+ var g = parseInt(rgbaMatch[2]);
+ var b = parseInt(rgbaMatch[3]);
+ var alpha = rgbaMatch[4] ? parseFloat(rgbaMatch[4]) : 1;
+ if (alpha < 0.95) {
+ modal.style.backgroundColor = 'rgb(' + r + ', ' + g + ', ' + b + ')';
+ } else {
+ modal.style.backgroundColor = bgColor;
+ }
+ } else {
+ modal.style.backgroundColor = bgColor;
+ }
+ }
+
+ // 应用到 tooltip(包括所有 tooltip 实例)
+ var tooltips = document.querySelectorAll('.history-tooltip');
+ tooltips.forEach(function(tooltip) {
+ var rgbaMatch = bgColor.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/);
+ if (rgbaMatch) {
+ var r = parseInt(rgbaMatch[1]);
+ var g = parseInt(rgbaMatch[2]);
+ var b = parseInt(rgbaMatch[3]);
+ var alpha = rgbaMatch[4] ? parseFloat(rgbaMatch[4]) : 1;
+ if (alpha < 0.95) {
+ tooltip.style.backgroundColor = 'rgb(' + r + ', ' + g + ', ' + b + ')';
+ } else {
+ tooltip.style.backgroundColor = bgColor;
+ }
+ } else {
+ tooltip.style.backgroundColor = bgColor;
+ }
+ });
+ }
+
+ // 检测文字颜色并应用(仅应用到模态框和 tooltip)
+ if (textColor && textColor !== 'rgba(0, 0, 0, 0)') {
+ // 应用到模态框的文字颜色
+ var modal = document.querySelector('.modal');
+ if (modal) {
+ modal.style.color = textColor;
+ }
+
+ // 应用到 tooltip 的文字颜色
+ var tooltips = document.querySelectorAll('.history-tooltip');
+ tooltips.forEach(function(tooltip) {
+ tooltip.style.color = textColor;
+ });
+ }
+ } catch (e) {
+ // 如果检测失败,使用默认值
+ console.log('Theme adaptation:', e);
+ }
+ }
+
+ // 初始应用主题颜色
+ setTimeout(applyThemeColors, 100);
+
+ // 监听 DOM 变化,自动应用到新创建的元素
+ if (typeof MutationObserver !== 'undefined') {
+ var observer = new MutationObserver(function(mutations) {
+ applyThemeColors();
+ });
+
+ setTimeout(function() {
+ var container = document.querySelector('.bandix-container');
+ if (container) {
+ observer.observe(container, {
+ childList: true,
+ subtree: true
+ });
+ }
+ }, 200);
+ }
+
return view;
}
});
diff --git a/luci-app-bandix/htdocs/luci-static/resources/view/bandix/settings.js b/luci-app-bandix/htdocs/luci-static/resources/view/bandix/settings.js
index fbc24da..54f1258 100644
--- a/luci-app-bandix/htdocs/luci-static/resources/view/bandix/settings.js
+++ b/luci-app-bandix/htdocs/luci-static/resources/view/bandix/settings.js
@@ -5,432 +5,8 @@
'require uci';
'require fs';
-const translations = {
- 'zh-cn': {
- '基本设置': '基本设置',
- '流量监控设置': '流量监控设置',
- '连接监控设置': '连接监控设置',
- 'DNS监控设置': 'DNS监控设置',
- 'Bandix 基本配置': 'Bandix 基本配置',
- '配置 Bandix 服务的基本参数': '配置 Bandix 服务的基本参数',
- 'Bandix 流量监控配置': 'Bandix 流量监控配置',
- '配置流量监控相关参数': '配置流量监控相关参数',
- 'Bandix 连接监控配置': 'Bandix 连接监控配置',
- '配置连接监控相关参数': '配置连接监控相关参数',
- 'Bandix DNS监控配置': 'Bandix DNS监控配置',
- '配置DNS监控相关参数': '配置DNS监控相关参数',
- '启用': '启用',
- '启用 Bandix 流量监控服务': '启用 Bandix 流量监控服务',
- '启用流量监控': '启用流量监控',
- '启用连接监控': '启用连接监控',
- '界面语言': '界面语言',
- '选择 Bandix 流量监控的显示语言': '选择 Bandix 流量监控的显示语言',
- '简体中文': '简体中文',
- '繁体中文': '繁体中文',
- '端口': '端口',
- 'Bandix 服务监听的端口': 'Bandix 服务监听的端口',
- '监控网卡': '监控网卡',
- '选择要监控的网络接口': '选择要监控的LAN网络接口',
- '网速单位': '网速单位',
- '选择网速显示的单位格式': '选择网速显示的单位格式',
- '字节单位 (B/s, KB/s, MB/s)': '字节单位 (B/s, KB/s, MB/s)',
- '比特单位 (bps, Kbps, Mbps)': '比特单位 (bps, Kbps, Mbps)',
- '界面主题': '界面主题',
- '选择 Bandix 流量监控的显示主题': '选择 Bandix 流量监控的显示主题',
- '跟随系统': '跟随系统',
- '明亮模式': '明亮模式',
- '暗黑模式': '暗黑模式',
- '意见反馈': '意见反馈',
- '日志级别': '日志级别',
- '设置 Bandix 服务的日志级别': '设置 Bandix 服务的日志级别',
- '离线超时时间': '离线超时时间',
- '设置设备离线判断的超时时间(秒)': '设置设备离线判断的超时时间(秒)。超过此时间未活动的设备将被标记为离线',
- '历史流量周期': '历史流量周期',
- '设置历史流量数据周期': '10分钟下,1个设备固定占用约60KB,修改会清空已有数据。\n由于ubus单次通信限制,无法设置更大的值。',
- '数据 flush 间隔': '数据 flush 间隔',
- '设置数据 flush 间隔': '设置数据写入磁盘的时间间隔',
- '1 分钟': '1 分钟',
- '5 分钟': '5 分钟',
- '10 分钟': '10 分钟',
- '15 分钟': '15 分钟',
- '20 分钟': '20 分钟',
- '25 分钟': '25 分钟',
- '30 分钟': '30 分钟',
- '1 小时': '1 小时',
- '2 小时': '2 小时',
- '数据目录': '数据目录',
- 'Bandix 数据存储目录': 'Bandix 数据存储目录',
- '启用 Bandix 连接监控功能': '启用 Bandix 连接监控功能',
- '启用 Bandix DNS监控功能': '启用 Bandix DNS监控功能',
- 'DNS最大记录数': 'DNS最大记录数',
- '设置DNS查询记录的最大保存数量': '设置DNS查询记录的最大保存数量,超过此数量将删除最旧的记录',
- '持久化历史数据': '持久化历史数据',
- '启用数据持久化功能': '启用数据持久化功能,只有启用此选项后才会持久化到磁盘'
- },
- 'zh-tw': {
- '基本设置': '基本設置',
- '流量监控设置': '流量監控設置',
- '连接监控设置': '連接監控設置',
- 'DNS监控设置': 'DNS監控設置',
- 'Bandix 基本配置': 'Bandix 基本配置',
- '配置 Bandix 服务的基本参数': '配置 Bandix 服務的基本參數',
- 'Bandix 流量监控配置': 'Bandix 流量監控配置',
- '配置流量监控相关参数': '配置流量監控相關參數',
- 'Bandix 连接监控配置': 'Bandix 連接監控配置',
- '配置连接监控相关参数': '配置連接監控相關參數',
- 'Bandix DNS监控配置': 'Bandix DNS監控配置',
- '配置DNS监控相关参数': '配置DNS監控相關參數',
- '启用': '啟用',
- '启用 Bandix 流量监控服务': '啟用 Bandix 流量監控服務',
- '启用流量监控': '啟用流量監控',
- '启用连接监控': '啟用連接監控',
- '界面语言': '界面語言',
- '选择 Bandix 流量监控的显示语言': '選擇 Bandix 流量監控的顯示語言',
- '简体中文': '簡體中文',
- '繁体中文': '繁體中文',
- '端口': '端口',
- 'Bandix 服务监听的端口': 'Bandix 服務監聽的端口',
- '监控网卡': '監控網卡',
- '选择要监控的网络接口': '選擇要監控的LAN網絡接口',
- '网速单位': '網速單位',
- '选择网速显示的单位格式': '選擇網速顯示的單位格式',
- '字节单位 (B/s, KB/s, MB/s)': '字節單位 (B/s, KB/s, MB/s)',
- '比特单位 (bps, Kbps, Mbps)': '比特單位 (bps, Kbps, Mbps)',
- '界面主题': '介面主題',
- '选择 Bandix 流量监控的显示主题': '選擇 Bandix 流量監控的顯示主題',
- '跟随系统': '跟隨系統',
- '明亮模式': '明亮模式',
- '暗黑模式': '暗黑模式',
- '意见反馈': '意見反饋',
- '日志级别': '日誌級別',
- '设置 Bandix 服务的日志级别': '設置 Bandix 服務的日誌級別',
- '离线超时时间': '離線超時時間',
- '设置设备离线判断的超时时间(秒)': '設定設備離線判斷的超時時間(秒)。超過此時間未活動的設備將被標記為離線',
- '历史流量周期': '歷史流量週期',
- '设置历史流量数据周期': '10分鐘下,1個設備固定占用約60KB,修改會清空已有資料。\n由於ubus單次通信限制,無法設置更大的值。',
- '数据 flush 间隔': '資料flush間隔',
- '设置数据 flush 间隔': '設定資料寫入磁碟的時間間隔',
- '1 分钟': '1 分鐘',
- '5 分钟': '5 分鐘',
- '10 分钟': '10 分鐘',
- '15 分钟': '15 分鐘',
- '20 分钟': '20 分鐘',
- '25 分钟': '25 分鐘',
- '30 分钟': '30 分鐘',
- '1 小时': '1 小時',
- '2 小时': '2 小時',
- '数据目录': '數據目錄',
- 'Bandix 数据存储目录': 'Bandix 數據存儲目錄',
- '启用 Bandix 连接监控功能': '啟用 Bandix 連接監控功能',
- '启用 Bandix DNS监控功能': '啟用 Bandix DNS監控功能',
- 'DNS最大记录数': 'DNS最大記錄數',
- '设置DNS查询记录的最大保存数量': '設定DNS查詢記錄的最大保存數量,超過此數量將刪除最舊的記錄',
- '持久化历史数据': '持久化歷史數據',
- '启用数据持久化功能': '啟用數據持久化功能,只有啟用此選項後才會持久化到磁碟'
- },
- 'en': {
- '基本设置': 'Basic Settings',
- '流量监控设置': 'Traffic Monitor Settings',
- '连接监控设置': 'Connection Monitor Settings',
- 'DNS监控设置': 'DNS Monitor Settings',
- 'Bandix 基本配置': 'Bandix Basic Configuration',
- '配置 Bandix 服务的基本参数': 'Configure basic parameters for Bandix service',
- 'Bandix 流量监控配置': 'Bandix Traffic Monitor Configuration',
- '配置流量监控相关参数': 'Configure traffic monitoring related parameters',
- 'Bandix 连接监控配置': 'Bandix Connection Monitor Configuration',
- '配置连接监控相关参数': 'Configure connection monitoring related parameters',
- 'Bandix DNS监控配置': 'Bandix DNS Monitor Configuration',
- '配置DNS监控相关参数': 'Configure DNS monitoring related parameters',
- '启用': 'Enable',
- '启用 Bandix 流量监控服务': 'Enable Bandix Traffic Monitor Service',
- '启用流量监控': 'Enable Traffic Monitoring',
- '启用连接监控': 'Enable Connection Monitoring',
- '启用DNS监控': 'Enable DNS Monitoring',
- '界面语言': 'Interface Language',
- '选择 Bandix 流量监控的显示语言': 'Select the display language for Bandix Traffic Monitor',
- '简体中文': 'Simplified Chinese',
- '繁体中文': 'Traditional Chinese',
- '端口': 'Port',
- 'Bandix 服务监听的端口': 'Port for Bandix service to listen on',
- '监控网卡': 'Monitor Interface',
- '选择要监控的网络接口': 'Select the LAN network interface to monitor',
- '网速单位': 'Speed Units',
- '选择网速显示的单位格式': 'Select the speed display unit format',
- '字节单位 (B/s, KB/s, MB/s)': 'Bytes Units (B/s, KB/s, MB/s)',
- '比特单位 (bps, Kbps, Mbps)': 'Bits Units (bps, Kbps, Mbps)',
- '界面主题': 'Interface Theme',
- '选择 Bandix 流量监控的显示主题': 'Select the display theme for Bandix Traffic Monitor',
- '跟随系统': 'Follow System',
- '明亮模式': 'Light Mode',
- '暗黑模式': 'Dark Mode',
- '意见反馈': 'Feedback',
- '日志级别': 'Log Level',
- '设置 Bandix 服务的日志级别': 'Set the log level for Bandix service',
- '离线超时时间': 'Offline Timeout',
- '设置设备离线判断的超时时间(秒)': 'Set the timeout for device offline detection (seconds). Devices inactive for longer than this time will be marked as offline',
- '历史流量周期': 'Traffic History Period',
- '设置历史流量数据周期': 'With 10-minute interval, 1 device uses a fixed size of about 60 KB, changing will clear existing data.\nDue to ubus single communication limit, larger values cannot be set.',
- '数据 flush 间隔': 'Data Flush Interval',
- '设置数据 flush 间隔': 'Set the interval for flushing data to disk',
- '1 分钟': '1 minute',
- '5 分钟': '5 minutes',
- '10 分钟': '10 minutes',
- '15 分钟': '15 minutes',
- '20 分钟': '20 minutes',
- '25 分钟': '25 minutes',
- '30 分钟': '30 minutes',
- '1 小时': '1 hour',
- '2 小时': '2 hours',
- '数据目录': 'Data Directory',
- 'Bandix 数据存储目录': 'Bandix data storage directory',
- '启用 Bandix 连接监控功能': 'Enable Bandix connection monitoring',
- '启用 Bandix DNS监控功能': 'Enable Bandix DNS monitoring',
- 'DNS最大记录数': 'DNS Max Records',
- '设置DNS查询记录的最大保存数量': 'Set the maximum number of DNS query records to keep. Older records will be deleted when this limit is exceeded',
- '持久化历史数据': 'Persist History Data',
- '启用数据持久化功能': 'Enable data persistence functionality, data will only be persisted to disk when this option is enabled'
- },
- 'fr': {
- '基本设置': 'Paramètres de Base',
- '流量监控设置': 'Paramètres de Surveillance du Trafic',
- '连接监控设置': 'Paramètres de Surveillance des Connexions',
- 'DNS监控设置': 'Paramètres de Surveillance DNS',
- 'Bandix 基本配置': 'Configuration de Base Bandix',
- '配置 Bandix 服务的基本参数': 'Configurer les paramètres de base du service Bandix',
- 'Bandix 流量监控配置': 'Configuration de Surveillance du Trafic Bandix',
- '配置流量监控相关参数': 'Configurer les paramètres liés à la surveillance du trafic',
- 'Bandix 连接监控配置': 'Configuration de Surveillance des Connexions Bandix',
- '配置连接监控相关参数': 'Configurer les paramètres liés à la surveillance des connexions',
- 'Bandix DNS监控配置': 'Configuration de Surveillance DNS Bandix',
- '配置DNS监控相关参数': 'Configurer les paramètres liés à la surveillance DNS',
- '启用': 'Activer',
- '启用 Bandix 流量监控服务': 'Activer le Service de Surveillance du Trafic Bandix',
- '启用流量监控': 'Activer la Surveillance du Trafic',
- '启用连接监控': 'Activer la Surveillance des Connexions',
- '启用DNS监控': 'Activer la Surveillance DNS',
- '界面语言': 'Langue de l\'Interface',
- '选择 Bandix 流量监控的显示语言': 'Sélectionner la langue d\'affichage pour le Moniteur de Trafic Bandix',
- '简体中文': 'Chinois Simplifié',
- '繁体中文': 'Chinois Traditionnel',
- '端口': 'Port',
- 'Bandix 服务监听的端口': 'Port d\'écoute du service Bandix',
- '监控网卡': 'Interface de Surveillance',
- '选择要监控的网络接口': 'Sélectionner l\'interface réseau LAN à surveiller',
- '网速单位': 'Unités de Vitesse',
- '选择网速显示的单位格式': 'Sélectionner le format d\'unité d\'affichage de la vitesse',
- '字节单位 (B/s, KB/s, MB/s)': 'Unités d\'Octets (B/s, KB/s, MB/s)',
- '比特单位 (bps, Kbps, Mbps)': 'Unités de Bits (bps, Kbps, Mbps)',
- '界面主题': 'Thème de l\'Interface',
- '选择 Bandix 流量监控的显示主题': 'Sélectionner le thème d\'affichage pour le Moniteur de Trafic Bandix',
- '跟随系统': 'Suivre le Système',
- '明亮模式': 'Mode Clair',
- '暗黑模式': 'Mode Sombre',
- '意见反馈': 'Commentaires',
- '日志级别': 'Niveau de Journal',
- '设置 Bandix 服务的日志级别': 'Définir le niveau de journal pour le service Bandix',
- '离线超时时间': 'Délai d\'expiration hors ligne',
- '设置设备离线判断的超时时间(秒)': 'Définir le délai d\'expiration pour la détection hors ligne des appareils (secondes). Les appareils inactifs plus longtemps que cette durée seront marqués comme hors ligne',
- '历史流量周期': 'Période d\'Historique du Trafic',
- '设置历史流量数据周期': "Avec un intervalle de 10 minutes, 1 appareil occupe environ 60 Ko (taille fixe), la modification effacera les données existantes.\nEn raison de la limite de communication unique d'ubus, des valeurs plus importantes ne peuvent pas être définies.",
- '数据 flush 间隔': 'Intervalle de flush',
- '设置数据 flush 间隔': 'Définir l\'intervalle pour effectuer le flush des données sur le disque',
- '1 分钟': '1 minute',
- '5 分钟': '5 minutes',
- '10 分钟': '10 minutes',
- '15 分钟': '15 minutes',
- '20 分钟': '20 minutes',
- '25 分钟': '25 minutes',
- '30 分钟': '30 minutes',
- '1 小时': '1 heure',
- '2 小时': '2 heures',
- '数据目录': 'Répertoire de Données',
- 'Bandix 数据存储目录': 'Répertoire de stockage de données Bandix',
- '启用 Bandix 连接监控功能': 'Activer la surveillance des connexions Bandix',
- '启用 Bandix DNS监控功能': 'Activer la surveillance DNS Bandix',
- 'DNS最大记录数': 'Nombre Maximum d\'Enregistrements DNS',
- '设置DNS查询记录的最大保存数量': 'Définir le nombre maximum d\'enregistrements de requêtes DNS à conserver. Les enregistrements plus anciens seront supprimés lorsque cette limite est dépassée',
- '持久化历史数据': 'Persister les Données Historiques',
- '启用数据持久化功能': 'Activer la fonctionnalité de persistance des données, les données ne seront persistées sur le disque que lorsque cette option est activée'
- },
- 'ja': {
- '基本设置': '基本設定',
- '流量监控设置': 'トラフィック監視設定',
- '连接监控设置': '接続監視設定',
- 'DNS监控设置': 'DNS監視設定',
- 'Bandix 基本配置': 'Bandix 基本設定',
- '配置 Bandix 服务的基本参数': 'Bandix サービスの基本パラメータを設定',
- 'Bandix 流量监控配置': 'Bandix トラフィック監視設定',
- '配置流量监控相关参数': 'トラフィック監視関連パラメータを設定',
- 'Bandix 连接监控配置': 'Bandix 接続監視設定',
- '配置连接监控相关参数': '接続監視関連パラメータを設定',
- 'Bandix DNS监控配置': 'Bandix DNS監視設定',
- '配置DNS监控相关参数': 'DNS監視関連パラメータを設定',
- '启用': '有効',
- '启用 Bandix 流量监控服务': 'Bandix トラフィックモニターサービスを有効にする',
- '启用流量监控': 'トラフィック監視を有効にする',
- '启用连接监控': '接続監視を有効にする',
- '启用DNS监控': 'DNS監視を有効にする',
- '界面语言': 'インターフェース言語',
- '选择 Bandix 流量监控的显示语言': 'Bandix トラフィックモニターの表示言語を選択',
- '简体中文': '簡体字中国語',
- '繁体中文': '繁体字中国語',
- '端口': 'ポート',
- 'Bandix 服务监听的端口': 'Bandix サービスのリッスンポート',
- '监控网卡': '監視インターフェース',
- '选择要监控的网络接口': '監視するLANネットワークインターフェースを選択',
- '网速单位': '速度単位',
- '选择网速显示的单位格式': '速度表示の単位形式を選択',
- '字节单位 (B/s, KB/s, MB/s)': 'バイト単位 (B/s, KB/s, MB/s)',
- '比特单位 (bps, Kbps, Mbps)': 'ビット単位 (bps, Kbps, Mbps)',
- '界面主题': 'インターフェーステーマ',
- '选择 Bandix 流量监控的显示主题': 'Bandix トラフィックモニターの表示テーマを選択',
- '跟随系统': 'システムに従う',
- '明亮模式': 'ライトモード',
- '暗黑模式': 'ダークモード',
- '意见反馈': 'フィードバック',
- '日志级别': 'ログレベル',
- '设置 Bandix 服务的日志级别': 'Bandix サービスのログレベルを設定',
- '离线超时时间': 'オフラインタイムアウト',
- '设置设备离线判断的超时时间(秒)': 'デバイスのオフライン検出のタイムアウト時間(秒)を設定。この時間を超えて非アクティブなデバイスはオフラインとしてマークされます',
- '历史流量周期': 'トラフィック履歴期間',
- '设置历史流量数据周期': '10分間隔では、1台のデバイスで固定サイズとして約60KBを使用し、変更すると既存データがクリアされます。\nubusの単一通信制限により、より大きな値を設定することはできません。',
- '数据 flush 间隔': 'データflush間隔',
- '设置数据 flush 间隔': 'データをディスクにflushする間隔を設定',
- '1 分钟': '1 分',
- '5 分钟': '5 分',
- '10 分钟': '10 分',
- '15 分钟': '15 分',
- '20 分钟': '20 分',
- '25 分钟': '25 分',
- '30 分钟': '30 分',
- '1 小时': '1 時間',
- '2 小时': '2 時間',
- '数据目录': 'データディレクトリ',
- 'Bandix 数据存储目录': 'Bandix データ保存ディレクトリ',
- '启用 Bandix 连接监控功能': 'Bandix 接続監視機能を有効にする',
- '启用 Bandix DNS监控功能': 'Bandix DNS監視機能を有効にする',
- 'DNS最大记录数': 'DNS最大記録数',
- '设置DNS查询记录的最大保存数量': 'DNSクエリ記録の最大保存数を設定。この数を超えると、最も古い記録が削除されます',
- '持久化历史数据': '履歴データの永続化',
- '启用数据持久化功能': 'データ永続化機能を有効にする。このオプションが有効な場合のみ、データがディスクに永続化されます'
- },
- 'ru': {
- '基本设置': 'Основные Настройки',
- '流量监控设置': 'Настройки Мониторинга Трафика',
- '连接监控设置': 'Настройки Мониторинга Соединений',
- 'DNS监控设置': 'Настройки Мониторинга DNS',
- 'Bandix 基本配置': 'Базовая Конфигурация Bandix',
- '配置 Bandix 服务的基本参数': 'Настроить основные параметры службы Bandix',
- 'Bandix 流量监控配置': 'Конфигурация Мониторинга Трафика Bandix',
- '配置流量监控相关参数': 'Настроить параметры, связанные с мониторингом трафика',
- 'Bandix 连接监控配置': 'Конфигурация Мониторинга Соединений Bandix',
- '配置连接监控相关参数': 'Настроить параметры, связанные с мониторингом соединений',
- 'Bandix DNS监控配置': 'Конфигурация Мониторинга DNS Bandix',
- '配置DNS监控相关参数': 'Настроить параметры, связанные с мониторингом DNS',
- '启用': 'Включить',
- '启用 Bandix 流量监控服务': 'Включить Службу Мониторинга Трафика Bandix',
- '启用流量监控': 'Включить Мониторинг Трафика',
- '启用连接监控': 'Включить Мониторинг Соединений',
- '启用DNS监控': 'Включить Мониторинг DNS',
- '界面语言': 'Язык Интерфейса',
- '选择 Bandix 流量监控的显示语言': 'Выберите язык отображения для Монитора Трафика Bandix',
- '简体中文': 'Упрощенный Китайский',
- '繁体中文': 'Традиционный Китайский',
- '端口': 'Порт',
- 'Bandix 服务监听的端口': 'Порт прослушивания службы Bandix',
- '监控网卡': 'Интерфейс Мониторинга',
- '选择要监控的网络接口': 'Выберите сетевой интерфейс LAN для мониторинга',
- '网速单位': 'Единицы Скорости',
- '选择网速显示的单位格式': 'Выберите формат единиц отображения скорости',
- '字节单位 (B/s, KB/s, MB/s)': 'Единицы Байтов (B/s, KB/s, MB/s)',
- '比特单位 (bps, Kbps, Mbps)': 'Единицы Битов (bps, Kbps, Mbps)',
- '界面主题': 'Тема Интерфейса',
- '选择 Bandix 流量监控的显示主题': 'Выберите тему отображения для Монитора Трафика Bandix',
- '跟随系统': 'Следовать Системе',
- '明亮模式': 'Светлый Режим',
- '暗黑模式': 'Темный Режим',
- '意见反馈': 'Обратная связь',
- '日志级别': 'Уровень Журналирования',
- '设置 Bandix 服务的日志级别': 'Установить уровень журналирования для службы Bandix',
- '离线超时时间': 'Таймаут отключения',
- '设置设备离线判断的超时时间(秒)': 'Установить таймаут для обнаружения отключения устройств (секунды). Устройства, неактивные дольше этого времени, будут помечены как отключенные',
- '历史流量周期': 'Период Истории Трафика',
- '设置历史流量数据周期': 'При интервале 10 минут одно устройство занимает около 60 КБ (фиксированный размер), изменение очистит существующие данные.\nИз-за ограничения единичной связи ubus нельзя установить большие значения.',
- '数据 flush 间隔': 'Интервал flush данных',
- '设置数据 flush 间隔': 'Установить интервал flush данных на диск',
- '1 分钟': '1 минута',
- '5 分钟': '5 минут',
- '10 分钟': '10 минут',
- '15 分钟': '15 минут',
- '20 分钟': '20 минут',
- '25 分钟': '25 минут',
- '30 分钟': '30 минут',
- '1 小时': '1 час',
- '2 小时': '2 часа',
- '数据目录': 'Каталог Данных',
- 'Bandix 数据存储目录': 'Каталог хранения данных Bandix',
- '启用 Bandix 连接监控功能': 'Включить мониторинг соединений Bandix',
- '启用 Bandix DNS监控功能': 'Включить мониторинг DNS Bandix',
- 'DNS最大记录数': 'Максимальное Количество Записей DNS',
- '设置DNS查询记录的最大保存数量': 'Установить максимальное количество записей DNS-запросов для хранения. Более старые записи будут удалены при превышении этого лимита',
- '持久化历史数据': 'Персистентность Исторических Данных',
- '启用数据持久化功能': 'Включить функциональность персистентности данных, данные будут сохраняться на диск только при включении этой опции'
- }
-};
-function getTranslation(key, language) {
- return translations[language]?.[key] || key;
-}
-
-// 获取系统语言并返回支持的语言代码
-function getSystemLanguage() {
- // 尝试获取 LuCI 的语言设置
- var luciLang = uci.get('luci', 'main', 'lang');
-
- if (luciLang && translations[luciLang]) {
- return luciLang;
- }
-
- // 如果没有 LuCI 语言设置,尝试获取浏览器语言作为回退
- var systemLang = document.documentElement.lang || 'en';
-
- // 检查是否支持该语言
- if (translations[systemLang]) {
- return systemLang;
- }
-
- // 如果不支持,返回英语
- return 'en';
-}
-
-function isDarkMode() {
- // 首先检查用户设置的主题
- var userTheme = uci.get('bandix', 'general', 'theme');
- if (userTheme) {
- if (userTheme === 'dark') {
- return true;
- } else if (userTheme === 'light') {
- return false;
- }
- // 如果是 'auto',继续检查系统主题
- }
-
- // 获取 LuCI 主题设置
- var mediaUrlBase = uci.get('luci', 'main', 'mediaurlbase');
- if (mediaUrlBase && mediaUrlBase.toLowerCase().includes('dark')) {
- return true;
- }
-
- // 如果是 argon 主题,检查 argon 配置
- if (mediaUrlBase && mediaUrlBase.toLowerCase().includes('argon')) {
- var argonMode = uci.get('argon', '@global[0]', 'mode');
- if (argonMode && argonMode.toLowerCase().includes('dark')) {
- return true;
- }
- }
-
- return false;
-}
+// 暗色模式检测已改为使用 CSS 媒体查询 @media (prefers-color-scheme: dark)
return view.extend({
load: function () {
@@ -438,7 +14,7 @@ return view.extend({
uci.load('bandix'),
uci.load('network'),
uci.load('luci'),
- uci.load('argon').catch(function() {
+ uci.load('argon').catch(function () {
// argon 配置可能不存在,忽略错误
return null;
})
@@ -449,7 +25,7 @@ return view.extend({
var m, s, o;
var networkConfig = uci.sections('network', 'device');
var physicalInterfaces = [];
-
+
// 确保UCI section存在,否则表单不会显示
if (!uci.get('bandix', 'general')) {
uci.add('bandix', 'general', 'general');
@@ -463,113 +39,6 @@ return view.extend({
if (!uci.get('bandix', 'dns')) {
uci.add('bandix', 'dns', 'dns');
}
-
- var language = uci.get('bandix', 'general', 'language');
- if (!language || language === 'auto') {
- language = getSystemLanguage();
- }
- var darkMode = isDarkMode();
-
- // 添加暗黑模式样式支持
- if (darkMode) {
- var style = E('style', {}, `
- body, .main {
- background-color: #0f172a !important;
- color: #e2e8f0 !important;
- }
-
- .cbi-section {
- background-color: #1E1E1E !important;
-
- border-radius: 8px !important;
- }
-
- .cbi-section h3 {
- color: #f1f5f9 !important;
- background-color: #333333 !important;
- border-bottom: 1px solid #1E1E1E !important;
- }
-
- .cbi-section-descr {
- color: #94a3b8 !important;
- }
-
- .cbi-value {
- border-bottom: 1px solid #1E1E1E !important;
- }
-
- .cbi-value-title {
- color: #e2e8f0 !important;
- }
-
- .cbi-value-description {
- color: #94a3b8 !important;
- }
-
- input[type="text"], input[type="number"], select, textarea {
- background-color: #333333 !important;
-
- color: #e2e8f0 !important;
- }
-
- input[type="text"]:focus, input[type="number"]:focus, select:focus, textarea:focus {
- border-color: #3b82f6 !important;
- box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.2) !important;
- }
-
- input[type="checkbox"] {
- accent-color: #3b82f6 !important;
- }
-
- .cbi-button, .btn {
- background-color: #333333 !important;
-
- color: #e2e8f0 !important;
- }
-
- .cbi-button:hover, .btn:hover {
- background-color: #1E1E1E !important;
- }
-
- .cbi-button-save {
- background-color: #3b82f6 !important;
- border-color: #3b82f6 !important;
- color: white !important;
- }
-
- .cbi-button-save:hover {
- background-color: #2563eb !important;
- }
-
- .cbi-section-error {
- background-color: #7f1d1d !important;
- border-color: #dc2626 !important;
- color: #fca5a5 !important;
- }
-
- /* 表格样式 */
- .table {
- background-color: #1E1E1E !important;
-
- }
-
- .table th {
- background-color: #333333 !important;
- color: #e2e8f0 !important;
- border-bottom: 1px solid #1E1E1E !important;
- }
-
- .table td {
- color: #cbd5e1 !important;
- border-bottom: 1px solid #1E1E1E !important;
- }
-
- .table tr:hover {
- background-color: #1E1E1E !important;
- }
- `);
- document.head.appendChild(style);
- }
// 从network配置中提取物理接口名称
if (networkConfig && networkConfig.length > 0) {
@@ -599,55 +68,31 @@ return view.extend({
m = new form.Map('bandix');
// 1. 基本设置部分 (general)
- s = m.section(form.NamedSection, 'general', 'general', getTranslation('基本设置', language));
- s.description = getTranslation('配置 Bandix 服务的基本参数', language);
+ s = m.section(form.NamedSection, 'general', 'general', _('Basic Settings'));
+ s.description = _('Configure basic parameters for Bandix service');
s.addremove = false;
- // 添加端口设置选项
- o = s.option(form.Value, 'port', getTranslation('端口', language),
- getTranslation('Bandix 服务监听的端口', language));
- o.default = '8686';
- o.datatype = 'port';
- o.placeholder = '8686';
- o.rmempty = false;
-
-
- // 添加网卡选择下拉菜单
- o = s.option(form.ListValue, 'iface', getTranslation('监控网卡', language),
- getTranslation('选择要监控的网络接口', language));
- o.rmempty = false;
-
- // 添加从配置获取的物理接口
- physicalInterfaces.forEach(function (iface) {
- o.value(iface, iface);
- });
-
- // 添加语言选择选项
- o = s.option(form.ListValue, 'language', getTranslation('界面语言', language),
- getTranslation('选择 Bandix 流量监控的显示语言', language));
- o.value('auto', getTranslation('跟随系统', language));
- o.value('zh-cn', getTranslation('简体中文', language));
- o.value('zh-tw', getTranslation('繁体中文', language));
- o.value('en', 'English');
- o.value('fr', 'Français');
- o.value('ja', '日本語');
- o.value('ru', 'Русский');
- o.default = 'auto';
+ // 添加端口设置选项
+ o = s.option(form.Value, 'port', _('Port'),
+ _('Port for Bandix service to listen on'));
+ o.default = '8686';
+ o.datatype = 'port';
+ o.placeholder = '8686';
o.rmempty = false;
- // 添加主题选择选项
- o = s.option(form.ListValue, 'theme', getTranslation('界面主题', language),
- getTranslation('选择 Bandix 流量监控的显示主题', language));
- o.value('auto', getTranslation('跟随系统', language));
- o.value('light', getTranslation('明亮模式', language));
- o.value('dark', getTranslation('暗黑模式', language));
- o.default = 'auto';
+ // 添加网卡选择下拉菜单
+ o = s.option(form.ListValue, 'iface', _('Monitor Interface'),
+ _('Select the LAN network interface to monitor'));
o.rmempty = false;
+ // 添加从配置获取的物理接口
+ physicalInterfaces.forEach(function (iface) {
+ o.value(iface, iface);
+ });
- // 添加日志级别选择选项
- o = s.option(form.ListValue, 'log_level', getTranslation('日志级别', language),
- getTranslation('设置 Bandix 服务的日志级别', language));
+ // 添加日志级别选择选项
+ o = s.option(form.ListValue, 'log_level', _('Log Level'),
+ _('Set the log level for Bandix service'));
o.value('trace', 'Trace');
o.value('debug', 'Debug');
o.value('info', 'Info');
@@ -657,106 +102,103 @@ return view.extend({
o.rmempty = false;
// 添加数据目录设置(只读)
- o = s.option(form.DummyValue, 'data_dir', getTranslation('数据目录', language));
+ o = s.option(form.DummyValue, 'data_dir', _('Data Directory'));
o.default = '/usr/share/bandix';
- o.cfgvalue = function(section_id) {
+ o.cfgvalue = function (section_id) {
return uci.get('bandix', section_id, 'data_dir') || '/usr/share/bandix';
};
// 添加意见反馈信息
- o = s.option(form.DummyValue, 'feedback_info', getTranslation('意见反馈', language));
+ o = s.option(form.DummyValue, 'feedback_info', _('Feedback'));
o.href = 'https://github.com/timsaya';
- o.cfgvalue = function() {
+ o.cfgvalue = function () {
return 'https://github.com/timsaya';
};
// 2. 流量监控设置部分 (traffic)
- s = m.section(form.NamedSection, 'traffic', 'traffic', getTranslation('流量监控设置', language));
- s.description = getTranslation('配置流量监控相关参数', language);
+ s = m.section(form.NamedSection, 'traffic', 'traffic', _('Traffic Monitor Settings'));
+ s.description = _('Configure traffic monitoring related parameters');
s.addremove = false;
- o = s.option(form.Flag, 'enabled', getTranslation('启用流量监控', language),
- getTranslation('启用 Bandix 流量监控服务', language));
+ o = s.option(form.Flag, 'enabled', _('Enable Traffic Monitoring'),
+ _('Enable Bandix Traffic Monitor Service'));
o.default = '0';
o.rmempty = false;
// 添加网速单位选择选项
- o = s.option(form.ListValue, 'speed_unit', getTranslation('网速单位', language),
- getTranslation('选择网速显示的单位格式', language));
- o.value('bytes', getTranslation('字节单位 (B/s, KB/s, MB/s)', language));
- o.value('bits', getTranslation('比特单位 (bps, Kbps, Mbps)', language));
+ o = s.option(form.ListValue, 'speed_unit', _('Speed Units'),
+ _('Select the speed display unit format'));
+ o.value('bytes', _('Bytes Units (B/s, KB/s, MB/s)'));
+ o.value('bits', _('Bits Units (bps, Kbps, Mbps)'));
o.default = 'bytes';
o.rmempty = false;
// 添加离线超时时间(秒)
- o = s.option(form.Value, 'offline_timeout', getTranslation('离线超时时间', language),
- getTranslation('设置设备离线判断的超时时间(秒)', language));
+ o = s.option(form.Value, 'offline_timeout', _('Offline Timeout'),
+ _('Set the timeout for device offline detection (seconds). Devices inactive for longer than this time will be marked as offline'));
o.datatype = 'uinteger';
o.placeholder = '600';
o.default = '600';
o.rmempty = true;
// 添加持久化历史数据选项
- o = s.option(form.Flag, 'traffic_persist_history', getTranslation('持久化历史数据', language),
- getTranslation('启用数据持久化功能', language));
+ o = s.option(form.Flag, 'traffic_persist_history', _('Persist History Data'),
+ _('Enable data persistence functionality, data will only be persisted to disk when this option is enabled'));
o.default = '0';
o.rmempty = false;
// 添加数据 flush 间隔(秒)
- o = s.option(form.ListValue, 'traffic_flush_interval_seconds', getTranslation('数据 flush 间隔', language),
- getTranslation('设置数据 flush 间隔', language));
- o.value('60', getTranslation('1 分钟', language));
- o.value('300', getTranslation('5 分钟', language));
- o.value('600', getTranslation('10 分钟', language));
- o.value('900', getTranslation('15 分钟', language));
- o.value('1200', getTranslation('20 分钟', language));
- o.value('1500', getTranslation('25 分钟', language));
- o.value('1800', getTranslation('30 分钟', language));
- o.value('3600', getTranslation('1 小时', language));
- o.value('7200', getTranslation('2 小时', language));
+ o = s.option(form.ListValue, 'traffic_flush_interval_seconds', _('Data Flush Interval'),
+ _('Set the interval for flushing data to disk'));
+ o.value('60', _('1 minute'));
+ o.value('300', _('5 minutes'));
+ o.value('600', _('10 minutes'));
+ o.value('900', _('15 minutes'));
+ o.value('1200', _('20 minutes'));
+ o.value('1500', _('25 minutes'));
+ o.value('1800', _('30 minutes'));
+ o.value('3600', _('1 hour'));
+ o.value('7200', _('2 hours'));
o.default = '600';
o.rmempty = false;
o.depends('traffic_persist_history', '1');
-
// 添加历史流量周期(秒)
- o = s.option(form.ListValue, 'traffic_retention_seconds', getTranslation('历史流量周期', language),
- getTranslation('设置历史流量数据周期', language));
- o.value('60', getTranslation('1 分钟', language));
- o.value('300', getTranslation('5 分钟', language));
- o.value('600', getTranslation('10 分钟', language));
- o.value('900', getTranslation('15 分钟', language));
- o.value('1200', getTranslation('20 分钟', language));
- o.value('1500', getTranslation('25 分钟', language));
- o.value('1800', getTranslation('30 分钟', language));
+ o = s.option(form.ListValue, 'traffic_retention_seconds', _('Traffic History Period'),
+ _('10 minutes interval uses about 60 KB per device'));
+ o.value('60', _('1 minute'));
+ o.value('300', _('5 minutes'));
+ o.value('600', _('10 minutes'));
+ o.value('900', _('15 minutes'));
+ o.value('1200', _('20 minutes'));
+ o.value('1500', _('25 minutes'));
+ o.value('1800', _('30 minutes'));
o.default = '600';
o.rmempty = false;
-
-
// 3. 连接监控设置部分 (connections)
- s = m.section(form.NamedSection, 'connections', 'connections', getTranslation('连接监控设置', language));
- s.description = getTranslation('配置连接监控相关参数', language);
+ s = m.section(form.NamedSection, 'connections', 'connections', _('Connection Monitor Settings'));
+ s.description = _('Configure connection monitoring related parameters');
s.addremove = false;
- o = s.option(form.Flag, 'enabled', getTranslation('启用连接监控', language),
- getTranslation('启用 Bandix 连接监控功能', language));
+ o = s.option(form.Flag, 'enabled', _('Enable Connection Monitoring'),
+ _('Enable Bandix connection monitoring'));
o.default = '0';
o.rmempty = false;
// 4. DNS监控设置部分 (dns)
- s = m.section(form.NamedSection, 'dns', 'dns', getTranslation('DNS监控设置', language));
- s.description = getTranslation('配置DNS监控相关参数', language);
+ s = m.section(form.NamedSection, 'dns', 'dns', _('DNS Monitor Settings'));
+ s.description = _('Configure DNS monitoring related parameters');
s.addremove = false;
- o = s.option(form.Flag, 'enabled', getTranslation('启用DNS监控', language),
- getTranslation('启用 Bandix DNS监控功能', language));
+ o = s.option(form.Flag, 'enabled', _('Enable DNS Monitoring'),
+ _('Enable Bandix DNS monitoring'));
o.default = '0';
o.rmempty = false;
// 添加DNS最大记录数选项
- o = s.option(form.Value, 'dns_max_records', getTranslation('DNS最大记录数', language),
- getTranslation('设置DNS查询记录的最大保存数量', language));
+ o = s.option(form.Value, 'dns_max_records', _('DNS Max Records'),
+ _('Set the maximum number of DNS query records to keep. Older records will be deleted when this limit is exceeded'));
o.datatype = 'uinteger';
o.placeholder = '10000';
o.default = '10000';
diff --git a/luci-app-bandix/po/es/bandix.po b/luci-app-bandix/po/es/bandix.po
new file mode 100644
index 0000000..b9bf0b1
--- /dev/null
+++ b/luci-app-bandix/po/es/bandix.po
@@ -0,0 +1,735 @@
+msgid ""
+msgstr ""
+"Content-Type: text/plain; charset=UTF-8\n"
+
+msgid "Bandix Traffic Monitor"
+msgstr "Monitor de Tráfico Bandix"
+
+msgid "Loading data..."
+msgstr "Cargando datos..."
+
+msgid "Unable to fetch data"
+msgstr "No se pueden obtener los datos"
+
+msgid "Unable to fetch history data"
+msgstr "No se pueden obtener los datos del historial"
+
+msgid "Hostname"
+msgstr "Nombre de host"
+
+msgid "IP Address"
+msgstr "Dirección IP"
+
+msgid "MAC Address"
+msgstr "Dirección MAC"
+
+msgid "Download Speed"
+msgstr "Velocidad de descarga"
+
+msgid "Upload Speed"
+msgstr "Velocidad de carga"
+
+msgid "Total Download"
+msgstr "Descarga total"
+
+msgid "Total Upload"
+msgstr "Carga total"
+
+msgid "Download Limit"
+msgstr "Límite de descarga"
+
+msgid "Upload Limit"
+msgstr "Límite de carga"
+
+msgid "Interface Language"
+msgstr "Idioma de la interfaz"
+
+msgid "Select the display language for Bandix Traffic Monitor"
+msgstr "Seleccione el idioma de visualización para el Monitor de Tráfico Bandix"
+
+msgid "Device Info"
+msgstr "Información del dispositivo"
+
+msgid "Device List"
+msgstr "Lista de dispositivos"
+
+msgid "LAN Traffic"
+msgstr "Tráfico LAN"
+
+msgid "WAN Traffic"
+msgstr "Tráfico WAN"
+
+msgid "Rate Limit"
+msgstr "Límite de velocidad"
+
+msgid "Actions"
+msgstr "Acciones"
+
+msgid "Online Devices"
+msgstr "Dispositivos en línea"
+
+msgid "WAN Only"
+msgstr "Solo WAN"
+
+msgid "Settings"
+msgstr "Configuración"
+
+msgid "Device Settings"
+msgstr "Configuración del dispositivo"
+
+msgid "Rate Limits"
+msgstr "Límites de velocidad"
+
+msgid "Remove Rate Limit"
+msgstr "Eliminar límite de velocidad"
+
+msgid "Save"
+msgstr "Guardar"
+
+msgid "Cancel"
+msgstr "Cancelar"
+
+msgid "Set Rate Limit"
+msgstr "Establecer límite de velocidad"
+
+msgid "Device"
+msgstr "Dispositivo"
+
+msgid "Set Hostname"
+msgstr "Establecer nombre de host"
+
+msgid "Please enter hostname"
+msgstr "Por favor ingrese el nombre de host"
+
+msgid "Hostname set successfully"
+msgstr "Nombre de host establecido correctamente"
+
+msgid "Failed to set hostname"
+msgstr "Error al establecer el nombre de host"
+
+msgid "Unlimited"
+msgstr "Ilimitado"
+
+msgid "Settings saved successfully"
+msgstr "Configuración guardada correctamente"
+
+msgid "Failed to save settings"
+msgstr "Error al guardar la configuración"
+
+msgid "Please enter a valid speed value"
+msgstr "Por favor ingrese un valor de velocidad válido"
+
+msgid "Speed value must be greater than 0"
+msgstr "El valor de velocidad debe ser mayor que 0"
+
+msgid "Saving..."
+msgstr "Guardando..."
+
+msgid "Rate limiting only applies to WAN traffic."
+msgstr "La limitación de velocidad solo se aplica al tráfico WAN."
+
+msgid "Tip: Enter 0 for unlimited"
+msgstr "Consejo: Ingrese 0 para ilimitado"
+
+msgid "Traffic History"
+msgstr "Historial de tráfico"
+
+msgid "Select Device"
+msgstr "Seleccionar dispositivo"
+
+msgid "All Devices"
+msgstr "Todos los dispositivos"
+
+msgid "Time Range"
+msgstr "Rango de tiempo"
+
+msgid "Last 5 minutes"
+msgstr "Últimos 5 minutos"
+
+msgid "Last 30 minutes"
+msgstr "Últimos 30 minutos"
+
+msgid "Last 2 hours"
+msgstr "Últimas 2 horas"
+
+msgid "Type"
+msgstr "Tipo"
+
+msgid "Total"
+msgstr "Total"
+
+msgid "LAN"
+msgstr "Tráfico LAN"
+
+msgid "WAN"
+msgstr "Tráfico WAN"
+
+msgid "Refresh"
+msgstr "Actualizar"
+
+msgid "Upload Rate"
+msgstr "Velocidad de carga"
+
+msgid "Download Rate"
+msgstr "Velocidad de descarga"
+
+msgid "Last"
+msgstr "Último"
+
+msgid "second"
+msgstr "segundo"
+
+msgid "minute"
+msgstr "minuto"
+
+msgid "hour"
+msgstr "hora"
+
+msgid "day"
+msgstr "día"
+
+msgid "week"
+msgstr "semana"
+
+msgid "Other Rates"
+msgstr "Otras velocidades"
+
+msgid "Cumulative"
+msgstr "Acumulado"
+
+msgid "Total Uploaded"
+msgstr "Total cargado"
+
+msgid "Total Downloaded"
+msgstr "Total descargado"
+
+msgid "LAN Uploaded"
+msgstr "LAN cargado"
+
+msgid "LAN Downloaded"
+msgstr "LAN descargado"
+
+msgid "WAN Uploaded"
+msgstr "WAN cargado"
+
+msgid "WAN Downloaded"
+msgstr "WAN descargado"
+
+msgid "Total Upload"
+msgstr "Carga total"
+
+msgid "Total Download"
+msgstr "Descarga total"
+
+msgid "LAN Upload"
+msgstr "Carga LAN"
+
+msgid "LAN Download"
+msgstr "Descarga LAN"
+
+msgid "WAN Upload"
+msgstr "Carga WAN"
+
+msgid "WAN Download"
+msgstr "Descarga WAN"
+
+msgid "Never Online"
+msgstr "Nunca en línea"
+
+msgid "Just Now"
+msgstr "Ahora mismo"
+
+msgid "min ago"
+msgstr "min hace"
+
+msgid "h ago"
+msgstr "h hace"
+
+msgid "days ago"
+msgstr "días hace"
+
+msgid "months ago"
+msgstr "meses hace"
+
+msgid "years ago"
+msgstr "años hace"
+
+msgid "Last Online"
+msgstr "Última vez en línea"
+
+msgid "Zoom"
+msgstr "Zoom"
+
+msgid "Sort By"
+msgstr "Ordenar por"
+
+msgid "Online Status"
+msgstr "Estado en línea"
+
+msgid "Total Traffic"
+msgstr "Tráfico total"
+
+msgid "Ascending"
+msgstr "Ascendente"
+
+msgid "Descending"
+msgstr "Descendente"
+
+msgid "Sort by Speed"
+msgstr "Ordenar por velocidad"
+
+msgid "Sort by Traffic"
+msgstr "Ordenar por tráfico"
+
+msgid "Simple Mode"
+msgstr "Modo simple"
+
+msgid "Detailed Mode"
+msgstr "Modo detallado"
+
+msgid "Bandix DNS Monitor"
+msgstr "Monitor DNS Bandix"
+
+msgid "DNS Monitor"
+msgstr "Monitor DNS"
+
+msgid "DNS Query Records"
+msgstr "Registros de consultas DNS"
+
+msgid "DNS Statistics"
+msgstr "Estadísticas DNS"
+
+msgid "DNS Monitoring Disabled"
+msgstr "Monitoreo DNS deshabilitado"
+
+msgid "Please enable DNS monitoring in settings"
+msgstr "Por favor habilite el monitoreo DNS en la configuración"
+
+msgid "Go to Settings"
+msgstr "Ir a configuración"
+
+msgid "No Data"
+msgstr "Sin datos"
+
+msgid "Time"
+msgstr "Hora"
+
+msgid "Domain"
+msgstr "Dominio"
+
+msgid "Query Type"
+msgstr "Tipo de consulta"
+
+msgid "Response Code"
+msgstr "Código de respuesta"
+
+msgid "Response Time"
+msgstr "Tiempo de respuesta"
+
+msgid "Source IP"
+msgstr "IP de origen"
+
+msgid "Destination IP"
+msgstr "IP de destino"
+
+msgid "Response IPs"
+msgstr "IPs de respuesta"
+
+msgid "Response Result"
+msgstr "Resultado de respuesta"
+
+msgid "DNS Server"
+msgstr "Servidor DNS"
+
+msgid "Query"
+msgstr "Consulta"
+
+msgid "Response"
+msgstr "Respuesta"
+
+msgid "Filter"
+msgstr "Filtro"
+
+msgid "Domain Filter"
+msgstr "Filtro de dominio"
+
+msgid "Device Filter"
+msgstr "Filtro de dispositivo"
+
+msgid "DNS Server Filter"
+msgstr "Filtro de servidor DNS"
+
+msgid "Type Filter"
+msgstr "Filtro de tipo"
+
+msgid "All"
+msgstr "Todos"
+
+msgid "Queries Only"
+msgstr "Solo consultas"
+
+msgid "Responses Only"
+msgstr "Solo respuestas"
+
+msgid "Search"
+msgstr "Buscar"
+
+msgid "Search Domain"
+msgstr "Buscar dominio"
+
+msgid "Search Device"
+msgstr "Buscar dispositivo"
+
+msgid "Search DNS Server"
+msgstr "Buscar servidor DNS"
+
+msgid "Clear"
+msgstr "Limpiar"
+
+msgid "Previous"
+msgstr "Anterior"
+
+msgid "Next"
+msgstr "Siguiente"
+
+msgid "Page"
+msgstr "Página"
+
+msgid "of"
+msgstr "de"
+
+msgid "Total"
+msgstr "Total"
+
+msgid "records"
+msgstr "registros"
+
+msgid "Per Page"
+msgstr "Por página"
+
+msgid ""
+msgstr ""
+
+msgid "Total Queries"
+msgstr "Total de consultas"
+
+msgid "Total Responses"
+msgstr "Total de respuestas"
+
+msgid "Queries with Response"
+msgstr "Consultas con respuesta"
+
+msgid "Queries without Response"
+msgstr "Consultas sin respuesta"
+
+msgid "Avg Response Time"
+msgstr "Tiempo de respuesta promedio"
+
+msgid "Min Response Time"
+msgstr "Tiempo de respuesta mínimo"
+
+msgid "Max Response Time"
+msgstr "Tiempo de respuesta máximo"
+
+msgid "Success Rate"
+msgstr "Tasa de éxito"
+
+msgid "Success"
+msgstr "Éxito"
+
+msgid "Failure"
+msgstr "Fallo"
+
+msgid "Top Domains"
+msgstr "Dominios principales"
+
+msgid "Top Query Types"
+msgstr "Tipos de consulta principales"
+
+msgid "Top Devices"
+msgstr "Dispositivos principales"
+
+msgid "Top DNS Servers"
+msgstr "Servidores DNS principales"
+
+msgid "Unique Devices"
+msgstr "Dispositivos únicos"
+
+msgid "ms"
+msgstr "ms"
+
+msgid "minutes"
+msgstr "minutos"
+
+msgid "Unknown Device"
+msgstr "Dispositivo desconocido"
+
+msgid "Domain not found"
+msgstr "Dominio no encontrado"
+
+msgid "Server error"
+msgstr "Error del servidor"
+
+msgid "Format error"
+msgstr "Error de formato"
+
+msgid "Refused"
+msgstr "Rechazado"
+
+msgid "Other"
+msgstr "Otro"
+
+msgid "Does not include DoH and DoT"
+msgstr "No incluye DoH y DoT"
+
+msgid "Latest Response Time"
+msgstr "Último tiempo de respuesta"
+
+msgid "Response Code Distribution"
+msgstr "Distribución de códigos de respuesta"
+
+msgid "Response Time Percentiles"
+msgstr "Percentiles de tiempo de respuesta"
+
+msgid "P50"
+msgstr "P50"
+
+msgid "P90"
+msgstr "P90"
+
+msgid "P95"
+msgstr "P95"
+
+msgid "P99"
+msgstr "P99"
+
+msgid "Bandix Connection Monitor"
+msgstr "Monitor de Conexión Bandix"
+
+msgid "Connection Monitor"
+msgstr "Monitor de conexión"
+
+msgid "Device Connection Statistics"
+msgstr "Estadísticas de conexión de dispositivos"
+
+msgid "Global Connection Statistics"
+msgstr "Estadísticas de conexión global"
+
+msgid "Active TCP"
+msgstr "TCP activo"
+
+msgid "Active UDP"
+msgstr "UDP activo"
+
+msgid "Closed TCP"
+msgstr "TCP cerrado"
+
+msgid "Total Connections"
+msgstr "Total de conexiones"
+
+msgid "Last Updated"
+msgstr "Última actualización"
+
+msgid "TCP Connections"
+msgstr "Conexiones TCP"
+
+msgid "UDP Connections"
+msgstr "Conexiones UDP"
+
+msgid "Established TCP"
+msgstr "TCP establecido"
+
+msgid "TIME_WAIT TCP"
+msgstr "TIME_WAIT TCP"
+
+msgid "CLOSE_WAIT TCP"
+msgstr "CLOSE_WAIT TCP"
+
+msgid "Total Devices"
+msgstr "Total de dispositivos"
+
+msgid "Connection Monitor Disabled"
+msgstr "Monitor de conexión deshabilitado"
+
+msgid "Please enable connection monitoring in settings"
+msgstr "Por favor habilite el monitoreo de conexión en la configuración"
+
+msgid "List only shows LAN device connections, data may differ from total connections."
+msgstr "La lista solo muestra conexiones de dispositivos LAN, los datos pueden diferir del total de conexiones."
+
+msgid "TCP Status Details"
+msgstr "Detalles del estado TCP"
+
+msgid "Basic Settings"
+msgstr "Configuración básica"
+
+msgid "Traffic Monitor Settings"
+msgstr "Configuración del monitor de tráfico"
+
+msgid "Connection Monitor Settings"
+msgstr "Configuración del monitor de conexión"
+
+msgid "DNS Monitor Settings"
+msgstr "Configuración del monitor DNS"
+
+msgid "Bandix Basic Configuration"
+msgstr "Configuración básica de Bandix"
+
+msgid "Configure basic parameters for Bandix service"
+msgstr "Configurar parámetros básicos para el servicio Bandix"
+
+msgid "Bandix Traffic Monitor Configuration"
+msgstr "Configuración del Monitor de Tráfico Bandix"
+
+msgid "Configure traffic monitoring related parameters"
+msgstr "Configurar parámetros relacionados con el monitoreo de tráfico"
+
+msgid "Bandix Connection Monitor Configuration"
+msgstr "Configuración del Monitor de Conexión Bandix"
+
+msgid "Configure connection monitoring related parameters"
+msgstr "Configurar parámetros relacionados con el monitoreo de conexión"
+
+msgid "Bandix DNS Monitor Configuration"
+msgstr "Configuración del Monitor DNS Bandix"
+
+msgid "Configure DNS monitoring related parameters"
+msgstr "Configurar parámetros relacionados con el monitoreo DNS"
+
+msgid "Enable"
+msgstr "Habilitar"
+
+msgid "Enable Bandix Traffic Monitor Service"
+msgstr "Habilitar servicio del Monitor de Tráfico Bandix"
+
+msgid "Enable Traffic Monitoring"
+msgstr "Habilitar monitoreo de tráfico"
+
+msgid "Enable Connection Monitoring"
+msgstr "Habilitar monitoreo de conexión"
+
+msgid "Enable DNS Monitoring"
+msgstr "Habilitar monitoreo DNS"
+
+msgid "Simplified Chinese"
+msgstr "Chino simplificado"
+
+msgid "Traditional Chinese"
+msgstr "Chino tradicional"
+
+msgid "Port"
+msgstr "Puerto"
+
+msgid "Port for Bandix service to listen on"
+msgstr "Puerto para que el servicio Bandix escuche"
+
+msgid "Monitor Interface"
+msgstr "Interfaz de monitoreo"
+
+msgid "Select the LAN network interface to monitor"
+msgstr "Seleccione la interfaz de red LAN para monitorear"
+
+msgid "Speed Units"
+msgstr "Unidades de velocidad"
+
+msgid "Select the speed display unit format"
+msgstr "Seleccione el formato de unidad de visualización de velocidad"
+
+msgid "Bytes Units (B/s, KB/s, MB/s)"
+msgstr "Unidades de bytes (B/s, KB/s, MB/s)"
+
+msgid "Bits Units (bps, Kbps, Mbps)"
+msgstr "Unidades de bits (bps, Kbps, Mbps)"
+
+msgid "Interface Theme"
+msgstr "Tema de la interfaz"
+
+msgid "Select the display theme for Bandix Traffic Monitor"
+msgstr "Seleccione el tema de visualización para el Monitor de Tráfico Bandix"
+
+msgid "Follow System"
+msgstr "Seguir sistema"
+
+msgid "Light Mode"
+msgstr "Modo claro"
+
+msgid "Dark Mode"
+msgstr "Modo oscuro"
+
+msgid "Feedback"
+msgstr "Comentarios"
+
+msgid "Log Level"
+msgstr "Nivel de registro"
+
+msgid "Set the log level for Bandix service"
+msgstr "Establecer el nivel de registro para el servicio Bandix"
+
+msgid "Offline Timeout"
+msgstr "Tiempo de espera sin conexión"
+
+msgid "Set the timeout for device offline detection (seconds). Devices inactive for longer than this time will be marked as offline"
+msgstr "Establecer el tiempo de espera para la detección de dispositivos sin conexión (segundos). Los dispositivos inactivos por más tiempo serán marcados como sin conexión"
+
+msgid "Traffic History Period"
+msgstr "Período del historial de tráfico"
+
+msgid "10 minutes interval uses about 60 KB per device"
+msgstr "Intervalo de 10 minutos usa aproximadamente 60 KB por dispositivo"
+
+msgid "Data Flush Interval"
+msgstr "Intervalo de vaciado de datos"
+
+msgid "Set the interval for flushing data to disk"
+msgstr "Establecer el intervalo para vaciar datos al disco"
+
+msgid "1 minute"
+msgstr "1 minuto"
+
+msgid "5 minutes"
+msgstr "5 minutos"
+
+msgid "10 minutes"
+msgstr "10 minutos"
+
+msgid "15 minutes"
+msgstr "15 minutos"
+
+msgid "20 minutes"
+msgstr "20 minutos"
+
+msgid "25 minutes"
+msgstr "25 minutos"
+
+msgid "30 minutes"
+msgstr "30 minutos"
+
+msgid "1 hour"
+msgstr "1 hora"
+
+msgid "2 hours"
+msgstr "2 horas"
+
+msgid "Data Directory"
+msgstr "Directorio de datos"
+
+msgid "Bandix data storage directory"
+msgstr "Directorio de almacenamiento de datos de Bandix"
+
+msgid "Enable Bandix connection monitoring"
+msgstr "Habilitar monitoreo de conexión Bandix"
+
+msgid "Enable Bandix DNS monitoring"
+msgstr "Habilitar monitoreo DNS Bandix"
+
+msgid "DNS Max Records"
+msgstr "Registros DNS máximos"
+
+msgid "Set the maximum number of DNS query records to keep. Older records will be deleted when this limit is exceeded"
+msgstr "Establecer el número máximo de registros de consulta DNS para mantener. Los registros más antiguos se eliminarán cuando se exceda este límite"
+
+msgid "Persist History Data"
+msgstr "Persistir datos del historial"
+
+msgid "Enable data persistence functionality, data will only be persisted to disk when this option is enabled"
+msgstr "Habilitar funcionalidad de persistencia de datos, los datos solo se persistirán en el disco cuando esta opción esté habilitada"
diff --git a/luci-app-bandix/po/fr/bandix.po b/luci-app-bandix/po/fr/bandix.po
new file mode 100644
index 0000000..165d2b1
--- /dev/null
+++ b/luci-app-bandix/po/fr/bandix.po
@@ -0,0 +1,735 @@
+msgid ""
+msgstr ""
+"Content-Type: text/plain; charset=UTF-8\n"
+
+msgid "Bandix Traffic Monitor"
+msgstr "Moniteur de trafic Bandix"
+
+msgid "Loading data..."
+msgstr "Chargement des données..."
+
+msgid "Unable to fetch data"
+msgstr "Impossible de récupérer les données"
+
+msgid "Unable to fetch history data"
+msgstr "无法获取历史数据"
+
+msgid "Hostname"
+msgstr "Nom d'hôte"
+
+msgid "IP Address"
+msgstr "Adresse IP"
+
+msgid "MAC Address"
+msgstr "Adresse MAC"
+
+msgid "Download Speed"
+msgstr "Vitesse de téléchargement"
+
+msgid "Upload Speed"
+msgstr "Vitesse de téléversement"
+
+msgid "Total Download"
+msgstr "Téléchargement total"
+
+msgid "Total Upload"
+msgstr "Téléversement total"
+
+msgid "Download Limit"
+msgstr "Limite de téléchargement"
+
+msgid "Upload Limit"
+msgstr "Limite de téléversement"
+
+msgid "Interface Language"
+msgstr "Langue de l'interface"
+
+msgid "Select the display language for Bandix Traffic Monitor"
+msgstr "Sélectionner la langue d'affichage pour le moniteur de trafic Bandix"
+
+msgid "Device Info"
+msgstr "Informations sur l'appareil"
+
+msgid "Device List"
+msgstr "Liste des appareils"
+
+msgid "LAN Traffic"
+msgstr "Trafic LAN"
+
+msgid "WAN Traffic"
+msgstr "Trafic WAN"
+
+msgid "Rate Limit"
+msgstr "Limite de débit"
+
+msgid "Actions"
+msgstr "Actions"
+
+msgid "Online Devices"
+msgstr "Appareils en ligne"
+
+msgid "WAN Only"
+msgstr "仅限WAN 流量"
+
+msgid "Settings"
+msgstr "Paramètres"
+
+msgid "Device Settings"
+msgstr "Paramètres de l'appareil"
+
+msgid "Rate Limits"
+msgstr "限速设置"
+
+msgid "Remove Rate Limit"
+msgstr "取消限速"
+
+msgid "Save"
+msgstr "Enregistrer"
+
+msgid "Cancel"
+msgstr "Annuler"
+
+msgid "Set Rate Limit"
+msgstr "设置限速"
+
+msgid "Device"
+msgstr "Appareil"
+
+msgid "Set Hostname"
+msgstr "设置主机名"
+
+msgid "Please enter hostname"
+msgstr "请输入主机名"
+
+msgid "Hostname set successfully"
+msgstr "主机名设置成功"
+
+msgid "Failed to set hostname"
+msgstr "主机名设置失败"
+
+msgid "Unlimited"
+msgstr "Illimité"
+
+msgid "Settings saved successfully"
+msgstr "Paramètres enregistrés avec succès"
+
+msgid "Failed to save settings"
+msgstr "Échec de l'enregistrement des paramètres"
+
+msgid "Please enter a valid speed value"
+msgstr "请输入有效的速度值"
+
+msgid "Speed value must be greater than 0"
+msgstr "速度值必须大于0"
+
+msgid "Saving..."
+msgstr "Enregistrement..."
+
+msgid "Rate limiting only applies to WAN traffic."
+msgstr "限速功能仅对 WAN 流量生效。"
+
+msgid "Tip: Enter 0 for unlimited"
+msgstr "提示:输入 0 表示无限制"
+
+msgid "Traffic History"
+msgstr "Historique du trafic"
+
+msgid "Select Device"
+msgstr "Sélectionner un appareil"
+
+msgid "All Devices"
+msgstr "Tous les appareils"
+
+msgid "Time Range"
+msgstr "时间范围"
+
+msgid "Last 5 minutes"
+msgstr "最近5分钟"
+
+msgid "Last 30 minutes"
+msgstr "最近30分钟"
+
+msgid "Last 2 hours"
+msgstr "最近2小时"
+
+msgid "Type"
+msgstr "Type"
+
+msgid "Total"
+msgstr "Total"
+
+msgid "LAN"
+msgstr "LAN 流量"
+
+msgid "WAN"
+msgstr "WAN 流量"
+
+msgid "Refresh"
+msgstr "Actualiser"
+
+msgid "Upload Rate"
+msgstr "Taux de téléversement"
+
+msgid "Download Rate"
+msgstr "Taux de téléchargement"
+
+msgid "Last"
+msgstr "最近"
+
+msgid "second"
+msgstr "秒"
+
+msgid "minute"
+msgstr "分钟"
+
+msgid "hour"
+msgstr "小时"
+
+msgid "day"
+msgstr "天"
+
+msgid "week"
+msgstr "周"
+
+msgid "Other Rates"
+msgstr "其他速率"
+
+msgid "Cumulative"
+msgstr "累计流量"
+
+msgid "Total Uploaded"
+msgstr "总上传"
+
+msgid "Total Downloaded"
+msgstr "总下载"
+
+msgid "LAN Uploaded"
+msgstr "LAN 已上传"
+
+msgid "LAN Downloaded"
+msgstr "LAN 已下载"
+
+msgid "WAN Uploaded"
+msgstr "WAN 已上传"
+
+msgid "WAN Downloaded"
+msgstr "WAN 已下载"
+
+msgid "Total Upload"
+msgstr "Téléversement total"
+
+msgid "Total Download"
+msgstr "Téléchargement total"
+
+msgid "LAN Upload"
+msgstr "LAN 上传速率"
+
+msgid "LAN Download"
+msgstr "LAN 下载速率"
+
+msgid "WAN Upload"
+msgstr "WAN 上传速率"
+
+msgid "WAN Download"
+msgstr "WAN 下载速率"
+
+msgid "Never Online"
+msgstr "Jamais en ligne"
+
+msgid "Just Now"
+msgstr "À l'instant"
+
+msgid "min ago"
+msgstr "min il y a"
+
+msgid "h ago"
+msgstr "h il y a"
+
+msgid "days ago"
+msgstr "jours il y a"
+
+msgid "months ago"
+msgstr "mois il y a"
+
+msgid "years ago"
+msgstr "ans il y a"
+
+msgid "Last Online"
+msgstr "Dernière connexion"
+
+msgid "Zoom"
+msgstr "缩放"
+
+msgid "Sort By"
+msgstr "排序方式"
+
+msgid "Online Status"
+msgstr "在线状态"
+
+msgid "Total Traffic"
+msgstr "总流量"
+
+msgid "Ascending"
+msgstr "升序"
+
+msgid "Descending"
+msgstr "降序"
+
+msgid "Sort by Speed"
+msgstr "按速度排序"
+
+msgid "Sort by Traffic"
+msgstr "按用量排序"
+
+msgid "Simple Mode"
+msgstr "Mode simple"
+
+msgid "Detailed Mode"
+msgstr "Mode détaillé"
+
+msgid "Bandix DNS Monitor"
+msgstr "Moniteur DNS Bandix"
+
+msgid "DNS Monitor"
+msgstr "Moniteur DNS"
+
+msgid "DNS Query Records"
+msgstr "Enregistrements de requêtes DNS"
+
+msgid "DNS Statistics"
+msgstr "DNS 统计信息"
+
+msgid "DNS Monitoring Disabled"
+msgstr "DNS监控未启用"
+
+msgid "Please enable DNS monitoring in settings"
+msgstr "请在设置中启用DNS监控功能"
+
+msgid "Go to Settings"
+msgstr "前往设置"
+
+msgid "No Data"
+msgstr "无数据"
+
+msgid "Time"
+msgstr "Heure"
+
+msgid "Domain"
+msgstr "Domaine"
+
+msgid "Query Type"
+msgstr "Type de requête"
+
+msgid "Response Code"
+msgstr "响应码"
+
+msgid "Response Time"
+msgstr "Temps de réponse"
+
+msgid "Source IP"
+msgstr "源IP"
+
+msgid "Destination IP"
+msgstr "目标IP"
+
+msgid "Response IPs"
+msgstr "响应IP"
+
+msgid "Response Result"
+msgstr "响应结果"
+
+msgid "DNS Server"
+msgstr "Serveur DNS"
+
+msgid "Query"
+msgstr "Requête"
+
+msgid "Response"
+msgstr "Réponse"
+
+msgid "Filter"
+msgstr "过滤"
+
+msgid "Domain Filter"
+msgstr "域名过滤"
+
+msgid "Device Filter"
+msgstr "设备过滤"
+
+msgid "DNS Server Filter"
+msgstr "DNS服务器过滤"
+
+msgid "Type Filter"
+msgstr "类型过滤"
+
+msgid "All"
+msgstr "全部"
+
+msgid "Queries Only"
+msgstr "仅查询"
+
+msgid "Responses Only"
+msgstr "仅响应"
+
+msgid "Search"
+msgstr "Rechercher"
+
+msgid "Search Domain"
+msgstr "搜索域名"
+
+msgid "Search Device"
+msgstr "搜索设备"
+
+msgid "Search DNS Server"
+msgstr "搜索DNS服务器"
+
+msgid "Clear"
+msgstr "清除"
+
+msgid "Previous"
+msgstr "Précédent"
+
+msgid "Next"
+msgstr "Suivant"
+
+msgid "Page"
+msgstr "Page"
+
+msgid "of"
+msgstr "sur"
+
+msgid "Total"
+msgstr "Total"
+
+msgid "records"
+msgstr "enregistrements"
+
+msgid "Per Page"
+msgstr "每页显示"
+
+msgid ""
+msgstr "条"
+
+msgid "Total Queries"
+msgstr "Total des requêtes"
+
+msgid "Total Responses"
+msgstr "总响应数"
+
+msgid "Queries with Response"
+msgstr "有响应查询"
+
+msgid "Queries without Response"
+msgstr "无响应查询"
+
+msgid "Avg Response Time"
+msgstr "平均响应时间"
+
+msgid "Min Response Time"
+msgstr "最快响应时间"
+
+msgid "Max Response Time"
+msgstr "最慢响应时间"
+
+msgid "Success Rate"
+msgstr "Taux de réussite"
+
+msgid "Success"
+msgstr "Succès"
+
+msgid "Failure"
+msgstr "Échec"
+
+msgid "Top Domains"
+msgstr "Domaines les plus consultés"
+
+msgid "Top Query Types"
+msgstr "最常用查询类型"
+
+msgid "Top Devices"
+msgstr "Appareils les plus actifs"
+
+msgid "Top DNS Servers"
+msgstr "最常用DNS服务器"
+
+msgid "Unique Devices"
+msgstr "唯一设备数"
+
+msgid "ms"
+msgstr "毫秒"
+
+msgid "minutes"
+msgstr "分钟"
+
+msgid "Unknown Device"
+msgstr "未知设备"
+
+msgid "Domain not found"
+msgstr "域名未找到"
+
+msgid "Server error"
+msgstr "服务器错误"
+
+msgid "Format error"
+msgstr "格式错误"
+
+msgid "Refused"
+msgstr "拒绝"
+
+msgid "Other"
+msgstr "其他"
+
+msgid "Does not include DoH and DoT"
+msgstr "不包含 DoH 和 DoT"
+
+msgid "Latest Response Time"
+msgstr "最新响应时间"
+
+msgid "Response Code Distribution"
+msgstr "响应码分布"
+
+msgid "Response Time Percentiles"
+msgstr "响应时间百分位"
+
+msgid "P50"
+msgstr "P50"
+
+msgid "P90"
+msgstr "P90"
+
+msgid "P95"
+msgstr "P95"
+
+msgid "P99"
+msgstr "P99"
+
+msgid "Bandix Connection Monitor"
+msgstr "Moniteur de connexion Bandix"
+
+msgid "Connection Monitor"
+msgstr "Moniteur de connexion"
+
+msgid "Device Connection Statistics"
+msgstr "设备连接统计"
+
+msgid "Global Connection Statistics"
+msgstr "全局连接统计"
+
+msgid "Active TCP"
+msgstr "活跃TCP"
+
+msgid "Active UDP"
+msgstr "活跃UDP"
+
+msgid "Closed TCP"
+msgstr "已关闭TCP"
+
+msgid "Total Connections"
+msgstr "Total des connexions"
+
+msgid "Last Updated"
+msgstr "最后更新"
+
+msgid "TCP Connections"
+msgstr "Connexions TCP"
+
+msgid "UDP Connections"
+msgstr "Connexions UDP"
+
+msgid "Established TCP"
+msgstr "已建立TCP"
+
+msgid "TIME_WAIT TCP"
+msgstr "TIME_WAIT TCP"
+
+msgid "CLOSE_WAIT TCP"
+msgstr "CLOSE_WAIT TCP"
+
+msgid "Total Devices"
+msgstr "设备总数"
+
+msgid "Connection Monitor Disabled"
+msgstr "连接监控未启用"
+
+msgid "Please enable connection monitoring in settings"
+msgstr "请在设置中启用连接监控功能"
+
+msgid "List only shows LAN device connections, data may differ from total connections."
+msgstr "列表只显示局域网设备连接,数据可能和总连接数不一致。"
+
+msgid "TCP Status Details"
+msgstr "TCP 状态详情"
+
+msgid "Basic Settings"
+msgstr "Paramètres de base"
+
+msgid "Traffic Monitor Settings"
+msgstr "Paramètres du moniteur de trafic"
+
+msgid "Connection Monitor Settings"
+msgstr "Paramètres du moniteur de connexion"
+
+msgid "DNS Monitor Settings"
+msgstr "Paramètres du moniteur DNS"
+
+msgid "Bandix Basic Configuration"
+msgstr "Bandix 基本配置"
+
+msgid "Configure basic parameters for Bandix service"
+msgstr "配置 Bandix 服务的基本参数"
+
+msgid "Bandix Traffic Monitor Configuration"
+msgstr "Bandix 流量监控配置"
+
+msgid "Configure traffic monitoring related parameters"
+msgstr "配置流量监控相关参数"
+
+msgid "Bandix Connection Monitor Configuration"
+msgstr "Bandix 连接监控配置"
+
+msgid "Configure connection monitoring related parameters"
+msgstr "配置连接监控相关参数"
+
+msgid "Bandix DNS Monitor Configuration"
+msgstr "Bandix DNS监控配置"
+
+msgid "Configure DNS monitoring related parameters"
+msgstr "配置DNS监控相关参数"
+
+msgid "Enable"
+msgstr "Activer"
+
+msgid "Enable Bandix Traffic Monitor Service"
+msgstr "启用 Bandix 流量监控服务"
+
+msgid "Enable Traffic Monitoring"
+msgstr "Activer la surveillance du trafic"
+
+msgid "Enable Connection Monitoring"
+msgstr "Activer la surveillance des connexions"
+
+msgid "Enable DNS Monitoring"
+msgstr "Activer la surveillance DNS"
+
+msgid "Simplified Chinese"
+msgstr "简体中文"
+
+msgid "Traditional Chinese"
+msgstr "繁体中文"
+
+msgid "Port"
+msgstr "Port"
+
+msgid "Port for Bandix service to listen on"
+msgstr "Bandix 服务监听的端口"
+
+msgid "Monitor Interface"
+msgstr "Interface de surveillance"
+
+msgid "Select the LAN network interface to monitor"
+msgstr "选择要监控的LAN网络接口"
+
+msgid "Speed Units"
+msgstr "Unités de vitesse"
+
+msgid "Select the speed display unit format"
+msgstr "选择网速显示的单位格式"
+
+msgid "Bytes Units (B/s, KB/s, MB/s)"
+msgstr "字节单位 (B/s, KB/s, MB/s)"
+
+msgid "Bits Units (bps, Kbps, Mbps)"
+msgstr "比特单位 (bps, Kbps, Mbps)"
+
+msgid "Interface Theme"
+msgstr "界面主题"
+
+msgid "Select the display theme for Bandix Traffic Monitor"
+msgstr "选择 Bandix 流量监控的显示主题"
+
+msgid "Follow System"
+msgstr "跟随系统"
+
+msgid "Light Mode"
+msgstr "明亮模式"
+
+msgid "Dark Mode"
+msgstr "暗黑模式"
+
+msgid "Feedback"
+msgstr "意见反馈"
+
+msgid "Log Level"
+msgstr "Niveau de journalisation"
+
+msgid "Set the log level for Bandix service"
+msgstr "设置 Bandix 服务的日志级别"
+
+msgid "Offline Timeout"
+msgstr "Délai d'expiration hors ligne"
+
+msgid "Set the timeout for device offline detection (seconds). Devices inactive for longer than this time will be marked as offline"
+msgstr "设置设备离线判断的超时时间(秒)。超过此时间未活动的设备将被标记为离线"
+
+msgid "Traffic History Period"
+msgstr "Période d'historique du trafic"
+
+msgid "10 minutes interval uses about 60 KB per device"
+msgstr "Intervalle de 10 minutes utilise environ 60 Ko par appareil"
+
+msgid "Data Flush Interval"
+msgstr "Intervalle de vidage des données"
+
+msgid "Set the interval for flushing data to disk"
+msgstr "设置数据写入磁盘的时间间隔"
+
+msgid "1 minute"
+msgstr "1 minute"
+
+msgid "5 minutes"
+msgstr "5 minutes"
+
+msgid "10 minutes"
+msgstr "10 minutes"
+
+msgid "15 minutes"
+msgstr "15 分钟"
+
+msgid "20 minutes"
+msgstr "20 分钟"
+
+msgid "25 minutes"
+msgstr "25 分钟"
+
+msgid "30 minutes"
+msgstr "30 分钟"
+
+msgid "1 hour"
+msgstr "1 heure"
+
+msgid "2 hours"
+msgstr "2 heures"
+
+msgid "Data Directory"
+msgstr "Répertoire de données"
+
+msgid "Bandix data storage directory"
+msgstr "Bandix 数据存储目录"
+
+msgid "Enable Bandix connection monitoring"
+msgstr "启用 Bandix 连接监控功能"
+
+msgid "Enable Bandix DNS monitoring"
+msgstr "启用 Bandix DNS监控功能"
+
+msgid "DNS Max Records"
+msgstr "Nombre maximum d'enregistrements DNS"
+
+msgid "Set the maximum number of DNS query records to keep. Older records will be deleted when this limit is exceeded"
+msgstr "设置DNS查询记录的最大保存数量,超过此数量将删除最旧的记录"
+
+msgid "Persist History Data"
+msgstr "Persister les données d'historique"
+
+msgid "Enable data persistence functionality, data will only be persisted to disk when this option is enabled"
+msgstr "启用数据持久化功能,只有启用此选项后才会持久化到磁盘"
\ No newline at end of file
diff --git a/luci-app-bandix/po/id/bandix.po b/luci-app-bandix/po/id/bandix.po
new file mode 100644
index 0000000..6e3bea9
--- /dev/null
+++ b/luci-app-bandix/po/id/bandix.po
@@ -0,0 +1,735 @@
+msgid ""
+msgstr ""
+"Content-Type: text/plain; charset=UTF-8\n"
+
+msgid "Bandix Traffic Monitor"
+msgstr "Monitor Lalu Lintas Bandix"
+
+msgid "Loading data..."
+msgstr "Memuat data..."
+
+msgid "Unable to fetch data"
+msgstr "Tidak dapat mengambil data"
+
+msgid "Unable to fetch history data"
+msgstr "Tidak dapat mengambil data riwayat"
+
+msgid "Hostname"
+msgstr "Nama host"
+
+msgid "IP Address"
+msgstr "Alamat IP"
+
+msgid "MAC Address"
+msgstr "Alamat MAC"
+
+msgid "Download Speed"
+msgstr "Kecepatan unduh"
+
+msgid "Upload Speed"
+msgstr "Kecepatan unggah"
+
+msgid "Total Download"
+msgstr "Total unduhan"
+
+msgid "Total Upload"
+msgstr "Total unggahan"
+
+msgid "Download Limit"
+msgstr "Batas unduhan"
+
+msgid "Upload Limit"
+msgstr "Batas unggahan"
+
+msgid "Interface Language"
+msgstr "Bahasa antarmuka"
+
+msgid "Select the display language for Bandix Traffic Monitor"
+msgstr "Pilih bahasa tampilan untuk Monitor Lalu Lintas Bandix"
+
+msgid "Device Info"
+msgstr "Info perangkat"
+
+msgid "Device List"
+msgstr "Daftar perangkat"
+
+msgid "LAN Traffic"
+msgstr "Lalu lintas LAN"
+
+msgid "WAN Traffic"
+msgstr "Lalu lintas WAN"
+
+msgid "Rate Limit"
+msgstr "Batas kecepatan"
+
+msgid "Actions"
+msgstr "Tindakan"
+
+msgid "Online Devices"
+msgstr "Perangkat online"
+
+msgid "WAN Only"
+msgstr "Hanya WAN"
+
+msgid "Settings"
+msgstr "Pengaturan"
+
+msgid "Device Settings"
+msgstr "Pengaturan perangkat"
+
+msgid "Rate Limits"
+msgstr "Batas kecepatan"
+
+msgid "Remove Rate Limit"
+msgstr "Hapus batas kecepatan"
+
+msgid "Save"
+msgstr "Simpan"
+
+msgid "Cancel"
+msgstr "Batal"
+
+msgid "Set Rate Limit"
+msgstr "Atur batas kecepatan"
+
+msgid "Device"
+msgstr "Perangkat"
+
+msgid "Set Hostname"
+msgstr "Atur nama host"
+
+msgid "Please enter hostname"
+msgstr "Masukkan nama host"
+
+msgid "Hostname set successfully"
+msgstr "Nama host berhasil diatur"
+
+msgid "Failed to set hostname"
+msgstr "Gagal mengatur nama host"
+
+msgid "Unlimited"
+msgstr "Tidak terbatas"
+
+msgid "Settings saved successfully"
+msgstr "Pengaturan berhasil disimpan"
+
+msgid "Failed to save settings"
+msgstr "Gagal menyimpan pengaturan"
+
+msgid "Please enter a valid speed value"
+msgstr "Masukkan nilai kecepatan yang valid"
+
+msgid "Speed value must be greater than 0"
+msgstr "Nilai kecepatan harus lebih besar dari 0"
+
+msgid "Saving..."
+msgstr "Menyimpan..."
+
+msgid "Rate limiting only applies to WAN traffic."
+msgstr "Pembatasan kecepatan hanya berlaku untuk lalu lintas WAN."
+
+msgid "Tip: Enter 0 for unlimited"
+msgstr "Tip: Masukkan 0 untuk tidak terbatas"
+
+msgid "Traffic History"
+msgstr "Riwayat lalu lintas"
+
+msgid "Select Device"
+msgstr "Pilih perangkat"
+
+msgid "All Devices"
+msgstr "Semua perangkat"
+
+msgid "Time Range"
+msgstr "Rentang waktu"
+
+msgid "Last 5 minutes"
+msgstr "5 menit terakhir"
+
+msgid "Last 30 minutes"
+msgstr "30 menit terakhir"
+
+msgid "Last 2 hours"
+msgstr "2 jam terakhir"
+
+msgid "Type"
+msgstr "Jenis"
+
+msgid "Total"
+msgstr "Total"
+
+msgid "LAN"
+msgstr "Lalu lintas LAN"
+
+msgid "WAN"
+msgstr "Lalu lintas WAN"
+
+msgid "Refresh"
+msgstr "Muat ulang"
+
+msgid "Upload Rate"
+msgstr "Tingkat unggahan"
+
+msgid "Download Rate"
+msgstr "Tingkat unduhan"
+
+msgid "Last"
+msgstr "Terakhir"
+
+msgid "second"
+msgstr "detik"
+
+msgid "minute"
+msgstr "menit"
+
+msgid "hour"
+msgstr "jam"
+
+msgid "day"
+msgstr "hari"
+
+msgid "week"
+msgstr "minggu"
+
+msgid "Other Rates"
+msgstr "Tingkat lainnya"
+
+msgid "Cumulative"
+msgstr "Kumulatif"
+
+msgid "Total Uploaded"
+msgstr "Total terunggah"
+
+msgid "Total Downloaded"
+msgstr "Total terunduh"
+
+msgid "LAN Uploaded"
+msgstr "LAN terunggah"
+
+msgid "LAN Downloaded"
+msgstr "LAN terunduh"
+
+msgid "WAN Uploaded"
+msgstr "WAN terunggah"
+
+msgid "WAN Downloaded"
+msgstr "WAN terunduh"
+
+msgid "Total Upload"
+msgstr "Total unggahan"
+
+msgid "Total Download"
+msgstr "Total unduhan"
+
+msgid "LAN Upload"
+msgstr "Unggahan LAN"
+
+msgid "LAN Download"
+msgstr "Unduhan LAN"
+
+msgid "WAN Upload"
+msgstr "Unggahan WAN"
+
+msgid "WAN Download"
+msgstr "Unduhan WAN"
+
+msgid "Never Online"
+msgstr "Tidak pernah online"
+
+msgid "Just Now"
+msgstr "Baru saja"
+
+msgid "min ago"
+msgstr "menit yang lalu"
+
+msgid "h ago"
+msgstr "jam yang lalu"
+
+msgid "days ago"
+msgstr "hari yang lalu"
+
+msgid "months ago"
+msgstr "bulan yang lalu"
+
+msgid "years ago"
+msgstr "tahun yang lalu"
+
+msgid "Last Online"
+msgstr "Terakhir online"
+
+msgid "Zoom"
+msgstr "Perbesar"
+
+msgid "Sort By"
+msgstr "Urutkan berdasarkan"
+
+msgid "Online Status"
+msgstr "Status online"
+
+msgid "Total Traffic"
+msgstr "Total lalu lintas"
+
+msgid "Ascending"
+msgstr "Naik"
+
+msgid "Descending"
+msgstr "Turun"
+
+msgid "Sort by Speed"
+msgstr "Urutkan berdasarkan kecepatan"
+
+msgid "Sort by Traffic"
+msgstr "Urutkan berdasarkan lalu lintas"
+
+msgid "Simple Mode"
+msgstr "Mode sederhana"
+
+msgid "Detailed Mode"
+msgstr "Mode detail"
+
+msgid "Bandix DNS Monitor"
+msgstr "Monitor DNS Bandix"
+
+msgid "DNS Monitor"
+msgstr "Monitor DNS"
+
+msgid "DNS Query Records"
+msgstr "Catatan kueri DNS"
+
+msgid "DNS Statistics"
+msgstr "Statistik DNS"
+
+msgid "DNS Monitoring Disabled"
+msgstr "Pemantauan DNS dinonaktifkan"
+
+msgid "Please enable DNS monitoring in settings"
+msgstr "Harap aktifkan pemantauan DNS di pengaturan"
+
+msgid "Go to Settings"
+msgstr "Buka pengaturan"
+
+msgid "No Data"
+msgstr "Tidak ada data"
+
+msgid "Time"
+msgstr "Waktu"
+
+msgid "Domain"
+msgstr "Domain"
+
+msgid "Query Type"
+msgstr "Jenis kueri"
+
+msgid "Response Code"
+msgstr "Kode respons"
+
+msgid "Response Time"
+msgstr "Waktu respons"
+
+msgid "Source IP"
+msgstr "IP sumber"
+
+msgid "Destination IP"
+msgstr "IP tujuan"
+
+msgid "Response IPs"
+msgstr "IP respons"
+
+msgid "Response Result"
+msgstr "Hasil respons"
+
+msgid "DNS Server"
+msgstr "Server DNS"
+
+msgid "Query"
+msgstr "Kueri"
+
+msgid "Response"
+msgstr "Respons"
+
+msgid "Filter"
+msgstr "Filter"
+
+msgid "Domain Filter"
+msgstr "Filter domain"
+
+msgid "Device Filter"
+msgstr "Filter perangkat"
+
+msgid "DNS Server Filter"
+msgstr "Filter server DNS"
+
+msgid "Type Filter"
+msgstr "Filter jenis"
+
+msgid "All"
+msgstr "Semua"
+
+msgid "Queries Only"
+msgstr "Hanya kueri"
+
+msgid "Responses Only"
+msgstr "Hanya respons"
+
+msgid "Search"
+msgstr "Cari"
+
+msgid "Search Domain"
+msgstr "Cari domain"
+
+msgid "Search Device"
+msgstr "Cari perangkat"
+
+msgid "Search DNS Server"
+msgstr "Cari server DNS"
+
+msgid "Clear"
+msgstr "Hapus"
+
+msgid "Previous"
+msgstr "Sebelumnya"
+
+msgid "Next"
+msgstr "Selanjutnya"
+
+msgid "Page"
+msgstr "Halaman"
+
+msgid "of"
+msgstr "dari"
+
+msgid "Total"
+msgstr "Total"
+
+msgid "records"
+msgstr "catatan"
+
+msgid "Per Page"
+msgstr "Per halaman"
+
+msgid ""
+msgstr ""
+
+msgid "Total Queries"
+msgstr "Total kueri"
+
+msgid "Total Responses"
+msgstr "Total respons"
+
+msgid "Queries with Response"
+msgstr "Kueri dengan respons"
+
+msgid "Queries without Response"
+msgstr "Kueri tanpa respons"
+
+msgid "Avg Response Time"
+msgstr "Waktu respons rata-rata"
+
+msgid "Min Response Time"
+msgstr "Waktu respons minimum"
+
+msgid "Max Response Time"
+msgstr "Waktu respons maksimum"
+
+msgid "Success Rate"
+msgstr "Tingkat keberhasilan"
+
+msgid "Success"
+msgstr "Berhasil"
+
+msgid "Failure"
+msgstr "Gagal"
+
+msgid "Top Domains"
+msgstr "Domain teratas"
+
+msgid "Top Query Types"
+msgstr "Jenis kueri teratas"
+
+msgid "Top Devices"
+msgstr "Perangkat teratas"
+
+msgid "Top DNS Servers"
+msgstr "Server DNS teratas"
+
+msgid "Unique Devices"
+msgstr "Perangkat unik"
+
+msgid "ms"
+msgstr "ms"
+
+msgid "minutes"
+msgstr "menit"
+
+msgid "Unknown Device"
+msgstr "Perangkat tidak dikenal"
+
+msgid "Domain not found"
+msgstr "Domain tidak ditemukan"
+
+msgid "Server error"
+msgstr "Kesalahan server"
+
+msgid "Format error"
+msgstr "Kesalahan format"
+
+msgid "Refused"
+msgstr "Ditolak"
+
+msgid "Other"
+msgstr "Lainnya"
+
+msgid "Does not include DoH and DoT"
+msgstr "Tidak termasuk DoH dan DoT"
+
+msgid "Latest Response Time"
+msgstr "Waktu respons terbaru"
+
+msgid "Response Code Distribution"
+msgstr "Distribusi kode respons"
+
+msgid "Response Time Percentiles"
+msgstr "Persentil waktu respons"
+
+msgid "P50"
+msgstr "P50"
+
+msgid "P90"
+msgstr "P90"
+
+msgid "P95"
+msgstr "P95"
+
+msgid "P99"
+msgstr "P99"
+
+msgid "Bandix Connection Monitor"
+msgstr "Monitor Koneksi Bandix"
+
+msgid "Connection Monitor"
+msgstr "Monitor koneksi"
+
+msgid "Device Connection Statistics"
+msgstr "Statistik koneksi perangkat"
+
+msgid "Global Connection Statistics"
+msgstr "Statistik koneksi global"
+
+msgid "Active TCP"
+msgstr "TCP aktif"
+
+msgid "Active UDP"
+msgstr "UDP aktif"
+
+msgid "Closed TCP"
+msgstr "TCP tertutup"
+
+msgid "Total Connections"
+msgstr "Total koneksi"
+
+msgid "Last Updated"
+msgstr "Terakhir diperbarui"
+
+msgid "TCP Connections"
+msgstr "Koneksi TCP"
+
+msgid "UDP Connections"
+msgstr "Koneksi UDP"
+
+msgid "Established TCP"
+msgstr "TCP terbentuk"
+
+msgid "TIME_WAIT TCP"
+msgstr "TIME_WAIT TCP"
+
+msgid "CLOSE_WAIT TCP"
+msgstr "CLOSE_WAIT TCP"
+
+msgid "Total Devices"
+msgstr "Total perangkat"
+
+msgid "Connection Monitor Disabled"
+msgstr "Monitor koneksi dinonaktifkan"
+
+msgid "Please enable connection monitoring in settings"
+msgstr "Harap aktifkan pemantauan koneksi di pengaturan"
+
+msgid "List only shows LAN device connections, data may differ from total connections."
+msgstr "Daftar hanya menampilkan koneksi perangkat LAN, data mungkin berbeda dari total koneksi."
+
+msgid "TCP Status Details"
+msgstr "Detail status TCP"
+
+msgid "Basic Settings"
+msgstr "Pengaturan dasar"
+
+msgid "Traffic Monitor Settings"
+msgstr "Pengaturan monitor lalu lintas"
+
+msgid "Connection Monitor Settings"
+msgstr "Pengaturan monitor koneksi"
+
+msgid "DNS Monitor Settings"
+msgstr "Pengaturan monitor DNS"
+
+msgid "Bandix Basic Configuration"
+msgstr "Konfigurasi dasar Bandix"
+
+msgid "Configure basic parameters for Bandix service"
+msgstr "Konfigurasi parameter dasar untuk layanan Bandix"
+
+msgid "Bandix Traffic Monitor Configuration"
+msgstr "Konfigurasi Monitor Lalu Lintas Bandix"
+
+msgid "Configure traffic monitoring related parameters"
+msgstr "Konfigurasi parameter terkait pemantauan lalu lintas"
+
+msgid "Bandix Connection Monitor Configuration"
+msgstr "Konfigurasi Monitor Koneksi Bandix"
+
+msgid "Configure connection monitoring related parameters"
+msgstr "Konfigurasi parameter terkait pemantauan koneksi"
+
+msgid "Bandix DNS Monitor Configuration"
+msgstr "Konfigurasi Monitor DNS Bandix"
+
+msgid "Configure DNS monitoring related parameters"
+msgstr "Konfigurasi parameter terkait pemantauan DNS"
+
+msgid "Enable"
+msgstr "Aktifkan"
+
+msgid "Enable Bandix Traffic Monitor Service"
+msgstr "Aktifkan layanan Monitor Lalu Lintas Bandix"
+
+msgid "Enable Traffic Monitoring"
+msgstr "Aktifkan pemantauan lalu lintas"
+
+msgid "Enable Connection Monitoring"
+msgstr "Aktifkan pemantauan koneksi"
+
+msgid "Enable DNS Monitoring"
+msgstr "Aktifkan pemantauan DNS"
+
+msgid "Simplified Chinese"
+msgstr "Bahasa Cina Sederhana"
+
+msgid "Traditional Chinese"
+msgstr "Bahasa Cina Tradisional"
+
+msgid "Port"
+msgstr "Port"
+
+msgid "Port for Bandix service to listen on"
+msgstr "Port untuk layanan Bandix mendengarkan"
+
+msgid "Monitor Interface"
+msgstr "Antarmuka monitor"
+
+msgid "Select the LAN network interface to monitor"
+msgstr "Pilih antarmuka jaringan LAN untuk dipantau"
+
+msgid "Speed Units"
+msgstr "Satuan kecepatan"
+
+msgid "Select the speed display unit format"
+msgstr "Pilih format satuan tampilan kecepatan"
+
+msgid "Bytes Units (B/s, KB/s, MB/s)"
+msgstr "Satuan byte (B/s, KB/s, MB/s)"
+
+msgid "Bits Units (bps, Kbps, Mbps)"
+msgstr "Satuan bit (bps, Kbps, Mbps)"
+
+msgid "Interface Theme"
+msgstr "Tema antarmuka"
+
+msgid "Select the display theme for Bandix Traffic Monitor"
+msgstr "Pilih tema tampilan untuk Monitor Lalu Lintas Bandix"
+
+msgid "Follow System"
+msgstr "Ikuti sistem"
+
+msgid "Light Mode"
+msgstr "Mode terang"
+
+msgid "Dark Mode"
+msgstr "Mode gelap"
+
+msgid "Feedback"
+msgstr "Umpan balik"
+
+msgid "Log Level"
+msgstr "Tingkat log"
+
+msgid "Set the log level for Bandix service"
+msgstr "Atur tingkat log untuk layanan Bandix"
+
+msgid "Offline Timeout"
+msgstr "Timeout offline"
+
+msgid "Set the timeout for device offline detection (seconds). Devices inactive for longer than this time will be marked as offline"
+msgstr "Atur timeout untuk deteksi perangkat offline (detik). Perangkat yang tidak aktif lebih lama dari waktu ini akan ditandai sebagai offline"
+
+msgid "Traffic History Period"
+msgstr "Periode riwayat lalu lintas"
+
+msgid "10 minutes interval uses about 60 KB per device"
+msgstr "Interval 10 menit menggunakan sekitar 60 KB per perangkat"
+
+msgid "Data Flush Interval"
+msgstr "Interval flush data"
+
+msgid "Set the interval for flushing data to disk"
+msgstr "Atur interval untuk mem-flush data ke disk"
+
+msgid "1 minute"
+msgstr "1 menit"
+
+msgid "5 minutes"
+msgstr "5 menit"
+
+msgid "10 minutes"
+msgstr "10 menit"
+
+msgid "15 minutes"
+msgstr "15 menit"
+
+msgid "20 minutes"
+msgstr "20 menit"
+
+msgid "25 minutes"
+msgstr "25 menit"
+
+msgid "30 minutes"
+msgstr "30 menit"
+
+msgid "1 hour"
+msgstr "1 jam"
+
+msgid "2 hours"
+msgstr "2 jam"
+
+msgid "Data Directory"
+msgstr "Direktori data"
+
+msgid "Bandix data storage directory"
+msgstr "Direktori penyimpanan data Bandix"
+
+msgid "Enable Bandix connection monitoring"
+msgstr "Aktifkan pemantauan koneksi Bandix"
+
+msgid "Enable Bandix DNS monitoring"
+msgstr "Aktifkan pemantauan DNS Bandix"
+
+msgid "DNS Max Records"
+msgstr "Maksimum catatan DNS"
+
+msgid "Set the maximum number of DNS query records to keep. Older records will be deleted when this limit is exceeded"
+msgstr "Atur jumlah maksimum catatan kueri DNS yang disimpan. Catatan lama akan dihapus ketika batas ini terlampaui"
+
+msgid "Persist History Data"
+msgstr "Simpan data riwayat"
+
+msgid "Enable data persistence functionality, data will only be persisted to disk when this option is enabled"
+msgstr "Aktifkan fungsi persistensi data, data hanya akan disimpan ke disk ketika opsi ini diaktifkan"
diff --git a/luci-app-bandix/po/ja/bandix.po b/luci-app-bandix/po/ja/bandix.po
new file mode 100644
index 0000000..7d827fe
--- /dev/null
+++ b/luci-app-bandix/po/ja/bandix.po
@@ -0,0 +1,735 @@
+msgid ""
+msgstr ""
+"Content-Type: text/plain; charset=UTF-8\n"
+
+msgid "Bandix Traffic Monitor"
+msgstr "Bandix トラフィックモニター"
+
+msgid "Loading data..."
+msgstr "データを読み込み中..."
+
+msgid "Unable to fetch data"
+msgstr "データを取得できません"
+
+msgid "Unable to fetch history data"
+msgstr "履歴データを取得できません"
+
+msgid "Hostname"
+msgstr "ホスト名"
+
+msgid "IP Address"
+msgstr "IPアドレス"
+
+msgid "MAC Address"
+msgstr "MACアドレス"
+
+msgid "Download Speed"
+msgstr "ダウンロード速度"
+
+msgid "Upload Speed"
+msgstr "アップロード速度"
+
+msgid "Total Download"
+msgstr "総ダウンロード量"
+
+msgid "Total Upload"
+msgstr "総アップロード量"
+
+msgid "Download Limit"
+msgstr "ダウンロード制限"
+
+msgid "Upload Limit"
+msgstr "アップロード制限"
+
+msgid "Interface Language"
+msgstr "インターフェース言語"
+
+msgid "Select the display language for Bandix Traffic Monitor"
+msgstr "Bandix トラフィックモニターの表示言語を選択"
+
+msgid "Device Info"
+msgstr "デバイス情報"
+
+msgid "Device List"
+msgstr "デバイス一覧"
+
+msgid "LAN Traffic"
+msgstr "LAN トラフィック"
+
+msgid "WAN Traffic"
+msgstr "WAN トラフィック"
+
+msgid "Rate Limit"
+msgstr "速度制限"
+
+msgid "Actions"
+msgstr "操作"
+
+msgid "Online Devices"
+msgstr "オンラインデバイス"
+
+msgid "WAN Only"
+msgstr "WAN トラフィックのみ"
+
+msgid "Settings"
+msgstr "設定"
+
+msgid "Device Settings"
+msgstr "デバイス設定"
+
+msgid "Rate Limits"
+msgstr "速度制限"
+
+msgid "Remove Rate Limit"
+msgstr "速度制限を解除"
+
+msgid "Save"
+msgstr "保存"
+
+msgid "Cancel"
+msgstr "キャンセル"
+
+msgid "Set Rate Limit"
+msgstr "速度制限を設定"
+
+msgid "Device"
+msgstr "デバイス"
+
+msgid "Set Hostname"
+msgstr "ホスト名を設定"
+
+msgid "Please enter hostname"
+msgstr "ホスト名を入力してください"
+
+msgid "Hostname set successfully"
+msgstr "ホスト名の設定に成功しました"
+
+msgid "Failed to set hostname"
+msgstr "ホスト名の設定に失敗しました"
+
+msgid "Unlimited"
+msgstr "無制限"
+
+msgid "Settings saved successfully"
+msgstr "設定を保存しました"
+
+msgid "Failed to save settings"
+msgstr "設定の保存に失敗しました"
+
+msgid "Please enter a valid speed value"
+msgstr "有効な速度値を入力してください"
+
+msgid "Speed value must be greater than 0"
+msgstr "速度値は0より大きい必要があります"
+
+msgid "Saving..."
+msgstr "保存中..."
+
+msgid "Rate limiting only applies to WAN traffic."
+msgstr "速度制限は WAN トラフィックにのみ適用されます。"
+
+msgid "Tip: Enter 0 for unlimited"
+msgstr "ヒント:0を入力すると無制限になります"
+
+msgid "Traffic History"
+msgstr "トラフィック履歴"
+
+msgid "Select Device"
+msgstr "デバイスを選択"
+
+msgid "All Devices"
+msgstr "すべてのデバイス"
+
+msgid "Time Range"
+msgstr "時間範囲"
+
+msgid "Last 5 minutes"
+msgstr "過去5分"
+
+msgid "Last 30 minutes"
+msgstr "過去30分"
+
+msgid "Last 2 hours"
+msgstr "過去2時間"
+
+msgid "Type"
+msgstr "タイプ"
+
+msgid "Total"
+msgstr "合計"
+
+msgid "LAN"
+msgstr "LAN トラフィック"
+
+msgid "WAN"
+msgstr "WAN トラフィック"
+
+msgid "Refresh"
+msgstr "更新"
+
+msgid "Upload Rate"
+msgstr "アップロード速度"
+
+msgid "Download Rate"
+msgstr "ダウンロード速度"
+
+msgid "Last"
+msgstr "最後"
+
+msgid "second"
+msgstr "秒"
+
+msgid "minute"
+msgstr "分"
+
+msgid "hour"
+msgstr "時間"
+
+msgid "day"
+msgstr "日"
+
+msgid "week"
+msgstr "週"
+
+msgid "Other Rates"
+msgstr "その他の速度"
+
+msgid "Cumulative"
+msgstr "累計トラフィック"
+
+msgid "Total Uploaded"
+msgstr "総アップロード"
+
+msgid "Total Downloaded"
+msgstr "総ダウンロード"
+
+msgid "LAN Uploaded"
+msgstr "LAN アップロード済み"
+
+msgid "LAN Downloaded"
+msgstr "LAN ダウンロード済み"
+
+msgid "WAN Uploaded"
+msgstr "WAN アップロード済み"
+
+msgid "WAN Downloaded"
+msgstr "WAN ダウンロード済み"
+
+msgid "Total Upload"
+msgstr "総アップロード速度"
+
+msgid "Total Download"
+msgstr "総ダウンロード速度"
+
+msgid "LAN Upload"
+msgstr "LAN アップロード速度"
+
+msgid "LAN Download"
+msgstr "LAN ダウンロード速度"
+
+msgid "WAN Upload"
+msgstr "WAN アップロード速度"
+
+msgid "WAN Download"
+msgstr "WAN ダウンロード速度"
+
+msgid "Never Online"
+msgstr "オンラインになったことがありません"
+
+msgid "Just Now"
+msgstr "たった今"
+
+msgid "min ago"
+msgstr "分前"
+
+msgid "h ago"
+msgstr "時間前"
+
+msgid "days ago"
+msgstr "日前"
+
+msgid "months ago"
+msgstr "ヶ月前"
+
+msgid "years ago"
+msgstr "年前"
+
+msgid "Last Online"
+msgstr "最終オンライン"
+
+msgid "Zoom"
+msgstr "ズーム"
+
+msgid "Sort By"
+msgstr "並び替え"
+
+msgid "Online Status"
+msgstr "オンライン状態"
+
+msgid "Total Traffic"
+msgstr "総トラフィック"
+
+msgid "Ascending"
+msgstr "昇順"
+
+msgid "Descending"
+msgstr "降順"
+
+msgid "Sort by Speed"
+msgstr "速度で並び替え"
+
+msgid "Sort by Traffic"
+msgstr "トラフィックで並び替え"
+
+msgid "Simple Mode"
+msgstr "シンプルモード"
+
+msgid "Detailed Mode"
+msgstr "詳細モード"
+
+msgid "Bandix DNS Monitor"
+msgstr "Bandix DNS モニター"
+
+msgid "DNS Monitor"
+msgstr "DNS モニター"
+
+msgid "DNS Query Records"
+msgstr "DNS クエリ記録"
+
+msgid "DNS Statistics"
+msgstr "DNS 統計情報"
+
+msgid "DNS Monitoring Disabled"
+msgstr "DNS監視が無効です"
+
+msgid "Please enable DNS monitoring in settings"
+msgstr "設定でDNS監視を有効にしてください"
+
+msgid "Go to Settings"
+msgstr "設定に移動"
+
+msgid "No Data"
+msgstr "データなし"
+
+msgid "Time"
+msgstr "時間"
+
+msgid "Domain"
+msgstr "ドメイン"
+
+msgid "Query Type"
+msgstr "クエリタイプ"
+
+msgid "Response Code"
+msgstr "応答コード"
+
+msgid "Response Time"
+msgstr "応答時間"
+
+msgid "Source IP"
+msgstr "送信元IP"
+
+msgid "Destination IP"
+msgstr "宛先IP"
+
+msgid "Response IPs"
+msgstr "応答IP"
+
+msgid "Response Result"
+msgstr "応答結果"
+
+msgid "DNS Server"
+msgstr "DNSサーバー"
+
+msgid "Query"
+msgstr "クエリ"
+
+msgid "Response"
+msgstr "応答"
+
+msgid "Filter"
+msgstr "フィルター"
+
+msgid "Domain Filter"
+msgstr "ドメインフィルター"
+
+msgid "Device Filter"
+msgstr "デバイスフィルター"
+
+msgid "DNS Server Filter"
+msgstr "DNSサーバーフィルター"
+
+msgid "Type Filter"
+msgstr "タイプフィルター"
+
+msgid "All"
+msgstr "すべて"
+
+msgid "Queries Only"
+msgstr "クエリのみ"
+
+msgid "Responses Only"
+msgstr "応答のみ"
+
+msgid "Search"
+msgstr "検索"
+
+msgid "Search Domain"
+msgstr "ドメインを検索"
+
+msgid "Search Device"
+msgstr "デバイスを検索"
+
+msgid "Search DNS Server"
+msgstr "DNSサーバーを検索"
+
+msgid "Clear"
+msgstr "クリア"
+
+msgid "Previous"
+msgstr "前へ"
+
+msgid "Next"
+msgstr "次へ"
+
+msgid "Page"
+msgstr "ページ"
+
+msgid "of"
+msgstr "の"
+
+msgid "Total"
+msgstr "合計"
+
+msgid "records"
+msgstr "件の記録"
+
+msgid "Per Page"
+msgstr "ページあたり"
+
+msgid ""
+msgstr "件"
+
+msgid "Total Queries"
+msgstr "総クエリ数"
+
+msgid "Total Responses"
+msgstr "総応答数"
+
+msgid "Queries with Response"
+msgstr "応答ありクエリ"
+
+msgid "Queries without Response"
+msgstr "応答なしクエリ"
+
+msgid "Avg Response Time"
+msgstr "平均応答時間"
+
+msgid "Min Response Time"
+msgstr "最短応答時間"
+
+msgid "Max Response Time"
+msgstr "最長応答時間"
+
+msgid "Success Rate"
+msgstr "成功率"
+
+msgid "Success"
+msgstr "成功"
+
+msgid "Failure"
+msgstr "失敗"
+
+msgid "Top Domains"
+msgstr "最もクエリされたドメイン"
+
+msgid "Top Query Types"
+msgstr "最も使用されたクエリタイプ"
+
+msgid "Top Devices"
+msgstr "最もアクティブなデバイス"
+
+msgid "Top DNS Servers"
+msgstr "最も使用されたDNSサーバー"
+
+msgid "Unique Devices"
+msgstr "ユニークデバイス数"
+
+msgid "ms"
+msgstr "ミリ秒"
+
+msgid "minutes"
+msgstr "分"
+
+msgid "Unknown Device"
+msgstr "不明なデバイス"
+
+msgid "Domain not found"
+msgstr "ドメインが見つかりません"
+
+msgid "Server error"
+msgstr "サーバーエラー"
+
+msgid "Format error"
+msgstr "フォーマットエラー"
+
+msgid "Refused"
+msgstr "拒否"
+
+msgid "Other"
+msgstr "その他"
+
+msgid "Does not include DoH and DoT"
+msgstr "DoH と DoT は含まれません"
+
+msgid "Latest Response Time"
+msgstr "最新の応答時間"
+
+msgid "Response Code Distribution"
+msgstr "応答コード分布"
+
+msgid "Response Time Percentiles"
+msgstr "応答時間パーセンタイル"
+
+msgid "P50"
+msgstr "P50"
+
+msgid "P90"
+msgstr "P90"
+
+msgid "P95"
+msgstr "P95"
+
+msgid "P99"
+msgstr "P99"
+
+msgid "Bandix Connection Monitor"
+msgstr "Bandix 接続モニター"
+
+msgid "Connection Monitor"
+msgstr "接続モニター"
+
+msgid "Device Connection Statistics"
+msgstr "デバイス接続統計"
+
+msgid "Global Connection Statistics"
+msgstr "グローバル接続統計"
+
+msgid "Active TCP"
+msgstr "アクティブTCP"
+
+msgid "Active UDP"
+msgstr "アクティブUDP"
+
+msgid "Closed TCP"
+msgstr "クローズTCP"
+
+msgid "Total Connections"
+msgstr "総接続数"
+
+msgid "Last Updated"
+msgstr "最終更新"
+
+msgid "TCP Connections"
+msgstr "TCP接続数"
+
+msgid "UDP Connections"
+msgstr "UDP接続数"
+
+msgid "Established TCP"
+msgstr "確立済みTCP"
+
+msgid "TIME_WAIT TCP"
+msgstr "TIME_WAIT TCP"
+
+msgid "CLOSE_WAIT TCP"
+msgstr "CLOSE_WAIT TCP"
+
+msgid "Total Devices"
+msgstr "デバイス総数"
+
+msgid "Connection Monitor Disabled"
+msgstr "接続モニターが無効です"
+
+msgid "Please enable connection monitoring in settings"
+msgstr "設定で接続モニターを有効にしてください"
+
+msgid "List only shows LAN device connections, data may differ from total connections."
+msgstr "リストはLANデバイス接続のみを表示します。データは総接続数と異なる場合があります。"
+
+msgid "TCP Status Details"
+msgstr "TCP 状態詳細"
+
+msgid "Basic Settings"
+msgstr "基本設定"
+
+msgid "Traffic Monitor Settings"
+msgstr "トラフィックモニター設定"
+
+msgid "Connection Monitor Settings"
+msgstr "接続モニター設定"
+
+msgid "DNS Monitor Settings"
+msgstr "DNSモニター設定"
+
+msgid "Bandix Basic Configuration"
+msgstr "Bandix 基本設定"
+
+msgid "Configure basic parameters for Bandix service"
+msgstr "Bandix サービスの基本パラメータを設定"
+
+msgid "Bandix Traffic Monitor Configuration"
+msgstr "Bandix トラフィックモニター設定"
+
+msgid "Configure traffic monitoring related parameters"
+msgstr "トラフィック監視関連のパラメータを設定"
+
+msgid "Bandix Connection Monitor Configuration"
+msgstr "Bandix 接続モニター設定"
+
+msgid "Configure connection monitoring related parameters"
+msgstr "接続監視関連のパラメータを設定"
+
+msgid "Bandix DNS Monitor Configuration"
+msgstr "Bandix DNSモニター設定"
+
+msgid "Configure DNS monitoring related parameters"
+msgstr "DNS監視関連のパラメータを設定"
+
+msgid "Enable"
+msgstr "有効"
+
+msgid "Enable Bandix Traffic Monitor Service"
+msgstr "Bandix トラフィックモニターサービスを有効にする"
+
+msgid "Enable Traffic Monitoring"
+msgstr "トラフィック監視を有効にする"
+
+msgid "Enable Connection Monitoring"
+msgstr "接続監視を有効にする"
+
+msgid "Enable DNS Monitoring"
+msgstr "DNS監視を有効にする"
+
+msgid "Simplified Chinese"
+msgstr "簡体字中国語"
+
+msgid "Traditional Chinese"
+msgstr "繁体字中国語"
+
+msgid "Port"
+msgstr "ポート"
+
+msgid "Port for Bandix service to listen on"
+msgstr "Bandix サービスがリッスンするポート"
+
+msgid "Monitor Interface"
+msgstr "監視インターフェース"
+
+msgid "Select the LAN network interface to monitor"
+msgstr "監視するLANネットワークインターフェースを選択"
+
+msgid "Speed Units"
+msgstr "速度単位"
+
+msgid "Select the speed display unit format"
+msgstr "速度表示の単位形式を選択"
+
+msgid "Bytes Units (B/s, KB/s, MB/s)"
+msgstr "バイト単位 (B/s, KB/s, MB/s)"
+
+msgid "Bits Units (bps, Kbps, Mbps)"
+msgstr "ビット単位 (bps, Kbps, Mbps)"
+
+msgid "Interface Theme"
+msgstr "インターフェーステーマ"
+
+msgid "Select the display theme for Bandix Traffic Monitor"
+msgstr "Bandix トラフィックモニターの表示テーマを選択"
+
+msgid "Follow System"
+msgstr "システムに従う"
+
+msgid "Light Mode"
+msgstr "ライトモード"
+
+msgid "Dark Mode"
+msgstr "ダークモード"
+
+msgid "Feedback"
+msgstr "フィードバック"
+
+msgid "Log Level"
+msgstr "ログレベル"
+
+msgid "Set the log level for Bandix service"
+msgstr "Bandix サービスのログレベルを設定"
+
+msgid "Offline Timeout"
+msgstr "オフラインタイムアウト"
+
+msgid "Set the timeout for device offline detection (seconds). Devices inactive for longer than this time will be marked as offline"
+msgstr "デバイスのオフライン検出のタイムアウトを設定(秒)。この時間より長く非アクティブなデバイスはオフラインとしてマークされます"
+
+msgid "Traffic History Period"
+msgstr "トラフィック履歴期間"
+
+msgid "10 minutes interval uses about 60 KB per device"
+msgstr "10分間隔でデバイスあたり約60KBを使用"
+
+msgid "Data Flush Interval"
+msgstr "データフラッシュ間隔"
+
+msgid "Set the interval for flushing data to disk"
+msgstr "データをディスクにフラッシュする間隔を設定"
+
+msgid "1 minute"
+msgstr "1分"
+
+msgid "5 minutes"
+msgstr "5分"
+
+msgid "10 minutes"
+msgstr "10分"
+
+msgid "15 minutes"
+msgstr "15分"
+
+msgid "20 minutes"
+msgstr "20分"
+
+msgid "25 minutes"
+msgstr "25分"
+
+msgid "30 minutes"
+msgstr "30分"
+
+msgid "1 hour"
+msgstr "1時間"
+
+msgid "2 hours"
+msgstr "2時間"
+
+msgid "Data Directory"
+msgstr "データディレクトリ"
+
+msgid "Bandix data storage directory"
+msgstr "Bandix データストレージディレクトリ"
+
+msgid "Enable Bandix connection monitoring"
+msgstr "Bandix 接続監視を有効にする"
+
+msgid "Enable Bandix DNS monitoring"
+msgstr "Bandix DNS監視を有効にする"
+
+msgid "DNS Max Records"
+msgstr "DNS最大記録数"
+
+msgid "Set the maximum number of DNS query records to keep. Older records will be deleted when this limit is exceeded"
+msgstr "保持するDNSクエリ記録の最大数を設定。この制限を超えると古い記録が削除されます"
+
+msgid "Persist History Data"
+msgstr "履歴データを永続化"
+
+msgid "Enable data persistence functionality, data will only be persisted to disk when this option is enabled"
+msgstr "データ永続化機能を有効にする。このオプションが有効な場合のみデータがディスクに永続化されます"
diff --git a/luci-app-bandix/po/ru/bandix.po b/luci-app-bandix/po/ru/bandix.po
new file mode 100644
index 0000000..ed3c83c
--- /dev/null
+++ b/luci-app-bandix/po/ru/bandix.po
@@ -0,0 +1,735 @@
+msgid ""
+msgstr ""
+"Content-Type: text/plain; charset=UTF-8\n"
+
+msgid "Bandix Traffic Monitor"
+msgstr "Монитор трафика Bandix"
+
+msgid "Loading data..."
+msgstr "Загрузка данных..."
+
+msgid "Unable to fetch data"
+msgstr "Не удалось получить данные"
+
+msgid "Unable to fetch history data"
+msgstr "无法获取历史数据"
+
+msgid "Hostname"
+msgstr "Имя хоста"
+
+msgid "IP Address"
+msgstr "IP-адрес"
+
+msgid "MAC Address"
+msgstr "MAC-адрес"
+
+msgid "Download Speed"
+msgstr "Скорость загрузки"
+
+msgid "Upload Speed"
+msgstr "Скорость выгрузки"
+
+msgid "Total Download"
+msgstr "Всего загружено"
+
+msgid "Total Upload"
+msgstr "Всего выгружено"
+
+msgid "Download Limit"
+msgstr "Ограничение загрузки"
+
+msgid "Upload Limit"
+msgstr "Ограничение выгрузки"
+
+msgid "Interface Language"
+msgstr "Язык интерфейса"
+
+msgid "Select the display language for Bandix Traffic Monitor"
+msgstr "Выберите язык отображения для монитора трафика Bandix"
+
+msgid "Device Info"
+msgstr "Информация об устройстве"
+
+msgid "Device List"
+msgstr "Список устройств"
+
+msgid "LAN Traffic"
+msgstr "Трафик LAN"
+
+msgid "WAN Traffic"
+msgstr "Трафик WAN"
+
+msgid "Rate Limit"
+msgstr "Ограничение скорости"
+
+msgid "Actions"
+msgstr "Действия"
+
+msgid "Online Devices"
+msgstr "Устройства в сети"
+
+msgid "WAN Only"
+msgstr "仅限WAN 流量"
+
+msgid "Settings"
+msgstr "Настройки"
+
+msgid "Device Settings"
+msgstr "Настройки устройства"
+
+msgid "Rate Limits"
+msgstr "限速设置"
+
+msgid "Remove Rate Limit"
+msgstr "取消限速"
+
+msgid "Save"
+msgstr "Сохранить"
+
+msgid "Cancel"
+msgstr "Отмена"
+
+msgid "Set Rate Limit"
+msgstr "设置限速"
+
+msgid "Device"
+msgstr "Устройство"
+
+msgid "Set Hostname"
+msgstr "设置主机名"
+
+msgid "Please enter hostname"
+msgstr "请输入主机名"
+
+msgid "Hostname set successfully"
+msgstr "主机名设置成功"
+
+msgid "Failed to set hostname"
+msgstr "主机名设置失败"
+
+msgid "Unlimited"
+msgstr "Без ограничений"
+
+msgid "Settings saved successfully"
+msgstr "Настройки успешно сохранены"
+
+msgid "Failed to save settings"
+msgstr "Не удалось сохранить настройки"
+
+msgid "Please enter a valid speed value"
+msgstr "请输入有效的速度值"
+
+msgid "Speed value must be greater than 0"
+msgstr "速度值必须大于0"
+
+msgid "Saving..."
+msgstr "Сохранение..."
+
+msgid "Rate limiting only applies to WAN traffic."
+msgstr "限速功能仅对 WAN 流量生效。"
+
+msgid "Tip: Enter 0 for unlimited"
+msgstr "提示:输入 0 表示无限制"
+
+msgid "Traffic History"
+msgstr "История трафика"
+
+msgid "Select Device"
+msgstr "Выбрать устройство"
+
+msgid "All Devices"
+msgstr "Все устройства"
+
+msgid "Time Range"
+msgstr "时间范围"
+
+msgid "Last 5 minutes"
+msgstr "最近5分钟"
+
+msgid "Last 30 minutes"
+msgstr "最近30分钟"
+
+msgid "Last 2 hours"
+msgstr "最近2小时"
+
+msgid "Type"
+msgstr "Тип"
+
+msgid "Total"
+msgstr "Всего"
+
+msgid "LAN"
+msgstr "LAN 流量"
+
+msgid "WAN"
+msgstr "WAN 流量"
+
+msgid "Refresh"
+msgstr "Обновить"
+
+msgid "Upload Rate"
+msgstr "Скорость выгрузки"
+
+msgid "Download Rate"
+msgstr "Скорость загрузки"
+
+msgid "Last"
+msgstr "最近"
+
+msgid "second"
+msgstr "秒"
+
+msgid "minute"
+msgstr "分钟"
+
+msgid "hour"
+msgstr "小时"
+
+msgid "day"
+msgstr "天"
+
+msgid "week"
+msgstr "周"
+
+msgid "Other Rates"
+msgstr "其他速率"
+
+msgid "Cumulative"
+msgstr "累计流量"
+
+msgid "Total Uploaded"
+msgstr "总上传"
+
+msgid "Total Downloaded"
+msgstr "总下载"
+
+msgid "LAN Uploaded"
+msgstr "LAN 已上传"
+
+msgid "LAN Downloaded"
+msgstr "LAN 已下载"
+
+msgid "WAN Uploaded"
+msgstr "WAN 已上传"
+
+msgid "WAN Downloaded"
+msgstr "WAN 已下载"
+
+msgid "Total Upload"
+msgstr "Всего выгружено"
+
+msgid "Total Download"
+msgstr "Всего загружено"
+
+msgid "LAN Upload"
+msgstr "LAN 上传速率"
+
+msgid "LAN Download"
+msgstr "LAN 下载速率"
+
+msgid "WAN Upload"
+msgstr "WAN 上传速率"
+
+msgid "WAN Download"
+msgstr "WAN 下载速率"
+
+msgid "Never Online"
+msgstr "Никогда не был в сети"
+
+msgid "Just Now"
+msgstr "Только что"
+
+msgid "min ago"
+msgstr "мин назад"
+
+msgid "h ago"
+msgstr "ч назад"
+
+msgid "days ago"
+msgstr "дн назад"
+
+msgid "months ago"
+msgstr "мес назад"
+
+msgid "years ago"
+msgstr "лет назад"
+
+msgid "Last Online"
+msgstr "Последний раз в сети"
+
+msgid "Zoom"
+msgstr "缩放"
+
+msgid "Sort By"
+msgstr "排序方式"
+
+msgid "Online Status"
+msgstr "在线状态"
+
+msgid "Total Traffic"
+msgstr "总流量"
+
+msgid "Ascending"
+msgstr "升序"
+
+msgid "Descending"
+msgstr "降序"
+
+msgid "Sort by Speed"
+msgstr "按速度排序"
+
+msgid "Sort by Traffic"
+msgstr "按用量排序"
+
+msgid "Simple Mode"
+msgstr "Простой режим"
+
+msgid "Detailed Mode"
+msgstr "Подробный режим"
+
+msgid "Bandix DNS Monitor"
+msgstr "Монитор DNS Bandix"
+
+msgid "DNS Monitor"
+msgstr "Монитор DNS"
+
+msgid "DNS Query Records"
+msgstr "Записи запросов DNS"
+
+msgid "DNS Statistics"
+msgstr "DNS 统计信息"
+
+msgid "DNS Monitoring Disabled"
+msgstr "DNS监控未启用"
+
+msgid "Please enable DNS monitoring in settings"
+msgstr "请在设置中启用DNS监控功能"
+
+msgid "Go to Settings"
+msgstr "前往设置"
+
+msgid "No Data"
+msgstr "无数据"
+
+msgid "Time"
+msgstr "Время"
+
+msgid "Domain"
+msgstr "Домен"
+
+msgid "Query Type"
+msgstr "Тип запроса"
+
+msgid "Response Code"
+msgstr "响应码"
+
+msgid "Response Time"
+msgstr "Время отклика"
+
+msgid "Source IP"
+msgstr "源IP"
+
+msgid "Destination IP"
+msgstr "目标IP"
+
+msgid "Response IPs"
+msgstr "响应IP"
+
+msgid "Response Result"
+msgstr "响应结果"
+
+msgid "DNS Server"
+msgstr "DNS-сервер"
+
+msgid "Query"
+msgstr "Запрос"
+
+msgid "Response"
+msgstr "Ответ"
+
+msgid "Filter"
+msgstr "过滤"
+
+msgid "Domain Filter"
+msgstr "域名过滤"
+
+msgid "Device Filter"
+msgstr "设备过滤"
+
+msgid "DNS Server Filter"
+msgstr "DNS服务器过滤"
+
+msgid "Type Filter"
+msgstr "类型过滤"
+
+msgid "All"
+msgstr "全部"
+
+msgid "Queries Only"
+msgstr "仅查询"
+
+msgid "Responses Only"
+msgstr "仅响应"
+
+msgid "Search"
+msgstr "Поиск"
+
+msgid "Search Domain"
+msgstr "搜索域名"
+
+msgid "Search Device"
+msgstr "搜索设备"
+
+msgid "Search DNS Server"
+msgstr "搜索DNS服务器"
+
+msgid "Clear"
+msgstr "清除"
+
+msgid "Previous"
+msgstr "Предыдущая"
+
+msgid "Next"
+msgstr "Следующая"
+
+msgid "Page"
+msgstr "Страница"
+
+msgid "of"
+msgstr "из"
+
+msgid "Total"
+msgstr "Всего"
+
+msgid "records"
+msgstr "записей"
+
+msgid "Per Page"
+msgstr "每页显示"
+
+msgid ""
+msgstr "条"
+
+msgid "Total Queries"
+msgstr "Всего запросов"
+
+msgid "Total Responses"
+msgstr "总响应数"
+
+msgid "Queries with Response"
+msgstr "有响应查询"
+
+msgid "Queries without Response"
+msgstr "无响应查询"
+
+msgid "Avg Response Time"
+msgstr "平均响应时间"
+
+msgid "Min Response Time"
+msgstr "最快响应时间"
+
+msgid "Max Response Time"
+msgstr "最慢响应时间"
+
+msgid "Success Rate"
+msgstr "Процент успеха"
+
+msgid "Success"
+msgstr "Успешно"
+
+msgid "Failure"
+msgstr "Ошибка"
+
+msgid "Top Domains"
+msgstr "Самые запрашиваемые домены"
+
+msgid "Top Query Types"
+msgstr "最常用查询类型"
+
+msgid "Top Devices"
+msgstr "Самые активные устройства"
+
+msgid "Top DNS Servers"
+msgstr "最常用DNS服务器"
+
+msgid "Unique Devices"
+msgstr "唯一设备数"
+
+msgid "ms"
+msgstr "毫秒"
+
+msgid "minutes"
+msgstr "分钟"
+
+msgid "Unknown Device"
+msgstr "未知设备"
+
+msgid "Domain not found"
+msgstr "域名未找到"
+
+msgid "Server error"
+msgstr "服务器错误"
+
+msgid "Format error"
+msgstr "格式错误"
+
+msgid "Refused"
+msgstr "拒绝"
+
+msgid "Other"
+msgstr "其他"
+
+msgid "Does not include DoH and DoT"
+msgstr "不包含 DoH 和 DoT"
+
+msgid "Latest Response Time"
+msgstr "最新响应时间"
+
+msgid "Response Code Distribution"
+msgstr "响应码分布"
+
+msgid "Response Time Percentiles"
+msgstr "响应时间百分位"
+
+msgid "P50"
+msgstr "P50"
+
+msgid "P90"
+msgstr "P90"
+
+msgid "P95"
+msgstr "P95"
+
+msgid "P99"
+msgstr "P99"
+
+msgid "Bandix Connection Monitor"
+msgstr "Монитор соединений Bandix"
+
+msgid "Connection Monitor"
+msgstr "Монитор соединений"
+
+msgid "Device Connection Statistics"
+msgstr "设备连接统计"
+
+msgid "Global Connection Statistics"
+msgstr "全局连接统计"
+
+msgid "Active TCP"
+msgstr "活跃TCP"
+
+msgid "Active UDP"
+msgstr "活跃UDP"
+
+msgid "Closed TCP"
+msgstr "已关闭TCP"
+
+msgid "Total Connections"
+msgstr "Всего соединений"
+
+msgid "Last Updated"
+msgstr "最后更新"
+
+msgid "TCP Connections"
+msgstr "Соединения TCP"
+
+msgid "UDP Connections"
+msgstr "Соединения UDP"
+
+msgid "Established TCP"
+msgstr "已建立TCP"
+
+msgid "TIME_WAIT TCP"
+msgstr "TIME_WAIT TCP"
+
+msgid "CLOSE_WAIT TCP"
+msgstr "CLOSE_WAIT TCP"
+
+msgid "Total Devices"
+msgstr "设备总数"
+
+msgid "Connection Monitor Disabled"
+msgstr "连接监控未启用"
+
+msgid "Please enable connection monitoring in settings"
+msgstr "请在设置中启用连接监控功能"
+
+msgid "List only shows LAN device connections, data may differ from total connections."
+msgstr "列表只显示局域网设备连接,数据可能和总连接数不一致。"
+
+msgid "TCP Status Details"
+msgstr "TCP 状态详情"
+
+msgid "Basic Settings"
+msgstr "Основные настройки"
+
+msgid "Traffic Monitor Settings"
+msgstr "Настройки монитора трафика"
+
+msgid "Connection Monitor Settings"
+msgstr "Настройки монитора соединений"
+
+msgid "DNS Monitor Settings"
+msgstr "Настройки монитора DNS"
+
+msgid "Bandix Basic Configuration"
+msgstr "Bandix 基本配置"
+
+msgid "Configure basic parameters for Bandix service"
+msgstr "配置 Bandix 服务的基本参数"
+
+msgid "Bandix Traffic Monitor Configuration"
+msgstr "Bandix 流量监控配置"
+
+msgid "Configure traffic monitoring related parameters"
+msgstr "配置流量监控相关参数"
+
+msgid "Bandix Connection Monitor Configuration"
+msgstr "Bandix 连接监控配置"
+
+msgid "Configure connection monitoring related parameters"
+msgstr "配置连接监控相关参数"
+
+msgid "Bandix DNS Monitor Configuration"
+msgstr "Bandix DNS监控配置"
+
+msgid "Configure DNS monitoring related parameters"
+msgstr "配置DNS监控相关参数"
+
+msgid "Enable"
+msgstr "Включить"
+
+msgid "Enable Bandix Traffic Monitor Service"
+msgstr "启用 Bandix 流量监控服务"
+
+msgid "Enable Traffic Monitoring"
+msgstr "Включить мониторинг трафика"
+
+msgid "Enable Connection Monitoring"
+msgstr "Включить мониторинг соединений"
+
+msgid "Enable DNS Monitoring"
+msgstr "Включить мониторинг DNS"
+
+msgid "Simplified Chinese"
+msgstr "简体中文"
+
+msgid "Traditional Chinese"
+msgstr "繁体中文"
+
+msgid "Port"
+msgstr "Порт"
+
+msgid "Port for Bandix service to listen on"
+msgstr "Bandix 服务监听的端口"
+
+msgid "Monitor Interface"
+msgstr "Интерфейс мониторинга"
+
+msgid "Select the LAN network interface to monitor"
+msgstr "选择要监控的LAN网络接口"
+
+msgid "Speed Units"
+msgstr "Единицы скорости"
+
+msgid "Select the speed display unit format"
+msgstr "选择网速显示的单位格式"
+
+msgid "Bytes Units (B/s, KB/s, MB/s)"
+msgstr "字节单位 (B/s, KB/s, MB/s)"
+
+msgid "Bits Units (bps, Kbps, Mbps)"
+msgstr "比特单位 (bps, Kbps, Mbps)"
+
+msgid "Interface Theme"
+msgstr "界面主题"
+
+msgid "Select the display theme for Bandix Traffic Monitor"
+msgstr "选择 Bandix 流量监控的显示主题"
+
+msgid "Follow System"
+msgstr "跟随系统"
+
+msgid "Light Mode"
+msgstr "明亮模式"
+
+msgid "Dark Mode"
+msgstr "暗黑模式"
+
+msgid "Feedback"
+msgstr "意见反馈"
+
+msgid "Log Level"
+msgstr "Уровень журналирования"
+
+msgid "Set the log level for Bandix service"
+msgstr "设置 Bandix 服务的日志级别"
+
+msgid "Offline Timeout"
+msgstr "Таймаут офлайн"
+
+msgid "Set the timeout for device offline detection (seconds). Devices inactive for longer than this time will be marked as offline"
+msgstr "设置设备离线判断的超时时间(秒)。超过此时间未活动的设备将被标记为离线"
+
+msgid "Traffic History Period"
+msgstr "Период истории трафика"
+
+msgid "10 minutes interval uses about 60 KB per device"
+msgstr "Интервал 10 минут использует около 60 КБ на устройство"
+
+msgid "Data Flush Interval"
+msgstr "Интервал сброса данных"
+
+msgid "Set the interval for flushing data to disk"
+msgstr "设置数据写入磁盘的时间间隔"
+
+msgid "1 minute"
+msgstr "1 минута"
+
+msgid "5 minutes"
+msgstr "5 минут"
+
+msgid "10 minutes"
+msgstr "10 минут"
+
+msgid "15 minutes"
+msgstr "15 分钟"
+
+msgid "20 minutes"
+msgstr "20 分钟"
+
+msgid "25 minutes"
+msgstr "25 分钟"
+
+msgid "30 minutes"
+msgstr "30 分钟"
+
+msgid "1 hour"
+msgstr "1 час"
+
+msgid "2 hours"
+msgstr "2 часа"
+
+msgid "Data Directory"
+msgstr "Каталог данных"
+
+msgid "Bandix data storage directory"
+msgstr "Bandix 数据存储目录"
+
+msgid "Enable Bandix connection monitoring"
+msgstr "启用 Bandix 连接监控功能"
+
+msgid "Enable Bandix DNS monitoring"
+msgstr "启用 Bandix DNS监控功能"
+
+msgid "DNS Max Records"
+msgstr "Максимум записей DNS"
+
+msgid "Set the maximum number of DNS query records to keep. Older records will be deleted when this limit is exceeded"
+msgstr "设置DNS查询记录的最大保存数量,超过此数量将删除最旧的记录"
+
+msgid "Persist History Data"
+msgstr "Сохранять историю данных"
+
+msgid "Enable data persistence functionality, data will only be persisted to disk when this option is enabled"
+msgstr "启用数据持久化功能,只有启用此选项后才会持久化到磁盘"
\ No newline at end of file
diff --git a/luci-app-bandix/po/th/bandix.po b/luci-app-bandix/po/th/bandix.po
new file mode 100644
index 0000000..501dc3d
--- /dev/null
+++ b/luci-app-bandix/po/th/bandix.po
@@ -0,0 +1,735 @@
+msgid ""
+msgstr ""
+"Content-Type: text/plain; charset=UTF-8\n"
+
+msgid "Bandix Traffic Monitor"
+msgstr "ตัวตรวจสอบการจราจร Bandix"
+
+msgid "Loading data..."
+msgstr "กำลังโหลดข้อมูล..."
+
+msgid "Unable to fetch data"
+msgstr "ไม่สามารถดึงข้อมูลได้"
+
+msgid "Unable to fetch history data"
+msgstr "ไม่สามารถดึงข้อมูลประวัติได้"
+
+msgid "Hostname"
+msgstr "ชื่อโฮสต์"
+
+msgid "IP Address"
+msgstr "ที่อยู่ IP"
+
+msgid "MAC Address"
+msgstr "ที่อยู่ MAC"
+
+msgid "Download Speed"
+msgstr "ความเร็วในการดาวน์โหลด"
+
+msgid "Upload Speed"
+msgstr "ความเร็วในการอัปโหลด"
+
+msgid "Total Download"
+msgstr "การดาวน์โหลดทั้งหมด"
+
+msgid "Total Upload"
+msgstr "การอัปโหลดทั้งหมด"
+
+msgid "Download Limit"
+msgstr "ขีดจำกัดการดาวน์โหลด"
+
+msgid "Upload Limit"
+msgstr "ขีดจำกัดการอัปโหลด"
+
+msgid "Interface Language"
+msgstr "ภาษาของอินเทอร์เฟซ"
+
+msgid "Select the display language for Bandix Traffic Monitor"
+msgstr "เลือกภาษาการแสดงผลสำหรับตัวตรวจสอบการจราจร Bandix"
+
+msgid "Device Info"
+msgstr "ข้อมูลอุปกรณ์"
+
+msgid "Device List"
+msgstr "รายการอุปกรณ์"
+
+msgid "LAN Traffic"
+msgstr "การจราจร LAN"
+
+msgid "WAN Traffic"
+msgstr "การจราจร WAN"
+
+msgid "Rate Limit"
+msgstr "ขีดจำกัดอัตรา"
+
+msgid "Actions"
+msgstr "การดำเนินการ"
+
+msgid "Online Devices"
+msgstr "อุปกรณ์ออนไลน์"
+
+msgid "WAN Only"
+msgstr "WAN เท่านั้น"
+
+msgid "Settings"
+msgstr "การตั้งค่า"
+
+msgid "Device Settings"
+msgstr "การตั้งค่าอุปกรณ์"
+
+msgid "Rate Limits"
+msgstr "ขีดจำกัดอัตรา"
+
+msgid "Remove Rate Limit"
+msgstr "ลบขีดจำกัดอัตรา"
+
+msgid "Save"
+msgstr "บันทึก"
+
+msgid "Cancel"
+msgstr "ยกเลิก"
+
+msgid "Set Rate Limit"
+msgstr "ตั้งค่าขีดจำกัดอัตรา"
+
+msgid "Device"
+msgstr "อุปกรณ์"
+
+msgid "Set Hostname"
+msgstr "ตั้งชื่อโฮสต์"
+
+msgid "Please enter hostname"
+msgstr "กรุณากรอกชื่อโฮสต์"
+
+msgid "Hostname set successfully"
+msgstr "ตั้งชื่อโฮสต์สำเร็จ"
+
+msgid "Failed to set hostname"
+msgstr "ตั้งชื่อโฮสต์ล้มเหลว"
+
+msgid "Unlimited"
+msgstr "ไม่จำกัด"
+
+msgid "Settings saved successfully"
+msgstr "บันทึกการตั้งค่าสำเร็จ"
+
+msgid "Failed to save settings"
+msgstr "บันทึกการตั้งค่าล้มเหลว"
+
+msgid "Please enter a valid speed value"
+msgstr "กรุณากรอกค่าความเร็วที่ถูกต้อง"
+
+msgid "Speed value must be greater than 0"
+msgstr "ค่าความเร็วต้องมากกว่า 0"
+
+msgid "Saving..."
+msgstr "กำลังบันทึก..."
+
+msgid "Rate limiting only applies to WAN traffic."
+msgstr "การจำกัดอัตราใช้กับ WAN เท่านั้น"
+
+msgid "Tip: Enter 0 for unlimited"
+msgstr "เคล็ดลับ: ใส่ 0 สำหรับไม่จำกัด"
+
+msgid "Traffic History"
+msgstr "ประวัติการจราจร"
+
+msgid "Select Device"
+msgstr "เลือกอุปกรณ์"
+
+msgid "All Devices"
+msgstr "อุปกรณ์ทั้งหมด"
+
+msgid "Time Range"
+msgstr "ช่วงเวลา"
+
+msgid "Last 5 minutes"
+msgstr "5 นาทีที่แล้ว"
+
+msgid "Last 30 minutes"
+msgstr "30 นาทีที่แล้ว"
+
+msgid "Last 2 hours"
+msgstr "2 ชั่วโมงที่แล้ว"
+
+msgid "Type"
+msgstr "ประเภท"
+
+msgid "Total"
+msgstr "ทั้งหมด"
+
+msgid "LAN"
+msgstr "การจราจร LAN"
+
+msgid "WAN"
+msgstr "การจราจร WAN"
+
+msgid "Refresh"
+msgstr "รีเฟรช"
+
+msgid "Upload Rate"
+msgstr "อัตราการอัปโหลด"
+
+msgid "Download Rate"
+msgstr "อัตราการดาวน์โหลด"
+
+msgid "Last"
+msgstr "ล่าสุด"
+
+msgid "second"
+msgstr "วินาที"
+
+msgid "minute"
+msgstr "นาที"
+
+msgid "hour"
+msgstr "ชั่วโมง"
+
+msgid "day"
+msgstr "วัน"
+
+msgid "week"
+msgstr "สัปดาห์"
+
+msgid "Other Rates"
+msgstr "อัตราอื่นๆ"
+
+msgid "Cumulative"
+msgstr "สะสม"
+
+msgid "Total Uploaded"
+msgstr "อัปโหลดทั้งหมด"
+
+msgid "Total Downloaded"
+msgstr "ดาวน์โหลดทั้งหมด"
+
+msgid "LAN Uploaded"
+msgstr "LAN อัปโหลดแล้ว"
+
+msgid "LAN Downloaded"
+msgstr "LAN ดาวน์โหลดแล้ว"
+
+msgid "WAN Uploaded"
+msgstr "WAN อัปโหลดแล้ว"
+
+msgid "WAN Downloaded"
+msgstr "WAN ดาวน์โหลดแล้ว"
+
+msgid "Total Upload"
+msgstr "การอัปโหลดทั้งหมด"
+
+msgid "Total Download"
+msgstr "การดาวน์โหลดทั้งหมด"
+
+msgid "LAN Upload"
+msgstr "การอัปโหลด LAN"
+
+msgid "LAN Download"
+msgstr "การดาวน์โหลด LAN"
+
+msgid "WAN Upload"
+msgstr "การอัปโหลด WAN"
+
+msgid "WAN Download"
+msgstr "การดาวน์โหลด WAN"
+
+msgid "Never Online"
+msgstr "ไม่เคยออนไลน์"
+
+msgid "Just Now"
+msgstr "เมื่อสักครู่"
+
+msgid "min ago"
+msgstr "นาทีที่แล้ว"
+
+msgid "h ago"
+msgstr "ชั่วโมงที่แล้ว"
+
+msgid "days ago"
+msgstr "วันที่แล้ว"
+
+msgid "months ago"
+msgstr "เดือนที่แล้ว"
+
+msgid "years ago"
+msgstr "ปีที่แล้ว"
+
+msgid "Last Online"
+msgstr "ออนไลน์ล่าสุด"
+
+msgid "Zoom"
+msgstr "ซูม"
+
+msgid "Sort By"
+msgstr "เรียงตาม"
+
+msgid "Online Status"
+msgstr "สถานะออนไลน์"
+
+msgid "Total Traffic"
+msgstr "การจราจรทั้งหมด"
+
+msgid "Ascending"
+msgstr "จากน้อยไปมาก"
+
+msgid "Descending"
+msgstr "จากมากไปน้อย"
+
+msgid "Sort by Speed"
+msgstr "เรียงตามความเร็ว"
+
+msgid "Sort by Traffic"
+msgstr "เรียงตามการจราจร"
+
+msgid "Simple Mode"
+msgstr "โหมดง่าย"
+
+msgid "Detailed Mode"
+msgstr "โหมดละเอียด"
+
+msgid "Bandix DNS Monitor"
+msgstr "ตัวตรวจสอบ DNS Bandix"
+
+msgid "DNS Monitor"
+msgstr "ตัวตรวจสอบ DNS"
+
+msgid "DNS Query Records"
+msgstr "บันทึกการสอบถาม DNS"
+
+msgid "DNS Statistics"
+msgstr "สถิติ DNS"
+
+msgid "DNS Monitoring Disabled"
+msgstr "การตรวจสอบ DNS ถูกปิดใช้งาน"
+
+msgid "Please enable DNS monitoring in settings"
+msgstr "กรุณาเปิดใช้งานการตรวจสอบ DNS ในการตั้งค่า"
+
+msgid "Go to Settings"
+msgstr "ไปที่การตั้งค่า"
+
+msgid "No Data"
+msgstr "ไม่มีข้อมูล"
+
+msgid "Time"
+msgstr "เวลา"
+
+msgid "Domain"
+msgstr "โดเมน"
+
+msgid "Query Type"
+msgstr "ประเภทการสอบถาม"
+
+msgid "Response Code"
+msgstr "รหัสการตอบกลับ"
+
+msgid "Response Time"
+msgstr "เวลาตอบกลับ"
+
+msgid "Source IP"
+msgstr "IP ต้นทาง"
+
+msgid "Destination IP"
+msgstr "IP ปลายทาง"
+
+msgid "Response IPs"
+msgstr "IP การตอบกลับ"
+
+msgid "Response Result"
+msgstr "ผลการตอบกลับ"
+
+msgid "DNS Server"
+msgstr "เซิร์ฟเวอร์ DNS"
+
+msgid "Query"
+msgstr "สอบถาม"
+
+msgid "Response"
+msgstr "ตอบกลับ"
+
+msgid "Filter"
+msgstr "กรอง"
+
+msgid "Domain Filter"
+msgstr "กรองโดเมน"
+
+msgid "Device Filter"
+msgstr "กรองอุปกรณ์"
+
+msgid "DNS Server Filter"
+msgstr "กรองเซิร์ฟเวอร์ DNS"
+
+msgid "Type Filter"
+msgstr "กรองประเภท"
+
+msgid "All"
+msgstr "ทั้งหมด"
+
+msgid "Queries Only"
+msgstr "สอบถามเท่านั้น"
+
+msgid "Responses Only"
+msgstr "ตอบกลับเท่านั้น"
+
+msgid "Search"
+msgstr "ค้นหา"
+
+msgid "Search Domain"
+msgstr "ค้นหาโดเมน"
+
+msgid "Search Device"
+msgstr "ค้นหาอุปกรณ์"
+
+msgid "Search DNS Server"
+msgstr "ค้นหาเซิร์ฟเวอร์ DNS"
+
+msgid "Clear"
+msgstr "ล้าง"
+
+msgid "Previous"
+msgstr "ก่อนหน้า"
+
+msgid "Next"
+msgstr "ถัดไป"
+
+msgid "Page"
+msgstr "หน้า"
+
+msgid "of"
+msgstr "จาก"
+
+msgid "Total"
+msgstr "ทั้งหมด"
+
+msgid "records"
+msgstr "รายการ"
+
+msgid "Per Page"
+msgstr "ต่อหน้า"
+
+msgid ""
+msgstr ""
+
+msgid "Total Queries"
+msgstr "การสอบถามทั้งหมด"
+
+msgid "Total Responses"
+msgstr "การตอบกลับทั้งหมด"
+
+msgid "Queries with Response"
+msgstr "การสอบถามที่มีการตอบกลับ"
+
+msgid "Queries without Response"
+msgstr "การสอบถามที่ไม่มีการตอบกลับ"
+
+msgid "Avg Response Time"
+msgstr "เวลาตอบกลับเฉลี่ย"
+
+msgid "Min Response Time"
+msgstr "เวลาตอบกลับต่ำสุด"
+
+msgid "Max Response Time"
+msgstr "เวลาตอบกลับสูงสุด"
+
+msgid "Success Rate"
+msgstr "อัตราความสำเร็จ"
+
+msgid "Success"
+msgstr "สำเร็จ"
+
+msgid "Failure"
+msgstr "ล้มเหลว"
+
+msgid "Top Domains"
+msgstr "โดเมนยอดนิยม"
+
+msgid "Top Query Types"
+msgstr "ประเภทการสอบถามยอดนิยม"
+
+msgid "Top Devices"
+msgstr "อุปกรณ์ยอดนิยม"
+
+msgid "Top DNS Servers"
+msgstr "เซิร์ฟเวอร์ DNS ยอดนิยม"
+
+msgid "Unique Devices"
+msgstr "จำนวนอุปกรณ์ที่ไม่ซ้ำ"
+
+msgid "ms"
+msgstr "มิลลิวินาที"
+
+msgid "minutes"
+msgstr "นาที"
+
+msgid "Unknown Device"
+msgstr "อุปกรณ์ที่ไม่รู้จัก"
+
+msgid "Domain not found"
+msgstr "ไม่พบโดเมน"
+
+msgid "Server error"
+msgstr "ข้อผิดพลาดของเซิร์ฟเวอร์"
+
+msgid "Format error"
+msgstr "ข้อผิดพลาดของรูปแบบ"
+
+msgid "Refused"
+msgstr "ปฏิเสธ"
+
+msgid "Other"
+msgstr "อื่นๆ"
+
+msgid "Does not include DoH and DoT"
+msgstr "ไม่รวม DoH และ DoT"
+
+msgid "Latest Response Time"
+msgstr "เวลาตอบกลับล่าสุด"
+
+msgid "Response Code Distribution"
+msgstr "การกระจายรหัสการตอบกลับ"
+
+msgid "Response Time Percentiles"
+msgstr "เปอร์เซ็นไทล์เวลาตอบกลับ"
+
+msgid "P50"
+msgstr "P50"
+
+msgid "P90"
+msgstr "P90"
+
+msgid "P95"
+msgstr "P95"
+
+msgid "P99"
+msgstr "P99"
+
+msgid "Bandix Connection Monitor"
+msgstr "ตัวตรวจสอบการเชื่อมต่อ Bandix"
+
+msgid "Connection Monitor"
+msgstr "ตัวตรวจสอบการเชื่อมต่อ"
+
+msgid "Device Connection Statistics"
+msgstr "สถิติการเชื่อมต่ออุปกรณ์"
+
+msgid "Global Connection Statistics"
+msgstr "สถิติการเชื่อมต่อทั่วโลก"
+
+msgid "Active TCP"
+msgstr "TCP ที่ใช้งาน"
+
+msgid "Active UDP"
+msgstr "UDP ที่ใช้งาน"
+
+msgid "Closed TCP"
+msgstr "TCP ที่ปิด"
+
+msgid "Total Connections"
+msgstr "การเชื่อมต่อทั้งหมด"
+
+msgid "Last Updated"
+msgstr "อัปเดตล่าสุด"
+
+msgid "TCP Connections"
+msgstr "การเชื่อมต่อ TCP"
+
+msgid "UDP Connections"
+msgstr "การเชื่อมต่อ UDP"
+
+msgid "Established TCP"
+msgstr "TCP ที่สร้างแล้ว"
+
+msgid "TIME_WAIT TCP"
+msgstr "TIME_WAIT TCP"
+
+msgid "CLOSE_WAIT TCP"
+msgstr "CLOSE_WAIT TCP"
+
+msgid "Total Devices"
+msgstr "อุปกรณ์ทั้งหมด"
+
+msgid "Connection Monitor Disabled"
+msgstr "ตัวตรวจสอบการเชื่อมต่อถูกปิดใช้งาน"
+
+msgid "Please enable connection monitoring in settings"
+msgstr "กรุณาเปิดใช้งานการตรวจสอบการเชื่อมต่อในการตั้งค่า"
+
+msgid "List only shows LAN device connections, data may differ from total connections."
+msgstr "รายการแสดงเฉพาะการเชื่อมต่ออุปกรณ์ LAN ข้อมูลอาจแตกต่างจากจำนวนการเชื่อมต่อทั้งหมด"
+
+msgid "TCP Status Details"
+msgstr "รายละเอียดสถานะ TCP"
+
+msgid "Basic Settings"
+msgstr "การตั้งค่าพื้นฐาน"
+
+msgid "Traffic Monitor Settings"
+msgstr "การตั้งค่าตัวตรวจสอบการจราจร"
+
+msgid "Connection Monitor Settings"
+msgstr "การตั้งค่าตัวตรวจสอบการเชื่อมต่อ"
+
+msgid "DNS Monitor Settings"
+msgstr "การตั้งค่าตัวตรวจสอบ DNS"
+
+msgid "Bandix Basic Configuration"
+msgstr "การกำหนดค่าพื้นฐาน Bandix"
+
+msgid "Configure basic parameters for Bandix service"
+msgstr "กำหนดค่าพารามิเตอร์พื้นฐานสำหรับบริการ Bandix"
+
+msgid "Bandix Traffic Monitor Configuration"
+msgstr "การกำหนดค่าตัวตรวจสอบการจราจร Bandix"
+
+msgid "Configure traffic monitoring related parameters"
+msgstr "กำหนดค่าพารามิเตอร์ที่เกี่ยวข้องกับการตรวจสอบการจราจร"
+
+msgid "Bandix Connection Monitor Configuration"
+msgstr "การกำหนดค่าตัวตรวจสอบการเชื่อมต่อ Bandix"
+
+msgid "Configure connection monitoring related parameters"
+msgstr "กำหนดค่าพารามิเตอร์ที่เกี่ยวข้องกับการตรวจสอบการเชื่อมต่อ"
+
+msgid "Bandix DNS Monitor Configuration"
+msgstr "การกำหนดค่าตัวตรวจสอบ DNS Bandix"
+
+msgid "Configure DNS monitoring related parameters"
+msgstr "กำหนดค่าพารามิเตอร์ที่เกี่ยวข้องกับการตรวจสอบ DNS"
+
+msgid "Enable"
+msgstr "เปิดใช้งาน"
+
+msgid "Enable Bandix Traffic Monitor Service"
+msgstr "เปิดใช้งานบริการตัวตรวจสอบการจราจร Bandix"
+
+msgid "Enable Traffic Monitoring"
+msgstr "เปิดใช้งานการตรวจสอบการจราจร"
+
+msgid "Enable Connection Monitoring"
+msgstr "เปิดใช้งานการตรวจสอบการเชื่อมต่อ"
+
+msgid "Enable DNS Monitoring"
+msgstr "เปิดใช้งานการตรวจสอบ DNS"
+
+msgid "Simplified Chinese"
+msgstr "ภาษาจีนตัวย่อ"
+
+msgid "Traditional Chinese"
+msgstr "ภาษาจีนตัวเต็ม"
+
+msgid "Port"
+msgstr "พอร์ต"
+
+msgid "Port for Bandix service to listen on"
+msgstr "พอร์ตสำหรับบริการ Bandix ฟัง"
+
+msgid "Monitor Interface"
+msgstr "อินเทอร์เฟซตรวจสอบ"
+
+msgid "Select the LAN network interface to monitor"
+msgstr "เลือกอินเทอร์เฟซเครือข่าย LAN ที่จะตรวจสอบ"
+
+msgid "Speed Units"
+msgstr "หน่วยความเร็ว"
+
+msgid "Select the speed display unit format"
+msgstr "เลือกรูปแบบหน่วยแสดงความเร็ว"
+
+msgid "Bytes Units (B/s, KB/s, MB/s)"
+msgstr "หน่วยไบต์ (B/s, KB/s, MB/s)"
+
+msgid "Bits Units (bps, Kbps, Mbps)"
+msgstr "หน่วยบิต (bps, Kbps, Mbps)"
+
+msgid "Interface Theme"
+msgstr "ธีมอินเทอร์เฟซ"
+
+msgid "Select the display theme for Bandix Traffic Monitor"
+msgstr "เลือกธีมการแสดงผลสำหรับตัวตรวจสอบการจราจร Bandix"
+
+msgid "Follow System"
+msgstr "ตามระบบ"
+
+msgid "Light Mode"
+msgstr "โหมดสว่าง"
+
+msgid "Dark Mode"
+msgstr "โหมดมืด"
+
+msgid "Feedback"
+msgstr "ข้อเสนอแนะ"
+
+msgid "Log Level"
+msgstr "ระดับบันทึก"
+
+msgid "Set the log level for Bandix service"
+msgstr "ตั้งค่าระดับบันทึกสำหรับบริการ Bandix"
+
+msgid "Offline Timeout"
+msgstr "หมดเวลาออฟไลน์"
+
+msgid "Set the timeout for device offline detection (seconds). Devices inactive for longer than this time will be marked as offline"
+msgstr "ตั้งค่าเวลาหมดเวลาสำหรับการตรวจจับอุปกรณ์ออฟไลน์ (วินาที) อุปกรณ์ที่ไม่ใช้งานนานกว่านี้จะถูกทำเครื่องหมายว่าออฟไลน์"
+
+msgid "Traffic History Period"
+msgstr "ระยะเวลาประวัติการจราจร"
+
+msgid "10 minutes interval uses about 60 KB per device"
+msgstr "ช่วงเวลา 10 นาทีใช้ประมาณ 60 KB ต่ออุปกรณ์"
+
+msgid "Data Flush Interval"
+msgstr "ช่วงเวลาล้างข้อมูล"
+
+msgid "Set the interval for flushing data to disk"
+msgstr "ตั้งค่าช่วงเวลาสำหรับล้างข้อมูลไปยังดิสก์"
+
+msgid "1 minute"
+msgstr "1 นาที"
+
+msgid "5 minutes"
+msgstr "5 นาที"
+
+msgid "10 minutes"
+msgstr "10 นาที"
+
+msgid "15 minutes"
+msgstr "15 นาที"
+
+msgid "20 minutes"
+msgstr "20 นาที"
+
+msgid "25 minutes"
+msgstr "25 นาที"
+
+msgid "30 minutes"
+msgstr "30 นาที"
+
+msgid "1 hour"
+msgstr "1 ชั่วโมง"
+
+msgid "2 hours"
+msgstr "2 ชั่วโมง"
+
+msgid "Data Directory"
+msgstr "ไดเรกทอรีข้อมูล"
+
+msgid "Bandix data storage directory"
+msgstr "ไดเรกทอรีจัดเก็บข้อมูล Bandix"
+
+msgid "Enable Bandix connection monitoring"
+msgstr "เปิดใช้งานการตรวจสอบการเชื่อมต่อ Bandix"
+
+msgid "Enable Bandix DNS monitoring"
+msgstr "เปิดใช้งานการตรวจสอบ DNS Bandix"
+
+msgid "DNS Max Records"
+msgstr "จำนวนบันทึก DNS สูงสุด"
+
+msgid "Set the maximum number of DNS query records to keep. Older records will be deleted when this limit is exceeded"
+msgstr "ตั้งค่าจำนวนสูงสุดของบันทึกการสอบถาม DNS ที่จะเก็บ บันทึกเก่าจะถูกลบเมื่อเกินขีดจำกัดนี้"
+
+msgid "Persist History Data"
+msgstr "เก็บข้อมูลประวัติ"
+
+msgid "Enable data persistence functionality, data will only be persisted to disk when this option is enabled"
+msgstr "เปิดใช้งานฟังก์ชันการเก็บข้อมูล ข้อมูลจะถูกเก็บไว้ในดิสก์เมื่อเปิดใช้งานตัวเลือกนี้เท่านั้น"
diff --git a/luci-app-bandix/po/zh-cn b/luci-app-bandix/po/zh-cn
new file mode 120000
index 0000000..8d69574
--- /dev/null
+++ b/luci-app-bandix/po/zh-cn
@@ -0,0 +1 @@
+zh_Hans
\ No newline at end of file
diff --git a/luci-app-bandix/po/zh-cn/bandix.po b/luci-app-bandix/po/zh-cn/bandix.po
deleted file mode 100644
index e919ccc..0000000
--- a/luci-app-bandix/po/zh-cn/bandix.po
+++ /dev/null
@@ -1,3 +0,0 @@
-msgid "Bandix"
-msgstr "Bandix 流量监控"
-
diff --git a/luci-app-bandix/po/zh_Hans/bandix.po b/luci-app-bandix/po/zh_Hans/bandix.po
index e919ccc..fab0de5 100644
--- a/luci-app-bandix/po/zh_Hans/bandix.po
+++ b/luci-app-bandix/po/zh_Hans/bandix.po
@@ -1,3 +1,735 @@
-msgid "Bandix"
+msgid ""
+msgstr ""
+"Content-Type: text/plain; charset=UTF-8\n"
+
+msgid "Bandix Traffic Monitor"
msgstr "Bandix 流量监控"
+msgid "Loading data..."
+msgstr "正在加载数据..."
+
+msgid "Unable to fetch data"
+msgstr "无法获取数据"
+
+msgid "Unable to fetch history data"
+msgstr "无法获取历史数据"
+
+msgid "Hostname"
+msgstr "主机名"
+
+msgid "IP Address"
+msgstr "IP地址"
+
+msgid "MAC Address"
+msgstr "MAC地址"
+
+msgid "Download Speed"
+msgstr "下载速度"
+
+msgid "Upload Speed"
+msgstr "上传速度"
+
+msgid "Total Download"
+msgstr "总下载量"
+
+msgid "Total Upload"
+msgstr "总上传量"
+
+msgid "Download Limit"
+msgstr "下载限速"
+
+msgid "Upload Limit"
+msgstr "上传限速"
+
+msgid "Interface Language"
+msgstr "界面语言"
+
+msgid "Select the display language for Bandix Traffic Monitor"
+msgstr "选择 Bandix 流量监控的显示语言"
+
+msgid "Device Info"
+msgstr "设备信息"
+
+msgid "Device List"
+msgstr "设备列表"
+
+msgid "LAN Traffic"
+msgstr "LAN 流量"
+
+msgid "WAN Traffic"
+msgstr "WAN 流量"
+
+msgid "Rate Limit"
+msgstr "限速设置"
+
+msgid "Actions"
+msgstr "操作"
+
+msgid "Online Devices"
+msgstr "在线设备"
+
+msgid "WAN Only"
+msgstr "仅限WAN 流量"
+
+msgid "Settings"
+msgstr "设置"
+
+msgid "Device Settings"
+msgstr "设备设置"
+
+msgid "Rate Limits"
+msgstr "限速设置"
+
+msgid "Remove Rate Limit"
+msgstr "取消限速"
+
+msgid "Save"
+msgstr "保存"
+
+msgid "Cancel"
+msgstr "取消"
+
+msgid "Set Rate Limit"
+msgstr "设置限速"
+
+msgid "Device"
+msgstr "设备"
+
+msgid "Set Hostname"
+msgstr "设置主机名"
+
+msgid "Please enter hostname"
+msgstr "请输入主机名"
+
+msgid "Hostname set successfully"
+msgstr "主机名设置成功"
+
+msgid "Failed to set hostname"
+msgstr "主机名设置失败"
+
+msgid "Unlimited"
+msgstr "无限制"
+
+msgid "Settings saved successfully"
+msgstr "设置成功"
+
+msgid "Failed to save settings"
+msgstr "设置失败"
+
+msgid "Please enter a valid speed value"
+msgstr "请输入有效的速度值"
+
+msgid "Speed value must be greater than 0"
+msgstr "速度值必须大于0"
+
+msgid "Saving..."
+msgstr "保存中..."
+
+msgid "Rate limiting only applies to WAN traffic."
+msgstr "限速功能仅对 WAN 流量生效。"
+
+msgid "Tip: Enter 0 for unlimited"
+msgstr "提示:输入 0 表示无限制"
+
+msgid "Traffic History"
+msgstr "历史流量趋势"
+
+msgid "Select Device"
+msgstr "选择设备"
+
+msgid "All Devices"
+msgstr "所有设备"
+
+msgid "Time Range"
+msgstr "时间范围"
+
+msgid "Last 5 minutes"
+msgstr "最近5分钟"
+
+msgid "Last 30 minutes"
+msgstr "最近30分钟"
+
+msgid "Last 2 hours"
+msgstr "最近2小时"
+
+msgid "Type"
+msgstr "类型"
+
+msgid "Total"
+msgstr "总流量"
+
+msgid "LAN"
+msgstr "LAN 流量"
+
+msgid "WAN"
+msgstr "WAN 流量"
+
+msgid "Refresh"
+msgstr "刷新"
+
+msgid "Upload Rate"
+msgstr "上传速率"
+
+msgid "Download Rate"
+msgstr "下载速率"
+
+msgid "Last"
+msgstr "最近"
+
+msgid "second"
+msgstr "秒"
+
+msgid "minute"
+msgstr "分钟"
+
+msgid "hour"
+msgstr "小时"
+
+msgid "day"
+msgstr "天"
+
+msgid "week"
+msgstr "周"
+
+msgid "Other Rates"
+msgstr "其他速率"
+
+msgid "Cumulative"
+msgstr "累计流量"
+
+msgid "Total Uploaded"
+msgstr "总上传"
+
+msgid "Total Downloaded"
+msgstr "总下载"
+
+msgid "LAN Uploaded"
+msgstr "LAN 已上传"
+
+msgid "LAN Downloaded"
+msgstr "LAN 已下载"
+
+msgid "WAN Uploaded"
+msgstr "WAN 已上传"
+
+msgid "WAN Downloaded"
+msgstr "WAN 已下载"
+
+msgid "Total Upload"
+msgstr "总上传速率"
+
+msgid "Total Download"
+msgstr "总下载速率"
+
+msgid "LAN Upload"
+msgstr "LAN 上传速率"
+
+msgid "LAN Download"
+msgstr "LAN 下载速率"
+
+msgid "WAN Upload"
+msgstr "WAN 上传速率"
+
+msgid "WAN Download"
+msgstr "WAN 下载速率"
+
+msgid "Never Online"
+msgstr "从未上线"
+
+msgid "Just Now"
+msgstr "刚刚"
+
+msgid "min ago"
+msgstr "分钟前"
+
+msgid "h ago"
+msgstr "小时前"
+
+msgid "days ago"
+msgstr "天前"
+
+msgid "months ago"
+msgstr "个月前"
+
+msgid "years ago"
+msgstr "年前"
+
+msgid "Last Online"
+msgstr "最后上线"
+
+msgid "Zoom"
+msgstr "缩放"
+
+msgid "Sort By"
+msgstr "排序方式"
+
+msgid "Online Status"
+msgstr "在线状态"
+
+msgid "Total Traffic"
+msgstr "总流量"
+
+msgid "Ascending"
+msgstr "升序"
+
+msgid "Descending"
+msgstr "降序"
+
+msgid "Sort by Speed"
+msgstr "按速度排序"
+
+msgid "Sort by Traffic"
+msgstr "按用量排序"
+
+msgid "Simple Mode"
+msgstr "简易模式"
+
+msgid "Detailed Mode"
+msgstr "详细模式"
+
+msgid "Bandix DNS Monitor"
+msgstr "Bandix DNS 监控"
+
+msgid "DNS Monitor"
+msgstr "DNS 监控"
+
+msgid "DNS Query Records"
+msgstr "DNS 查询记录"
+
+msgid "DNS Statistics"
+msgstr "DNS 统计信息"
+
+msgid "DNS Monitoring Disabled"
+msgstr "DNS监控未启用"
+
+msgid "Please enable DNS monitoring in settings"
+msgstr "请在设置中启用DNS监控功能"
+
+msgid "Go to Settings"
+msgstr "前往设置"
+
+msgid "No Data"
+msgstr "无数据"
+
+msgid "Time"
+msgstr "时间"
+
+msgid "Domain"
+msgstr "域名"
+
+msgid "Query Type"
+msgstr "查询类型"
+
+msgid "Response Code"
+msgstr "响应码"
+
+msgid "Response Time"
+msgstr "响应时间"
+
+msgid "Source IP"
+msgstr "源IP"
+
+msgid "Destination IP"
+msgstr "目标IP"
+
+msgid "Response IPs"
+msgstr "响应IP"
+
+msgid "Response Result"
+msgstr "响应结果"
+
+msgid "DNS Server"
+msgstr "DNS服务器"
+
+msgid "Query"
+msgstr "查询"
+
+msgid "Response"
+msgstr "响应"
+
+msgid "Filter"
+msgstr "过滤"
+
+msgid "Domain Filter"
+msgstr "域名过滤"
+
+msgid "Device Filter"
+msgstr "设备过滤"
+
+msgid "DNS Server Filter"
+msgstr "DNS服务器过滤"
+
+msgid "Type Filter"
+msgstr "类型过滤"
+
+msgid "All"
+msgstr "全部"
+
+msgid "Queries Only"
+msgstr "仅查询"
+
+msgid "Responses Only"
+msgstr "仅响应"
+
+msgid "Search"
+msgstr "搜索"
+
+msgid "Search Domain"
+msgstr "搜索域名"
+
+msgid "Search Device"
+msgstr "搜索设备"
+
+msgid "Search DNS Server"
+msgstr "搜索DNS服务器"
+
+msgid "Clear"
+msgstr "清除"
+
+msgid "Previous"
+msgstr "上一页"
+
+msgid "Next"
+msgstr "下一页"
+
+msgid "Page"
+msgstr "第"
+
+msgid "of"
+msgstr "页,共"
+
+msgid "Total"
+msgstr "全部"
+
+msgid "records"
+msgstr "条记录"
+
+msgid "Per Page"
+msgstr "每页显示"
+
+msgid ""
+msgstr "条"
+
+msgid "Total Queries"
+msgstr "总查询数"
+
+msgid "Total Responses"
+msgstr "总响应数"
+
+msgid "Queries with Response"
+msgstr "有响应查询"
+
+msgid "Queries without Response"
+msgstr "无响应查询"
+
+msgid "Avg Response Time"
+msgstr "平均响应时间"
+
+msgid "Min Response Time"
+msgstr "最快响应时间"
+
+msgid "Max Response Time"
+msgstr "最慢响应时间"
+
+msgid "Success Rate"
+msgstr "成功率"
+
+msgid "Success"
+msgstr "成功"
+
+msgid "Failure"
+msgstr "失败"
+
+msgid "Top Domains"
+msgstr "最常查询域名"
+
+msgid "Top Query Types"
+msgstr "最常用查询类型"
+
+msgid "Top Devices"
+msgstr "最活跃设备"
+
+msgid "Top DNS Servers"
+msgstr "最常用DNS服务器"
+
+msgid "Unique Devices"
+msgstr "唯一设备数"
+
+msgid "ms"
+msgstr "毫秒"
+
+msgid "minutes"
+msgstr "分钟"
+
+msgid "Unknown Device"
+msgstr "未知设备"
+
+msgid "Domain not found"
+msgstr "域名未找到"
+
+msgid "Server error"
+msgstr "服务器错误"
+
+msgid "Format error"
+msgstr "格式错误"
+
+msgid "Refused"
+msgstr "拒绝"
+
+msgid "Other"
+msgstr "其他"
+
+msgid "Does not include DoH and DoT"
+msgstr "不包含 DoH 和 DoT"
+
+msgid "Latest Response Time"
+msgstr "最新响应时间"
+
+msgid "Response Code Distribution"
+msgstr "响应码分布"
+
+msgid "Response Time Percentiles"
+msgstr "响应时间百分位"
+
+msgid "P50"
+msgstr "P50"
+
+msgid "P90"
+msgstr "P90"
+
+msgid "P95"
+msgstr "P95"
+
+msgid "P99"
+msgstr "P99"
+
+msgid "Bandix Connection Monitor"
+msgstr "Bandix 连接监控"
+
+msgid "Connection Monitor"
+msgstr "连接监控"
+
+msgid "Device Connection Statistics"
+msgstr "设备连接统计"
+
+msgid "Global Connection Statistics"
+msgstr "全局连接统计"
+
+msgid "Active TCP"
+msgstr "活跃TCP"
+
+msgid "Active UDP"
+msgstr "活跃UDP"
+
+msgid "Closed TCP"
+msgstr "已关闭TCP"
+
+msgid "Total Connections"
+msgstr "总连接数"
+
+msgid "Last Updated"
+msgstr "最后更新"
+
+msgid "TCP Connections"
+msgstr "TCP连接数"
+
+msgid "UDP Connections"
+msgstr "UDP连接数"
+
+msgid "Established TCP"
+msgstr "已建立TCP"
+
+msgid "TIME_WAIT TCP"
+msgstr "TIME_WAIT TCP"
+
+msgid "CLOSE_WAIT TCP"
+msgstr "CLOSE_WAIT TCP"
+
+msgid "Total Devices"
+msgstr "设备总数"
+
+msgid "Connection Monitor Disabled"
+msgstr "连接监控未启用"
+
+msgid "Please enable connection monitoring in settings"
+msgstr "请在设置中启用连接监控功能"
+
+msgid "List only shows LAN device connections, data may differ from total connections."
+msgstr "列表只显示局域网设备连接,数据可能和总连接数不一致。"
+
+msgid "TCP Status Details"
+msgstr "TCP 状态详情"
+
+msgid "Basic Settings"
+msgstr "基本设置"
+
+msgid "Traffic Monitor Settings"
+msgstr "流量监控设置"
+
+msgid "Connection Monitor Settings"
+msgstr "连接监控设置"
+
+msgid "DNS Monitor Settings"
+msgstr "DNS监控设置"
+
+msgid "Bandix Basic Configuration"
+msgstr "Bandix 基本配置"
+
+msgid "Configure basic parameters for Bandix service"
+msgstr "配置 Bandix 服务的基本参数"
+
+msgid "Bandix Traffic Monitor Configuration"
+msgstr "Bandix 流量监控配置"
+
+msgid "Configure traffic monitoring related parameters"
+msgstr "配置流量监控相关参数"
+
+msgid "Bandix Connection Monitor Configuration"
+msgstr "Bandix 连接监控配置"
+
+msgid "Configure connection monitoring related parameters"
+msgstr "配置连接监控相关参数"
+
+msgid "Bandix DNS Monitor Configuration"
+msgstr "Bandix DNS监控配置"
+
+msgid "Configure DNS monitoring related parameters"
+msgstr "配置DNS监控相关参数"
+
+msgid "Enable"
+msgstr "启用"
+
+msgid "Enable Bandix Traffic Monitor Service"
+msgstr "启用 Bandix 流量监控服务"
+
+msgid "Enable Traffic Monitoring"
+msgstr "启用流量监控"
+
+msgid "Enable Connection Monitoring"
+msgstr "启用连接监控"
+
+msgid "Enable DNS Monitoring"
+msgstr "启用DNS监控"
+
+msgid "Simplified Chinese"
+msgstr "简体中文"
+
+msgid "Traditional Chinese"
+msgstr "繁体中文"
+
+msgid "Port"
+msgstr "端口"
+
+msgid "Port for Bandix service to listen on"
+msgstr "Bandix 服务监听的端口"
+
+msgid "Monitor Interface"
+msgstr "监控网卡"
+
+msgid "Select the LAN network interface to monitor"
+msgstr "选择要监控的LAN网络接口"
+
+msgid "Speed Units"
+msgstr "网速单位"
+
+msgid "Select the speed display unit format"
+msgstr "选择网速显示的单位格式"
+
+msgid "Bytes Units (B/s, KB/s, MB/s)"
+msgstr "字节单位 (B/s, KB/s, MB/s)"
+
+msgid "Bits Units (bps, Kbps, Mbps)"
+msgstr "比特单位 (bps, Kbps, Mbps)"
+
+msgid "Interface Theme"
+msgstr "界面主题"
+
+msgid "Select the display theme for Bandix Traffic Monitor"
+msgstr "选择 Bandix 流量监控的显示主题"
+
+msgid "Follow System"
+msgstr "跟随系统"
+
+msgid "Light Mode"
+msgstr "明亮模式"
+
+msgid "Dark Mode"
+msgstr "暗黑模式"
+
+msgid "Feedback"
+msgstr "意见反馈"
+
+msgid "Log Level"
+msgstr "日志级别"
+
+msgid "Set the log level for Bandix service"
+msgstr "设置 Bandix 服务的日志级别"
+
+msgid "Offline Timeout"
+msgstr "离线超时时间"
+
+msgid "Set the timeout for device offline detection (seconds). Devices inactive for longer than this time will be marked as offline"
+msgstr "设置设备离线判断的超时时间(秒)。超过此时间未活动的设备将被标记为离线"
+
+msgid "Traffic History Period"
+msgstr "历史流量周期"
+
+msgid "10 minutes interval uses about 60 KB per device"
+msgstr "10分钟历史周期,每个设备占用 60KB"
+
+msgid "Data Flush Interval"
+msgstr "数据 flush 间隔"
+
+msgid "Set the interval for flushing data to disk"
+msgstr "设置数据写入磁盘的时间间隔"
+
+msgid "1 minute"
+msgstr "1 分钟"
+
+msgid "5 minutes"
+msgstr "5 分钟"
+
+msgid "10 minutes"
+msgstr "10 分钟"
+
+msgid "15 minutes"
+msgstr "15 分钟"
+
+msgid "20 minutes"
+msgstr "20 分钟"
+
+msgid "25 minutes"
+msgstr "25 分钟"
+
+msgid "30 minutes"
+msgstr "30 分钟"
+
+msgid "1 hour"
+msgstr "1 小时"
+
+msgid "2 hours"
+msgstr "2 小时"
+
+msgid "Data Directory"
+msgstr "数据目录"
+
+msgid "Bandix data storage directory"
+msgstr "Bandix 数据存储目录"
+
+msgid "Enable Bandix connection monitoring"
+msgstr "启用 Bandix 连接监控功能"
+
+msgid "Enable Bandix DNS monitoring"
+msgstr "启用 Bandix DNS监控功能"
+
+msgid "DNS Max Records"
+msgstr "DNS最大记录数"
+
+msgid "Set the maximum number of DNS query records to keep. Older records will be deleted when this limit is exceeded"
+msgstr "设置DNS查询记录的最大保存数量,超过此数量将删除最旧的记录"
+
+msgid "Persist History Data"
+msgstr "持久化历史数据"
+
+msgid "Enable data persistence functionality, data will only be persisted to disk when this option is enabled"
+msgstr "启用数据持久化功能,只有启用此选项后才会持久化到磁盘"
\ No newline at end of file
diff --git a/luci-app-bandix/po/zh_Hant/bandix.po b/luci-app-bandix/po/zh_Hant/bandix.po
new file mode 100644
index 0000000..327fd9a
--- /dev/null
+++ b/luci-app-bandix/po/zh_Hant/bandix.po
@@ -0,0 +1,735 @@
+msgid ""
+msgstr ""
+"Content-Type: text/plain; charset=UTF-8\n"
+
+msgid "Bandix Traffic Monitor"
+msgstr "Bandix 流量監控"
+
+msgid "Loading data..."
+msgstr "正在載入數据..."
+
+msgid "Unable to fetch data"
+msgstr "無法取得數据"
+
+msgid "Unable to fetch history data"
+msgstr "無法取得歷史數据"
+
+msgid "Hostname"
+msgstr "主機名"
+
+msgid "IP Address"
+msgstr "IP位址"
+
+msgid "MAC Address"
+msgstr "MAC位址"
+
+msgid "Download Speed"
+msgstr "下載速度"
+
+msgid "Upload Speed"
+msgstr "上傳速度"
+
+msgid "Total Download"
+msgstr "總下載量"
+
+msgid "Total Upload"
+msgstr "總上傳量"
+
+msgid "Download Limit"
+msgstr "下載限速"
+
+msgid "Upload Limit"
+msgstr "上傳限速"
+
+msgid "Interface Language"
+msgstr "介面語言"
+
+msgid "Select the display language for Bandix Traffic Monitor"
+msgstr "選擇 Bandix 流量監控的顯示语言"
+
+msgid "Device Info"
+msgstr "設備資訊"
+
+msgid "Device List"
+msgstr "設備清單"
+
+msgid "LAN Traffic"
+msgstr "LAN 流量"
+
+msgid "WAN Traffic"
+msgstr "WAN 流量"
+
+msgid "Rate Limit"
+msgstr "限速設定"
+
+msgid "Actions"
+msgstr "操作"
+
+msgid "Online Devices"
+msgstr "線上設備"
+
+msgid "WAN Only"
+msgstr "仅限WAN 流量"
+
+msgid "Settings"
+msgstr "設定"
+
+msgid "Device Settings"
+msgstr "設備設定"
+
+msgid "Rate Limits"
+msgstr "限速設定"
+
+msgid "Remove Rate Limit"
+msgstr "取消限速"
+
+msgid "Save"
+msgstr "儲存"
+
+msgid "Cancel"
+msgstr "取消"
+
+msgid "Set Rate Limit"
+msgstr "設定限速"
+
+msgid "Device"
+msgstr "設備"
+
+msgid "Set Hostname"
+msgstr "設定主機名"
+
+msgid "Please enter hostname"
+msgstr "請輸入主機名"
+
+msgid "Hostname set successfully"
+msgstr "主機名設定成功"
+
+msgid "Failed to set hostname"
+msgstr "主機名設定失敗"
+
+msgid "Unlimited"
+msgstr "無限制"
+
+msgid "Settings saved successfully"
+msgstr "設定成功"
+
+msgid "Failed to save settings"
+msgstr "設定失敗"
+
+msgid "Please enter a valid speed value"
+msgstr "請輸入有效的速度值"
+
+msgid "Speed value must be greater than 0"
+msgstr "速度值必須大於0"
+
+msgid "Saving..."
+msgstr "儲存中..."
+
+msgid "Rate limiting only applies to WAN traffic."
+msgstr "限速功能僅對 WAN 流量生效。"
+
+msgid "Tip: Enter 0 for unlimited"
+msgstr "提示:输入 0 表示無限制"
+
+msgid "Traffic History"
+msgstr "歷史流量趨勢"
+
+msgid "Select Device"
+msgstr "選擇設備"
+
+msgid "All Devices"
+msgstr "所有設備"
+
+msgid "Time Range"
+msgstr "時間範圍"
+
+msgid "Last 5 minutes"
+msgstr "最近5分鐘"
+
+msgid "Last 30 minutes"
+msgstr "最近30分鐘"
+
+msgid "Last 2 hours"
+msgstr "最近2小時"
+
+msgid "Type"
+msgstr "類型"
+
+msgid "Total"
+msgstr "總流量"
+
+msgid "LAN"
+msgstr "LAN 流量"
+
+msgid "WAN"
+msgstr "WAN 流量"
+
+msgid "Refresh"
+msgstr "重新整理"
+
+msgid "Upload Rate"
+msgstr "上傳速率"
+
+msgid "Download Rate"
+msgstr "下載速率"
+
+msgid "Last"
+msgstr "最近"
+
+msgid "second"
+msgstr "秒"
+
+msgid "minute"
+msgstr "分鐘"
+
+msgid "hour"
+msgstr "小時"
+
+msgid "day"
+msgstr "天"
+
+msgid "week"
+msgstr "周"
+
+msgid "Other Rates"
+msgstr "其他速率"
+
+msgid "Cumulative"
+msgstr "累計流量"
+
+msgid "Total Uploaded"
+msgstr "總上傳"
+
+msgid "Total Downloaded"
+msgstr "總下載"
+
+msgid "LAN Uploaded"
+msgstr "LAN 已上傳"
+
+msgid "LAN Downloaded"
+msgstr "LAN 已下載"
+
+msgid "WAN Uploaded"
+msgstr "WAN 已上傳"
+
+msgid "WAN Downloaded"
+msgstr "WAN 已下載"
+
+msgid "Total Upload"
+msgstr "總上傳速率"
+
+msgid "Total Download"
+msgstr "總下載速率"
+
+msgid "LAN Upload"
+msgstr "LAN 上傳速率"
+
+msgid "LAN Download"
+msgstr "LAN 下載速率"
+
+msgid "WAN Upload"
+msgstr "WAN 上傳速率"
+
+msgid "WAN Download"
+msgstr "WAN 下載速率"
+
+msgid "Never Online"
+msgstr "從未上線"
+
+msgid "Just Now"
+msgstr "剛剛"
+
+msgid "min ago"
+msgstr "分鐘前"
+
+msgid "h ago"
+msgstr "小時前"
+
+msgid "days ago"
+msgstr "天前"
+
+msgid "months ago"
+msgstr "个月前"
+
+msgid "years ago"
+msgstr "年前"
+
+msgid "Last Online"
+msgstr "最後上線"
+
+msgid "Zoom"
+msgstr "縮放"
+
+msgid "Sort By"
+msgstr "排序方式"
+
+msgid "Online Status"
+msgstr "線上狀態"
+
+msgid "Total Traffic"
+msgstr "總流量"
+
+msgid "Ascending"
+msgstr "升序"
+
+msgid "Descending"
+msgstr "降序"
+
+msgid "Sort by Speed"
+msgstr "依速度排序"
+
+msgid "Sort by Traffic"
+msgstr "依用量排序"
+
+msgid "Simple Mode"
+msgstr "簡易模式"
+
+msgid "Detailed Mode"
+msgstr "詳細模式"
+
+msgid "Bandix DNS Monitor"
+msgstr "Bandix DNS 監控"
+
+msgid "DNS Monitor"
+msgstr "DNS 監控"
+
+msgid "DNS Query Records"
+msgstr "DNS 查詢記錄"
+
+msgid "DNS Statistics"
+msgstr "DNS 統計資訊"
+
+msgid "DNS Monitoring Disabled"
+msgstr "DNS監控未啟用"
+
+msgid "Please enable DNS monitoring in settings"
+msgstr "请在設定中啟用DNS監控功能"
+
+msgid "Go to Settings"
+msgstr "前往設定"
+
+msgid "No Data"
+msgstr "無資料"
+
+msgid "Time"
+msgstr "時間"
+
+msgid "Domain"
+msgstr "網域"
+
+msgid "Query Type"
+msgstr "查詢類型"
+
+msgid "Response Code"
+msgstr "回應碼"
+
+msgid "Response Time"
+msgstr "回應時間"
+
+msgid "Source IP"
+msgstr "來源IP"
+
+msgid "Destination IP"
+msgstr "目標IP"
+
+msgid "Response IPs"
+msgstr "回應IP"
+
+msgid "Response Result"
+msgstr "回應結果"
+
+msgid "DNS Server"
+msgstr "DNS伺服器"
+
+msgid "Query"
+msgstr "查詢"
+
+msgid "Response"
+msgstr "回應"
+
+msgid "Filter"
+msgstr "篩選"
+
+msgid "Domain Filter"
+msgstr "網域篩選"
+
+msgid "Device Filter"
+msgstr "設備篩選"
+
+msgid "DNS Server Filter"
+msgstr "DNS伺服器篩選"
+
+msgid "Type Filter"
+msgstr "類型篩選"
+
+msgid "All"
+msgstr "全部"
+
+msgid "Queries Only"
+msgstr "仅查詢"
+
+msgid "Responses Only"
+msgstr "仅回應"
+
+msgid "Search"
+msgstr "搜尋"
+
+msgid "Search Domain"
+msgstr "搜尋網域"
+
+msgid "Search Device"
+msgstr "搜尋設備"
+
+msgid "Search DNS Server"
+msgstr "搜尋DNS伺服器"
+
+msgid "Clear"
+msgstr "清除"
+
+msgid "Previous"
+msgstr "上一頁"
+
+msgid "Next"
+msgstr "下一頁"
+
+msgid "Page"
+msgstr "第"
+
+msgid "of"
+msgstr "頁,共"
+
+msgid "Total"
+msgstr "全部"
+
+msgid "records"
+msgstr "筆記錄"
+
+msgid "Per Page"
+msgstr "每頁顯示"
+
+msgid ""
+msgstr "筆"
+
+msgid "Total Queries"
+msgstr "總查詢數"
+
+msgid "Total Responses"
+msgstr "總回應數"
+
+msgid "Queries with Response"
+msgstr "有回應查詢"
+
+msgid "Queries without Response"
+msgstr "無回應查詢"
+
+msgid "Avg Response Time"
+msgstr "平均回應時間"
+
+msgid "Min Response Time"
+msgstr "最快回應時間"
+
+msgid "Max Response Time"
+msgstr "最慢回應時間"
+
+msgid "Success Rate"
+msgstr "成功率"
+
+msgid "Success"
+msgstr "成功"
+
+msgid "Failure"
+msgstr "失敗"
+
+msgid "Top Domains"
+msgstr "最常查詢網域"
+
+msgid "Top Query Types"
+msgstr "最常用查詢類型"
+
+msgid "Top Devices"
+msgstr "最活躍設備"
+
+msgid "Top DNS Servers"
+msgstr "最常用DNS伺服器"
+
+msgid "Unique Devices"
+msgstr "唯一設備數"
+
+msgid "ms"
+msgstr "毫秒"
+
+msgid "minutes"
+msgstr "分鐘"
+
+msgid "Unknown Device"
+msgstr "未知設備"
+
+msgid "Domain not found"
+msgstr "網域未找到"
+
+msgid "Server error"
+msgstr "伺服器錯誤"
+
+msgid "Format error"
+msgstr "格式錯誤"
+
+msgid "Refused"
+msgstr "拒絕"
+
+msgid "Other"
+msgstr "其他"
+
+msgid "Does not include DoH and DoT"
+msgstr "不包含 DoH 和 DoT"
+
+msgid "Latest Response Time"
+msgstr "最新回應時間"
+
+msgid "Response Code Distribution"
+msgstr "回應碼分布"
+
+msgid "Response Time Percentiles"
+msgstr "回應時間百分位"
+
+msgid "P50"
+msgstr "P50"
+
+msgid "P90"
+msgstr "P90"
+
+msgid "P95"
+msgstr "P95"
+
+msgid "P99"
+msgstr "P99"
+
+msgid "Bandix Connection Monitor"
+msgstr "Bandix 連線監控"
+
+msgid "Connection Monitor"
+msgstr "連線監控"
+
+msgid "Device Connection Statistics"
+msgstr "設備連線統計"
+
+msgid "Global Connection Statistics"
+msgstr "全域連線統計"
+
+msgid "Active TCP"
+msgstr "活躍TCP"
+
+msgid "Active UDP"
+msgstr "活躍UDP"
+
+msgid "Closed TCP"
+msgstr "已關閉TCP"
+
+msgid "Total Connections"
+msgstr "總連線數"
+
+msgid "Last Updated"
+msgstr "最後更新"
+
+msgid "TCP Connections"
+msgstr "TCP連線數"
+
+msgid "UDP Connections"
+msgstr "UDP連線數"
+
+msgid "Established TCP"
+msgstr "已建立TCP"
+
+msgid "TIME_WAIT TCP"
+msgstr "TIME_WAIT TCP"
+
+msgid "CLOSE_WAIT TCP"
+msgstr "CLOSE_WAIT TCP"
+
+msgid "Total Devices"
+msgstr "設備總數"
+
+msgid "Connection Monitor Disabled"
+msgstr "連線監控未啟用"
+
+msgid "Please enable connection monitoring in settings"
+msgstr "请在設定中啟用連線監控功能"
+
+msgid "List only shows LAN device connections, data may differ from total connections."
+msgstr "清單只顯示區域網路設備連線,數据可能和總連線數不一致。"
+
+msgid "TCP Status Details"
+msgstr "TCP 狀態詳情"
+
+msgid "Basic Settings"
+msgstr "基本設定"
+
+msgid "Traffic Monitor Settings"
+msgstr "流量監控設定"
+
+msgid "Connection Monitor Settings"
+msgstr "連線監控設定"
+
+msgid "DNS Monitor Settings"
+msgstr "DNS監控設定"
+
+msgid "Bandix Basic Configuration"
+msgstr "Bandix 基本配置"
+
+msgid "Configure basic parameters for Bandix service"
+msgstr "配置 Bandix 服務的基本参數"
+
+msgid "Bandix Traffic Monitor Configuration"
+msgstr "Bandix 流量監控配置"
+
+msgid "Configure traffic monitoring related parameters"
+msgstr "配置流量監控相關参數"
+
+msgid "Bandix Connection Monitor Configuration"
+msgstr "Bandix 連線監控配置"
+
+msgid "Configure connection monitoring related parameters"
+msgstr "配置連線監控相關参數"
+
+msgid "Bandix DNS Monitor Configuration"
+msgstr "Bandix DNS監控配置"
+
+msgid "Configure DNS monitoring related parameters"
+msgstr "配置DNS監控相關参數"
+
+msgid "Enable"
+msgstr "啟用"
+
+msgid "Enable Bandix Traffic Monitor Service"
+msgstr "啟用 Bandix 流量監控服務"
+
+msgid "Enable Traffic Monitoring"
+msgstr "啟用流量監控"
+
+msgid "Enable Connection Monitoring"
+msgstr "啟用連線監控"
+
+msgid "Enable DNS Monitoring"
+msgstr "啟用DNS監控"
+
+msgid "Simplified Chinese"
+msgstr "简体中文"
+
+msgid "Traditional Chinese"
+msgstr "繁体中文"
+
+msgid "Port"
+msgstr "連接埠"
+
+msgid "Port for Bandix service to listen on"
+msgstr "Bandix 服務監聽的連接埠"
+
+msgid "Monitor Interface"
+msgstr "監控網卡"
+
+msgid "Select the LAN network interface to monitor"
+msgstr "選擇要監控的LAN網路介面"
+
+msgid "Speed Units"
+msgstr "网速單位"
+
+msgid "Select the speed display unit format"
+msgstr "選擇网速顯示的單位格式"
+
+msgid "Bytes Units (B/s, KB/s, MB/s)"
+msgstr "位元組單位 (B/s, KB/s, MB/s)"
+
+msgid "Bits Units (bps, Kbps, Mbps)"
+msgstr "位元單位 (bps, Kbps, Mbps)"
+
+msgid "Interface Theme"
+msgstr "界面主題"
+
+msgid "Select the display theme for Bandix Traffic Monitor"
+msgstr "選擇 Bandix 流量監控的顯示主題"
+
+msgid "Follow System"
+msgstr "跟隨系統"
+
+msgid "Light Mode"
+msgstr "明亮模式"
+
+msgid "Dark Mode"
+msgstr "暗黑模式"
+
+msgid "Feedback"
+msgstr "意見回饋"
+
+msgid "Log Level"
+msgstr "日誌層級"
+
+msgid "Set the log level for Bandix service"
+msgstr "設定 Bandix 服務的日誌層級"
+
+msgid "Offline Timeout"
+msgstr "離線超时時間"
+
+msgid "Set the timeout for device offline detection (seconds). Devices inactive for longer than this time will be marked as offline"
+msgstr "設定設備離線判斷的超时時間(秒)。超過此時間未活動的設備将被標記为離線"
+
+msgid "Traffic History Period"
+msgstr "歷史流量週期"
+
+msgid "10 minutes interval uses about 60 KB per device"
+msgstr "10分鐘歷史週期,每个設備佔用 60KB"
+
+msgid "Data Flush Interval"
+msgstr "數据 flush 間隔"
+
+msgid "Set the interval for flushing data to disk"
+msgstr "設定數据寫入磁碟的時間間隔"
+
+msgid "1 minute"
+msgstr "1 分鐘"
+
+msgid "5 minutes"
+msgstr "5 分鐘"
+
+msgid "10 minutes"
+msgstr "10 分鐘"
+
+msgid "15 minutes"
+msgstr "15 分鐘"
+
+msgid "20 minutes"
+msgstr "20 分鐘"
+
+msgid "25 minutes"
+msgstr "25 分鐘"
+
+msgid "30 minutes"
+msgstr "30 分鐘"
+
+msgid "1 hour"
+msgstr "1 小時"
+
+msgid "2 hours"
+msgstr "2 小時"
+
+msgid "Data Directory"
+msgstr "數据目录"
+
+msgid "Bandix data storage directory"
+msgstr "Bandix 數据儲存目錄"
+
+msgid "Enable Bandix connection monitoring"
+msgstr "啟用 Bandix 連線監控功能"
+
+msgid "Enable Bandix DNS monitoring"
+msgstr "啟用 Bandix DNS監控功能"
+
+msgid "DNS Max Records"
+msgstr "DNS最大记录數"
+
+msgid "Set the maximum number of DNS query records to keep. Older records will be deleted when this limit is exceeded"
+msgstr "設定DNS查詢記錄的最大儲存數量,超過此數量将刪除最舊的记录"
+
+msgid "Persist History Data"
+msgstr "持久化歷史數据"
+
+msgid "Enable data persistence functionality, data will only be persisted to disk when this option is enabled"
+msgstr "啟用數据持久化功能,只有啟用此选项后才會持久化到磁碟"
\ No newline at end of file
diff --git a/luci-app-kucat-config/Makefile b/luci-app-kucat-config/Makefile
deleted file mode 100644
index 2d0d845..0000000
--- a/luci-app-kucat-config/Makefile
+++ /dev/null
@@ -1,24 +0,0 @@
-#
-# Copyright 2023-2025 sirpdboy team
-# This is free software, licensed under the Apache License, Version 2.0 .
-#
-
-include $(TOPDIR)/rules.mk
-NAME:=kucat
-PKG_NAME:=luci-app-$(NAME)
-LUCI_TITLE:=LuCI support for Kucat theme setting by sirpdboy
-LUCI_DEPENDS:=+curl
-LUCI_PKGARCH:=all
-
-PKG_VERSION:=1.1.1
-PKG_RELEASE:=20250722
-
-define Package/$(PKG_NAME)/conffiles
-/etc/config/kucat
-endef
-
-include $(TOPDIR)/feeds/luci/luci.mk
-
-# call BuildPackage - OpenWrt buildroot signature
-
-
diff --git a/luci-app-kucat-config/README.md b/luci-app-kucat-config/README.md
deleted file mode 100644
index 172a20c..0000000
--- a/luci-app-kucat-config/README.md
+++ /dev/null
@@ -1,91 +0,0 @@
- [](https://t.me/joinchat/AAAAAEpRF88NfOK5vBXGBQ)
-
-
- luci-app-kucat-config
-
-
-
-
-
-
-
-
-
-[中文](README_CN.md) | [English]
-
-
-
-# [luci-app-kucat-config](https://github.com/sirpdboy/luci-app-kucat-config)
-- Development time: December 2021
-- Release time: February 2023
-- Open source date: April 2023
-- Main supports LEDE18.06 and official 18.06 branch open source time: April 2023 version: 1.2.6
-- JS supports official website 19.07-24.10 branch latest version: May 2025 version: 2.5.9 compatible with OPENWRT 24.10 branch.
-
-# Function Introduction
-- Being cool is beauty, born from movement.
-- A toolbar with 5 shortcut keys.
-- Basic BUG, adapted to all plugins currently available
-- We have made a lot of optimizations for mobile phones and other devices
-- There are matching theme setting tools, which can adjust shortcut keys, background images, and various color schemes to choose from.
-
-# As mentioned earlier, the development of the Cool Cat theme drew inspiration and borrowed some code from Opentopd theme, Jerryk God's Argon theme, and Thinktip God's Neobird theme. Thank you for your valuable contributions!
-
-
-# Special Instructions
-- When there are advanced settings and KUCAT theme setting tools in the system, the settings in the advanced settings shall prevail.
-- If there are any issues with the settings, you can restore them using function 4 after logging in via SSH.
-- If it is an advanced setting, to restore the default settings, you can use:/etc/init.d/advancedplus reset
-- If it is the KUCAT setting tool, to restore the default settings, you can use:/etc/init.d/kucat reset
-
-## describe
-
-- luci-theme-kucat :https://github.com/sirpdboy/luci-theme-kucat
-- luci-app-advancedplus :https://github.com/sirpdboy/luci-app-advancedplus
-- luci-app-kucat-config :https://github.com/sirpdboy/luci-app-kucat-config
-
-## interface
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-# My other project
-
-- Watch Dog : https://github.com/sirpdboy/luci-app-watchdog
-- Net Speedtest : https://github.com/sirpdboy/luci-app-netspeedtest
-- Task Plan : https://github.com/sirpdboy/luci-app-taskplan
-- Power Off Device : https://github.com/sirpdboy/luci-app-poweroffdevice
-- OpentoPD Theme : https://github.com/sirpdboy/luci-theme-opentopd
-- Ku Cat Theme : https://github.com/sirpdboy/luci-theme-kucat
-- Ku Cat Theme Config : https://github.com/sirpdboy/luci-app-kucat-config
-- NFT Time Control : https://github.com/sirpdboy/luci-app-timecontrol
-- Parent Control: https://github.com/sirpdboy/luci-theme-parentcontrol
-- Eqos Plus: https://github.com/sirpdboy/luci-app-eqosplus
-- Advanced : https://github.com/sirpdboy/luci-app-advanced
-- ddns-go : https://github.com/sirpdboy/luci-app-ddns-go
-- Advanced Plus): https://github.com/sirpdboy/luci-app-advancedplus
-- Net Wizard: https://github.com/sirpdboy/luci-app-netwizard
-- Part Exp: https://github.com/sirpdboy/luci-app-partexp
-- Lukcy: https://github.com/sirpdboy/luci-app-lukcy
-
-## HELP
-
-| | |
-| :-----------------: | :-------------: |
-| |  |
-
-
-
-
-
- [](https://t.me/joinchat/AAAAAEpRF88NfOK5vBXGBQ)
diff --git a/luci-app-kucat-config/README_CN.md b/luci-app-kucat-config/README_CN.md
deleted file mode 100644
index 043d57b..0000000
--- a/luci-app-kucat-config/README_CN.md
+++ /dev/null
@@ -1,114 +0,0 @@
- [](https://t.me/joinchat/AAAAAEpRF88NfOK5vBXGBQ)
-
-
- luci-app-kucat-config
-
-
-
-
-
-
-
-
-
-[中文] | [English](README.md)
-
-
-
-# 酷猫主题链接: https://github.com/sirpdboy/luci-theme-kucat
-# 进队设置系统工具-酷猫/argon/design三主题设置下载链接: [https://github.com/sirpdboy/luci-app-advancedplus](https://github.com/sirpdboy/luci-app-advancedplus/releases)
-# 酷猫主题设置工具 链接: [https://github.com/sirpdboy/luci-app-kucat-config](https://github.com/sirpdboy/luci-app-kucat-config)
-- 开发时间:2021年12月
-- 发布时间:2023年2月
-- 开源时间:2023年4月
-- 支持 支持官网openwrt 18.06-24.10分支 开源时间:2023年9月 最新版本:1.1.0
-
-# 特殊说明
-- 当系统中 有进阶设置又有 KUCAT主题设置工具时, 以进阶设置中的设置为准。
-- 如果设置上有问题,可以在SSH登陆后,用4号功能恢复设置。
-- 如果是进阶设置,恢复默认设置,可以用:/etc/init.d/advancedplus reset
-
-# 功能介绍
-
-- 以酷为美,因动而生.
-- 带5种快捷键的导航栏.
-- 适应目前18.06到23.05的所有版本,对大多数插件等进行适配优化.
-- 针对手机等做了大量优化.
-- 有配套的主题设置工具,可调节快捷键、背景图片、颜色多种方案可供选择。
-- 集成亮色和暗色模式。
-
-#【说在前面的话】此次酷猫主题研发借鉴:Opentopd主题、Jerryk大神argon主题、以及thinktip大神的neobird主题的部分灵感及参考借用部分代码,在这里表示感谢,感谢有你们珠玉在前!
-#目前源码暂不开源,如果你们需要联系本人可以免费获取。
-
-## 目前最新版本,仅VIP固件中集成,开源升级时间在6个月以后。感谢大家支持与理解。
-
-# 为什么叫酷猫 ?
-
-## 二个原因:
-
-## 一是这主题就是为酷而生,一切只为酷与简单!从此让你的主题不再单调!让主题活起来!按钮动起来!全面掌控主题颜色设置!
-
-## 二是因为这主题是在九个方案版本中测试研究后决定出来(正合猫有9条命),另外是本人(年后到现在)在无数个夜班熬呀熬,熬出来的,都熬成夜猫子了,所以定名:【英文名:KuCat】,【中文名:酷猫】。
-
-
-另外要说明一下就是本人弄固件不只是编译与搬运,是开发,是创新,更是爱好!请不要将本人与一些搬运工编译的固件来比较!那样会只会显得你很无知。
-
-当然在OPENWRT的世界里比本人更优秀更专业的人士大有人在,本人能做的就是尽自己微薄浅湿的力量,尽量让固件更好用和更少的BUG。
-
-不管是本人还是别人开发的插件,这里面的每一行代码,都凝聚着开发者在后面无数辛勤汗水和智慧的付出,请大家珍重他们的劳动成果与心血付出!
-
-也许某些功能某些要求还达不到你的预期要求,有道是一人难满百人意,有些可能是本身实现不了,有些可能是能力不足的原因实现不了.
-
-不管如何,如果有问题或者BUG请多给理解与支持【当然有能力的请给这些开发者们更多的物资支持】!
-
-在此要特别感谢VIP群里以及不在群里的所有为爱发电的好朋友们!是你们给本人无尽的力量!!谢谢!!
-
-## 界面
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-# My other project
-
-- 路由安全看门狗 :https://github.com/sirpdboy/luci-app-watchdog
-- 网络速度测试 :https://github.com/sirpdboy/luci-app-netspeedtest
-- 计划任务插件(原定时设置) : https://github.com/sirpdboy/luci-app-taskplan
-- 关机功能插件 : https://github.com/sirpdboy/luci-app-poweroffdevice
-- opentopd主题 : https://github.com/sirpdboy/luci-theme-opentopd
-- kucat酷猫主题: https://github.com/sirpdboy/luci-theme-kucat
-- kucat酷猫主题设置工具: https://github.com/sirpdboy/luci-app-kucat-config
-- NFT版上网时间控制插件: https://github.com/sirpdboy/luci-app-timecontrol
-- 家长控制: https://github.com/sirpdboy/luci-theme-parentcontrol
-- 定时限速: https://github.com/sirpdboy/luci-app-eqosplus
-- 系统高级设置 : https://github.com/sirpdboy/luci-app-advanced
-- ddns-go动态域名: https://github.com/sirpdboy/luci-app-ddns-go
-- 进阶设置(系统高级设置+主题设置kucat/agron/opentopd): https://github.com/sirpdboy/luci-app-advancedplus
-- 网络设置向导: https://github.com/sirpdboy/luci-app-netwizard
-- 一键分区扩容: https://github.com/sirpdboy/luci-app-partexp
-- lukcy大吉: https://github.com/sirpdboy/luci-app-lukcy
-
-## 捐助
-
-
-
-| | |
-| :-----------------: | :-------------: |
-| |  |
-
-
-
-
-
- [](https://t.me/joinchat/AAAAAEpRF88NfOK5vBXGBQ)
-
diff --git a/luci-app-kucat-config/doc/kucat1.png b/luci-app-kucat-config/doc/kucat1.png
deleted file mode 100644
index d630f75..0000000
Binary files a/luci-app-kucat-config/doc/kucat1.png and /dev/null differ
diff --git a/luci-app-kucat-config/doc/kucat2.png b/luci-app-kucat-config/doc/kucat2.png
deleted file mode 100644
index 84e8e85..0000000
Binary files a/luci-app-kucat-config/doc/kucat2.png and /dev/null differ
diff --git a/luci-app-kucat-config/doc/kucat3.png b/luci-app-kucat-config/doc/kucat3.png
deleted file mode 100644
index 3066cf6..0000000
Binary files a/luci-app-kucat-config/doc/kucat3.png and /dev/null differ
diff --git a/luci-app-kucat-config/doc/kucat4.png b/luci-app-kucat-config/doc/kucat4.png
deleted file mode 100644
index a5a7aa9..0000000
Binary files a/luci-app-kucat-config/doc/kucat4.png and /dev/null differ
diff --git a/luci-app-kucat-config/doc/kucatz1.png b/luci-app-kucat-config/doc/kucatz1.png
deleted file mode 100644
index 0ea3a6f..0000000
Binary files a/luci-app-kucat-config/doc/kucatz1.png and /dev/null differ
diff --git a/luci-app-kucat-config/doc/kucatz2.png b/luci-app-kucat-config/doc/kucatz2.png
deleted file mode 100644
index 2a63cd8..0000000
Binary files a/luci-app-kucat-config/doc/kucatz2.png and /dev/null differ
diff --git a/luci-app-kucat-config/luasrc/controller/kucat-config.lua b/luci-app-kucat-config/luasrc/controller/kucat-config.lua
deleted file mode 100644
index b8516df..0000000
--- a/luci-app-kucat-config/luasrc/controller/kucat-config.lua
+++ /dev/null
@@ -1,19 +0,0 @@
---[[
-LuCI - Lua Configuration kucat-config
- Copyright (C) 2022-2024 sirpdboy https://github.com/sirpdboy/luci-app-kucat-config
-]]--
-
-module("luci.controller.kucat-config", package.seeall)
-
-function index()
- if not nixio.fs.access("/etc/config/kucat") then
- return
- end
- local page
- page = entry({"admin","system","kucat-config"},alias("admin","system","kucat-config","kucat-config"),_("KuCat Theme Config"),88)
- page.dependent = true
- page.acl_depends = { "luci-app-kucat-config" }
- entry({"admin","system","kucat-config","kucat-config"},cbi("kucat-config/kucat-config"),_("KuCat Theme Config"),40).leaf = true
- entry({"admin", "system","kucat-config","upload"}, form("kucat-config/upload"), _("Login Background Upload"), 70).leaf = true
- entry({"admin", "system","kucat-config","kucatupload"}, form("kucat-config/kucatupload"), _("Desktop background upload"), 80).leaf = true
-end
diff --git a/luci-app-kucat-config/luasrc/model/cbi/kucat-config/kucat-config.lua b/luci-app-kucat-config/luasrc/model/cbi/kucat-config/kucat-config.lua
deleted file mode 100644
index d835e46..0000000
--- a/luci-app-kucat-config/luasrc/model/cbi/kucat-config/kucat-config.lua
+++ /dev/null
@@ -1,174 +0,0 @@
-local a, t, e
-local opacity_sets = {
- 0,
- 1,
- 2,
- 3,
- 4,
- 5,
- 6,
- 7,
- 8,
- 9,
- 10,
- 20
-}
-
-local ts_sets = {
- 0,
- 0.05,
- 0.1,
- 0.2,
- 0.3,
- 0.4,
- 0.5,
- 0.6,
- 0.7,
- 0.8,
- 0.9,
- 0.95,
- 1
-}
-a = Map("kucat")
-a.title = translate("KuCat Theme Config")
-a.description = translate("Set and manage features such as KuCat themed background wallpaper, main background color, partition background, transparency, blur, toolbar retraction and shortcut pointing.")..
-translate("There are 6 preset color schemes, and only the desktop background image can be set to display or not. The custom color values are RGB values such as 255,0,0 (representing red), and a blur radius of 0 indicates no lag in the image.")..
-translate("For specific usage, see:")..translate("GitHub @sirpdboy/luci-app-kucat-config ")
-t = a:section(TypedSection, "basic", translate("Settings"))
-t.anonymous = true
-
-e = t:option(ListValue, 'background', translate('Wallpaper Source'),translate('Local wallpapers need to be uploaded on their own, and only the first update downloaded on the same day will be automatically downloaded.'))
-e:value('0', translate('Local wallpaper'))
-e:value('1', translate('Auto download Iciba wallpaper'))
-e:value('2', translate('Auto download unsplash wallpaper'))
-e:value('3', translate('Auto download Bing wallpaper'))
-e:value('4', translate('Auto download Bird 4K wallpaper'))
-e.default = '0'
-e.rmempty = false
-
-e = t:option(Flag, "bklock", translate("Wallpaper synchronization"),translate("Is the login wallpaper consistent with the desktop wallpaper? If not selected, it indicates that the desktop wallpaper and login wallpaper are set independently."))
-e.rmempty = false
-e.default = '0'
-
-e = t:option(Flag, "setbar", translate("Expand Toolbar"),translate('Expand or shrink the toolbar'))
-e.rmempty = false
-e.default = '0'
-
-e = t:option(Flag, "bgqs", translate("Refreshing mode"))
-e.rmempty = false
-e.default = '0'
-
-e = t:option(Flag, "dayword", translate("Enable Daily Word"))
-e.rmempty = false
-e.default = '0'
-
-e = t:option(Value, 'gohome', translate('Status Homekey settings'))
-e:value('overview', translate('Overview'))
-e:value('online', translate('Online User'))
-e:value('realtime', translate('Realtime Graphs'))
-e:value('netdata', translate('NetData'))
-e.default = 'overview'
-e.rmempty = false
-
-e = t:option(Value, 'gouser', translate('System Userkey settings'))
-e:value('kucat-config', translate('Advanced plus'))
-e:value('netwizard', translate('Inital Setup'))
-e:value('system', translate('System'))
-e:value('admin', translate('Administration'))
-e:value('terminal', translate('TTYD Terminal'))
-e:value('packages', translate('Software'))
-e:value('filetransfer', translate('FileTransfer'))
-e.default = 'kucat-config'
-e.rmempty = false
-
-e = t:option(Value, 'gossr', translate('Services Ssrkey settings'))
-e:value('shadowsocksr', translate('SSR'))
-e:value('bypass', translate('bypass'))
-e:value('nikki', translate('nikki[Mihomo]'))
-e:value('passwall', translate('passwall'))
-e:value('passwall2', translate('passwall2'))
-e:value('openclash', translate('OpenClash'))
-e:value('homeproxy', translate('HomeProxy'))
-e:value('vssr', translate('Hello World'))
-e.default = 'bypass'
-e.rmempty = false
-
-e = t:option(Flag, "fontmode", translate("Care mode (large font)"))
-e.rmempty = false
-e.default = '0'
-
-e = t:option(DummyValue, '', translate('RGB Palette Tools'))
-e.rawhtml = true
-e.template = 'kucat-config/color_primary'
-
-t = a:section(TypedSection, "theme", translate("Color scheme list"))
-t.template = "cbi/tblsection"
-t.anonymous = true
-t.addremove = true
-
-e = t:option(Value, 'remarks', translate('Remarks'))
-
-e = t:option(Flag, "use", translate("Enable color matching"))
-e.rmempty = false
-e.default = '1'
-
-e = t:option(ListValue, 'mode', translate('Theme mode'))
-e:value('auto', translate('Auto'))
-e:value('light', translate('Light'))
-e:value('dark', translate('Dark'))
-e.default = 'light'
-
-e = t:option(Value, 'primary_rgbm', translate('Main Background color(RGB)'))
-e:value("blue",translate("RoyalBlue"))
-e:value("green",translate("MediumSeaGreen"))
-e:value("orange",translate("SandyBrown"))
-e:value("red",translate("TomatoRed"))
-e:value("black",translate("Black tea eye protection gray"))
-e:value("gray",translate("Cool night time(gray and dark)"))
-e:value("bluets",translate("Cool Ocean Heart (transparent and bright)"))
-e.default='green'
-e.datatype = ufloat
-e.default='74,161,133'
-
-e = t:option(Flag, "bkuse", translate("Enable wallpaper"))
-e.rmempty = false
-e.default = '1'
-
-e = t:option(Value, 'primary_rgbm_ts', translate('Wallpaper transparency'))
-for _, v in ipairs(ts_sets) do
- e:value(v)
-end
-e.datatype = ufloat
-e.rmempty = false
-
-e.default='0.5'
-
-e = t:option(Value, 'primary_opacity', translate('Wallpaper blur radius'))
-for _, v in ipairs(opacity_sets) do
- e:value(v)
-end
-e.datatype = ufloat
-e.rmempty = false
-
-e.default='10'
-
-e = t:option(Value, 'primary_rgbs', translate('Fence background(RGB)'))
-e.default='225,112,88'
-e.datatype = ufloat
-
-e = t:option(Value, 'primary_rgbs_ts', translate('Fence background transparency'))
-for _, v in ipairs(ts_sets) do
- e:value(v)
-end
-e.datatype = ufloat
-e.rmempty = false
-
-e.default='0.3'
-
-a.apply_on_parse = true
-a.on_after_apply = function(self,map)
- luci.sys.exec("/etc/init.d/kucat start >/dev/null 2>&1")
- luci.http.redirect(luci.dispatcher.build_url("admin","system","kucat-config","kucat-config"))
-end
-
-return a
diff --git a/luci-app-kucat-config/luasrc/model/cbi/kucat-config/kucatupload.lua b/luci-app-kucat-config/luasrc/model/cbi/kucat-config/kucatupload.lua
deleted file mode 100644
index f9fd89d..0000000
--- a/luci-app-kucat-config/luasrc/model/cbi/kucat-config/kucatupload.lua
+++ /dev/null
@@ -1,126 +0,0 @@
-local nxfs = require 'nixio.fs'
-local wa = require 'luci.tools.webadmin'
-local sys = require 'luci.sys'
-local http = require 'luci.http'
-local nutil = require 'nixio.util'
-local name = 'kucat'
-local uci = require 'luci.model.uci'.cursor()
-
-local function get_root_fs()
- if nxfs.stat('/overlay') then
- return '/overlay'
- else
- return '/'
- end
-end
-
-local fstat = nxfs.statvfs(get_root_fs())
-local space_total = fstat and fstat.blocks or 0
-local space_free = fstat and fstat.bfree or 0
-local space_used = space_total - space_free
-
-local free_byte = space_free * fstat.frsize
-
-function glob(...)
- local iter, code, msg = nxfs.glob(...)
- if iter then
- return nutil.consume(iter)
- else
- return nil, code, msg
- end
-end
-
-ful = SimpleForm('upload', translate('Upload (Free: ') .. wa.byte_format(free_byte) .. ')', translate("You can upload jpg, png, and gif files to create your favorite kucat desktop interface"))
-ful.reset = false
-ful.submit = false
-
-sul = ful:section(SimpleSection, '', translate("Upload file to '/www/luci-static/kucat/bg/'"))
-fu = sul:option(FileUpload, '')
-fu.template = 'kucat-config/other_upload'
-um = sul:option(DummyValue, '', nil)
-um.template = 'kucat-config/other_dvalue'
-
-local dir, fd
-dir = '/www/luci-static/kucat/bg/'
-nxfs.mkdir(dir)
-http.setfilehandler(
- function(meta, chunk, eof)
- if not fd then
- if not meta then
- return
- end
-
- if meta and chunk then
- fd = nixio.open(dir .. meta.file, 'w')
- end
-
- if not fd then
- um.value = translate('Create upload file error.')
- return
- end
- end
- if chunk and fd then
- fd:write(chunk)
- end
- if eof and fd then
- fd:close()
- fd = nil
- um.value = translate('File saved to') .. ' "/www/luci-static/kucat/bg/' .. meta.file .. '"'
- end
- end
-)
-
-if http.formvalue('upload') then
- local f = http.formvalue('ulfile')
- if #f <= 0 then
- um.value = translate('No specify upload file.')
- end
-end
-
-local function getSizeStr(size)
- local i = 0
- local byteUnits = {' kB', ' MB', ' GB', ' TB'}
- repeat
- size = size / 1024
- i = i + 1
- until (size <= 1024)
- return string.format('%.1f', size) .. byteUnits[i]
-end
-
-local inits, attr = {}
-for i, f in ipairs(glob(dir .. '*')) do
- attr = nxfs.stat(f)
- if attr then
- inits[i] = {}
- inits[i].name = nxfs.basename(f)
- inits[i].mtime = os.date('%Y-%m-%d %H:%M:%S', attr.mtime)
- inits[i].modestr = attr.modestr
- inits[i].size = getSizeStr(attr.size)
- inits[i].remove = 0
- inits[i].install = false
- end
-end
-
-form = SimpleForm('filelist', translate('Background file list'), nil)
-form.reset = false
-form.submit = false
-
-tb = form:section(Table, inits)
-nm = tb:option(DummyValue, 'name', translate('File name'))
-mt = tb:option(DummyValue, 'mtime', translate('Modify time'))
-sz = tb:option(DummyValue, 'size', translate('Size'))
-btnrm = tb:option(Button, 'remove', translate('Remove'))
-btnrm.render = function(self, section, scope)
- self.inputstyle = 'remove'
- Button.render(self, section, scope)
-end
-
-btnrm.write = function(self, section)
- local v = nxfs.unlink(dir .. nxfs.basename(inits[section].name))
- if v then
- table.remove(inits, section)
- end
- return v
-end
-
-return ful, form
diff --git a/luci-app-kucat-config/luasrc/model/cbi/kucat-config/upload.lua b/luci-app-kucat-config/luasrc/model/cbi/kucat-config/upload.lua
deleted file mode 100644
index 6e501cf..0000000
--- a/luci-app-kucat-config/luasrc/model/cbi/kucat-config/upload.lua
+++ /dev/null
@@ -1,127 +0,0 @@
-local nxfs = require 'nixio.fs'
-local wa = require 'luci.tools.webadmin'
-local sys = require 'luci.sys'
-local http = require 'luci.http'
-local nutil = require 'nixio.util'
-local name = 'kucat'
-local uci = require 'luci.model.uci'.cursor()
-
-
-local function get_root_fs()
- if nxfs.stat('/overlay') then
- return '/overlay'
- else
- return '/'
- end
-end
-
-local fstat = nxfs.statvfs(get_root_fs())
-local space_total = fstat and fstat.blocks or 0
-local space_free = fstat and fstat.bfree or 0
-local space_used = space_total - space_free
-
-local free_byte = space_free * fstat.frsize
-
-function glob(...)
- local iter, code, msg = nxfs.glob(...)
- if iter then
- return nutil.consume(iter)
- else
- return nil, code, msg
- end
-end
-
-ful = SimpleForm('upload', translate('Upload (Free: ') .. wa.byte_format(free_byte) .. ')', translate("You can upload files such as jpg,png,gif files, To change the login page background.Uploading Kucat, Agron, and Opentopd all three themes can be used once"))
-ful.reset = false
-ful.submit = false
-
-sul = ful:section(SimpleSection, '', translate("Upload file to '/www/luci-static/resources/background/'"))
-fu = sul:option(FileUpload, '')
-fu.template = 'kucat-config/other_upload'
-um = sul:option(DummyValue, '', nil)
-um.template = 'kucat-config/other_dvalue'
-
-local dir, fd
-dir = '/www/luci-static/resources/background/'
-nxfs.mkdir(dir)
-http.setfilehandler(
- function(meta, chunk, eof)
- if not fd then
- if not meta then
- return
- end
-
- if meta and chunk then
- fd = nixio.open(dir .. meta.file, 'w')
- end
-
- if not fd then
- um.value = translate('Create upload file error.')
- return
- end
- end
- if chunk and fd then
- fd:write(chunk)
- end
- if eof and fd then
- fd:close()
- fd = nil
- um.value = translate('File saved to') .. ' "/www/luci-static/resources/background/' .. meta.file .. '"'
- end
- end
-)
-
-if http.formvalue('upload') then
- local f = http.formvalue('ulfile')
- if #f <= 0 then
- um.value = translate('No specify upload file.')
- end
-end
-
-local function getSizeStr(size)
- local i = 0
- local byteUnits = {' kB', ' MB', ' GB', ' TB'}
- repeat
- size = size / 1024
- i = i + 1
- until (size <= 1024)
- return string.format('%.1f', size) .. byteUnits[i]
-end
-
-local inits, attr = {}
-for i, f in ipairs(glob(dir .. '*')) do
- attr = nxfs.stat(f)
- if attr then
- inits[i] = {}
- inits[i].name = nxfs.basename(f)
- inits[i].mtime = os.date('%Y-%m-%d %H:%M:%S', attr.mtime)
- inits[i].modestr = attr.modestr
- inits[i].size = getSizeStr(attr.size)
- inits[i].remove = 0
- inits[i].install = false
- end
-end
-
-form = SimpleForm('filelist', translate('Background file list'), nil)
-form.reset = false
-form.submit = false
-
-tb = form:section(Table, inits)
-nm = tb:option(DummyValue, 'name', translate('File name'))
-mt = tb:option(DummyValue, 'mtime', translate('Modify time'))
-sz = tb:option(DummyValue, 'size', translate('Size'))
-btnrm = tb:option(Button, 'remove', translate('Remove'))
-btnrm.render = function(self, section, scope)
- self.inputstyle = 'remove'
- Button.render(self, section, scope)
-end
-
-btnrm.write = function(self, section)
- local v = nxfs.unlink(dir .. nxfs.basename(inits[section].name))
- if v then
- table.remove(inits, section)
- end
- return v
-end
-
-return ful, form
diff --git a/luci-app-kucat-config/luasrc/view/kucat-config/color_primary.htm b/luci-app-kucat-config/luasrc/view/kucat-config/color_primary.htm
deleted file mode 100644
index 289a1b6..0000000
--- a/luci-app-kucat-config/luasrc/view/kucat-config/color_primary.htm
+++ /dev/null
@@ -1,40 +0,0 @@
-<%+cbi/valueheader%>
-
-
-<%+cbi/valuefooter%>
-
-
diff --git a/luci-app-kucat-config/luasrc/view/kucat-config/other_dvalue.htm b/luci-app-kucat-config/luasrc/view/kucat-config/other_dvalue.htm
deleted file mode 100644
index 296c61e..0000000
--- a/luci-app-kucat-config/luasrc/view/kucat-config/other_dvalue.htm
+++ /dev/null
@@ -1,8 +0,0 @@
-<%+cbi/valueheader%>
-
-<%
- local val = self:cfgvalue(section) or self.default or ""
- write(pcdata(val))
-%>
-
-<%+cbi/valuefooter%>
diff --git a/luci-app-kucat-config/luasrc/view/kucat-config/other_upload.htm b/luci-app-kucat-config/luasrc/view/kucat-config/other_upload.htm
deleted file mode 100644
index 97a5158..0000000
--- a/luci-app-kucat-config/luasrc/view/kucat-config/other_upload.htm
+++ /dev/null
@@ -1,5 +0,0 @@
-<%+cbi/valueheader%>
- <%:Choose local file:%>
-
-
-<%+cbi/valuefooter%>
diff --git a/luci-app-kucat-config/po/zh-cn/kucat-config.po b/luci-app-kucat-config/po/zh-cn/kucat-config.po
deleted file mode 100644
index dc0cdde..0000000
--- a/luci-app-kucat-config/po/zh-cn/kucat-config.po
+++ /dev/null
@@ -1,180 +0,0 @@
-msgid ""
-msgstr "Content-Type: text/plain; charset=UTF-8\n"
-
-msgid "KuCat Theme Config"
-msgstr "KuCat主题设置"
-
-msgid "Theme Config"
-msgstr "主题设置"
-
-msgid "Set and manage features such as KuCat theme background wallpaper, main background color, partition background, transparency, blur, toolbar retraction and shortcut pointing."
-msgstr "设置和管理酷猫主题登陆壁纸、桌面壁纸、主背景色、隔栏背景、透明度、模糊度、工具栏收放和快捷指向等功能。"
-
-msgid "Wallpaper Source"
-msgstr "壁纸来源"
-
-msgid "Local wallpaper"
-msgstr "本地壁纸库"
-
-msgid "Auto download Bing wallpaper"
-msgstr "自动下载Bing每日壁纸"
-
-msgid "Auto download unsplash wallpaper"
-msgstr "自动下载unsplash壁纸"
-
-msgid "Auto download Iciba wallpaper"
-msgstr "自动下载词霸每日壁纸"
-
-msgid "Auto download Bird 4K wallpaper"
-msgstr "自动下载360小鸟4K高清壁纸(每日4千张随机选)"
-
-msgid "Wallpaper synchronization"
-msgstr "壁纸同步"
-
-msgid "Refreshing mode"
-msgstr "清爽模式"
-
-msgid "Enable Daily Word"
-msgstr "启用每日一言"
-
-msgid "Care mode (large font)"
-msgstr "关怀模式(大字体)"
-
-msgid "Expand Toolbar"
-msgstr "展开导航栏"
-
-msgid "Services Ssrkey settings"
-msgstr "服务相关快捷键设置"
-
-msgid "System Userkey settings"
-msgstr "系统相关快捷键设置"
-
-msgid "Status Homekey settings"
-msgstr "状态相关快捷键设置"
-
-msgid "Color scheme list"
-msgstr "颜色配置方案列表"
-
-msgid "Enable color matching"
-msgstr "启用配色方案"
-
-msgid "Theme mode"
-msgstr "主题模式"
-
-msgid "Light"
-msgstr "亮色"
-
-msgid "Auto"
-msgstr "自动"
-
-msgid "Dark"
-msgstr "暗色"
-
-msgid "RoyalBlue"
-msgstr "皇家高端蓝"
-
-msgid "MediumSeaGreen"
-msgstr "中海清爽绿"
-
-msgid "SandyBrown"
-msgstr "桑迪布朗黄"
-
-msgid "Black tea eye protection gray"
-msgstr "黑茶护眼灰"
-
-msgid "TomatoRed"
-msgstr "番茄别样红"
-
-msgid "Cool night time(gray and dark)"
-msgstr "炫酷黑夜时空(灰度暗)"
-
-msgid "Cool Ocean Heart (transparent and bright)"
-msgstr "炫酷海洋之心(透明亮)"
-
-msgid "Enable wallpaper"
-msgstr "启用壁纸"
-
-msgid "Wallpaper blur radius"
-msgstr "壁纸模糊半径"
-
-msgid "Wallpaper transparency"
-msgstr "壁纸透明度"
-
-msgid "Main Background color(RGB)"
-msgstr "主背景色(RGB)"
-
-msgid "Fence background transparency"
-msgstr "隔栏背景透明度"
-
-msgid "Fence background(RGB)"
-msgstr "隔栏背景色(RGB)"
-
-msgid "Action"
-msgstr "操作"
-
-msgid "Delete"
-msgstr "删除"
-
-msgid "Expecting: %s"
-msgstr "请输入:%s"
-
-msgid "Failed to upload file: %s."
-msgstr "上传文件失败:%s。"
-
-msgid "Filename"
-msgstr "文件名"
-
-msgid "Files will be uploaded to %s."
-msgstr "文件将被上传至%s。"
-
-msgid "Modified date"
-msgstr "修改时间"
-
-msgid "No files found."
-msgstr "没有找到文件。"
-
-msgid "Save current settings"
-msgstr "保存当前设置"
-
-msgid "Save settings"
-msgstr "保存设置"
-
-msgid "Size"
-msgstr "大小"
-
-msgid "Upload background"
-msgstr "上传背景"
-
-msgid "Upload background (available space: %1024.2mB)"
-msgstr "上传背景(可用空间:%1024.2mB)"
-
-msgid "Upload..."
-msgstr "上传..."
-
-msgid ""
-"You can upload files such as gif/jpg/mp4/png/webm/webp files, to change the "
-"login page background."
-msgstr ""
-"你可以上传 gif/jpg/mp4/png/webm/webp 等格式的文件,以创建自己喜欢的登录界面。"
-
-msgid "Login Background Upload"
-msgstr "登录壁纸上传"
-
-msgid "Desktop background upload"
-msgstr "桌面壁纸上传"
-
-msgid "Kucat Theme Desktop Background Wallpaper Upload and Management."
-msgstr "kucat主题桌面背景壁纸文件上传和管理。"
-
-msgid "Kucat theme login background wallpaper upload and management."
-msgstr "kucat 主题登录背景壁纸上传和管理"
-
-msgid "Background file list"
-msgstr "背景文件列表"
-
-msgid "Progress bar Font Color"
-msgstr "进度条字体颜色"
-
-msgid ""
-msgstr ""
-
diff --git a/luci-app-kucat-config/po/zh_Hans/kucat-config.po b/luci-app-kucat-config/po/zh_Hans/kucat-config.po
deleted file mode 100644
index a1f34bc..0000000
--- a/luci-app-kucat-config/po/zh_Hans/kucat-config.po
+++ /dev/null
@@ -1,258 +0,0 @@
-msgid ""
-msgstr "Content-Type: text/plain; charset=UTF-8\n"
-
-msgid "KuCat Theme Config"
-msgstr "KuCat主题设置"
-
-msgid "Theme Config"
-msgstr "主题设置"
-
-
-msgid "Here you can set the blur and transparency of the login page of argon theme, and manage the background pictures and videos.[Chrome is recommended]"
-msgstr "在这里你可以设置argon 主题的登录页面的模糊和透明度。[建议使用 Chrome]"
-
-msgid "Here you can set the mode of the theme and change the proxy tool icon in the navigation bar. [Recommend Chrome]"
-msgstr "这里可以设置主题的模式和更换导航栏的代理工具图标。[建议使用 Chrome]"
-
-msgid "Wallpaper Source"
-msgstr "壁纸来源"
-
-msgid "Local wallpaper"
-msgstr "本地壁纸库"
-
-msgid "Auto download Bing wallpaper"
-msgstr "自动下载Bing每日壁纸"
-
-msgid "Auto download unsplash wallpaper"
-msgstr "自动下载unsplash壁纸"
-
-msgid "Auto download Iciba wallpaper"
-msgstr "自动下载词霸每日壁纸"
-
-msgid "Auto download Bird 4K wallpaper"
-msgstr "自动下载360小鸟4K高清壁纸(每日4千张随机选)"
-
-msgid "Wallpaper synchronization"
-msgstr "壁纸同步"
-
-msgid "Refreshing mode"
-msgstr "清爽模式"
-
-msgid "Enable Daily Word"
-msgstr "启用每日一言"
-
-msgid "Care mode (large font)"
-msgstr "关怀模式(大字体)"
-
-msgid "Expand Toolbar"
-msgstr "展开导航栏"
-
-
-msgid "You can upload jpg, png, and gif files to create your favorite kucat desktop interface"
-msgstr "你可以上传jpg、png、gif文件,以创建自己喜欢的kucat桌面界面"
-
-msgid "You can upload files such as jpg,png,gif files, To change the login page background.Uploading Kucat, Agron, and Opentopd all three themes can be used once"
-msgstr "你可以上传jpg、png、gif文件,以创建自己喜欢的登录界面,上传一次Kucat,Agron,Opentopd三种主题都可使用"
-
-msgid "Save Changes"
-msgstr "保存更改"
-
-msgid "Choose local file:"
-msgstr "选择本地文件:"
-
-msgid "Couldn't open file:"
-msgstr "无法打开文件:"
-
-msgid "Create upload file error."
-msgstr "创建上传文件失败。"
-
-msgid "File name"
-msgstr "文件名"
-
-msgid "File saved to"
-msgstr "文件保存到"
-
-msgid "FileTransfer"
-msgstr "文件传输"
-
-msgid "Install"
-msgstr "安装"
-
-msgid "Attributes"
-msgstr "属性"
-
-msgid "Modify time"
-msgstr "修改时间"
-
-msgid "No specify upload file."
-msgstr "未指定上传文件"
-
-msgid "Path on Route:"
-msgstr "路由根目录:"
-
-msgid "Remove"
-msgstr "移除"
-
-msgid "Size"
-msgstr "大小"
-
-msgid "Upload (Free:"
-msgstr "上传 (剩余空间:"
-
-msgid "Background file list"
-msgstr "壁纸文件列表"
-
-msgid "Upload file to '/www/luci-static/resources/background/'"
-msgstr "文件将上传到'/www/luci-static/resources/background/'"
-
-msgid "Color scheme list"
-msgstr "颜色配置方案列表"
-
-msgid "List menu background color transparency"
-msgstr "列表菜单背景色透明度"
-
-
-msgid "There are 6 preset color schemes, and only the desktop background image can be set to display or not. The custom color values are RGB values such as 255,0,0 (representing red), and a blur radius of 0 indicates no lag in the image."
-msgstr "预置6种颜色方案,只可设置桌面壁纸图显示与否。自定义的颜色值是RGB值如:255,0,0(表示红色),模糊半径为0则画面无卡顿。"
-
-msgid "Set and manage features such as KuCat themed background wallpaper, main background color, partition background, transparency, blur, toolbar retraction and shortcut pointing."
-msgstr "设置和管理酷猫主题登陆壁纸、桌面壁纸、主背景色、隔栏背景、透明度、模糊度、工具栏收放和快捷指向等功能。"
-
-msgid "For specific usage, see:"
-msgstr "使用说明:"
-
-msgid "Services Ssrkey settings"
-msgstr "服务相关快捷键设置"
-
-msgid "System Userkey settings"
-msgstr "系统相关快捷键设置"
-
-msgid "Status Homekey settings"
-msgstr "状态相关快捷键设置"
-
-msgid "Color scheme list"
-msgstr "颜色配置方案列表"
-
-msgid "Enable color matching"
-msgstr "启用配色方案"
-
-msgid "Theme mode"
-msgstr "主题模式"
-
-msgid "Light"
-msgstr "亮色"
-
-msgid "Auto"
-msgstr "自动"
-
-msgid "Dark"
-msgstr "暗色"
-
-msgid "RoyalBlue"
-msgstr "皇家高端蓝"
-
-msgid "MediumSeaGreen"
-msgstr "中海清爽绿"
-
-msgid "SandyBrown"
-msgstr "桑迪布朗黄"
-
-msgid "Black tea eye protection gray"
-msgstr "黑茶护眼灰"
-
-msgid "TomatoRed"
-msgstr "番茄别样红"
-
-msgid "Cool night time(gray and dark)"
-msgstr "炫酷黑夜时空(灰度暗)"
-
-msgid "Cool Ocean Heart (transparent and bright)"
-msgstr "炫酷海洋之心(透明亮)"
-
-msgid "Enable wallpaper"
-msgstr "启用壁纸"
-
-msgid "Wallpaper blur radius"
-msgstr "壁纸模糊半径"
-
-msgid "Wallpaper transparency"
-msgstr "壁纸透明度"
-
-msgid "Main Background color(RGB)"
-msgstr "主背景色(RGB)"
-
-msgid "Fence background transparency"
-msgstr "隔栏背景透明度"
-
-msgid "Fence background(RGB)"
-msgstr "隔栏背景色(RGB)"
-
-msgid "Action"
-msgstr "操作"
-
-msgid "Delete"
-msgstr "删除"
-
-msgid "Expecting: %s"
-msgstr "请输入:%s"
-
-msgid "Failed to upload file: %s."
-msgstr "上传文件失败:%s。"
-
-msgid "Filename"
-msgstr "文件名"
-
-msgid "Files will be uploaded to %s."
-msgstr "文件将被上传至%s。"
-
-msgid "Modified date"
-msgstr "修改时间"
-
-msgid "No files found."
-msgstr "没有找到文件。"
-
-msgid "Save current settings"
-msgstr "保存当前设置"
-
-msgid "Save settings"
-msgstr "保存设置"
-
-msgid "Size"
-msgstr "大小"
-
-msgid "Upload background"
-msgstr "上传背景"
-
-msgid "Upload background (available space: %1024.2mB)"
-msgstr "上传背景(可用空间:%1024.2mB)"
-
-msgid "Upload..."
-msgstr "上传..."
-
-msgid ""
-"You can upload files such as gif/jpg/mp4/png/webm/webp files, to change the "
-"login page background."
-msgstr ""
-"你可以上传 gif/jpg/mp4/png/webm/webp 等格式的文件,以创建自己喜欢的登录界面。"
-
-msgid "Login Background Upload"
-msgstr "登录壁纸上传"
-
-msgid "Desktop background upload"
-msgstr "桌面壁纸上传"
-
-msgid "Kucat Theme Desktop Background Wallpaper Upload and Management."
-msgstr "kucat主题桌面背景壁纸文件上传和管理。"
-
-msgid "Kucat theme login background wallpaper upload and management."
-msgstr "kucat 主题登录背景壁纸上传和管理"
-
-msgid "Background file list"
-msgstr "背景文件列表"
-
-msgid "Progress bar Font Color"
-msgstr "进度条字体颜色"
-
-msgid ""
-msgstr ""
-
diff --git a/luci-app-kucat-config/root/etc/config/kucat b/luci-app-kucat-config/root/etc/config/kucat
deleted file mode 100644
index fc98843..0000000
--- a/luci-app-kucat-config/root/etc/config/kucat
+++ /dev/null
@@ -1,104 +0,0 @@
-config basic
- option gouser 'admin'
- option gohome 'overview'
- option setbar '1'
- option wizard '0'
- option bgqs '1'
- option gossr 'openclash'
- option dayword '0'
- option background '0'
- option bklock '0'
- option mode 'dark'
- option bkuse '0'
- option primary_rgbs_ts '0'
- option primary_rgbbody '33,45,60'
- option primary_opacity '0'
- option primary_rgbs '76,157,69'
- option primary_rgbm_ts '0.9'
- option primary_rgbm '26,131,97'
- option fontmode '0'
- option font_d '1.1rem'
- option font_z '0.92rem'
- option font_x '0.875rem'
-
-
-config theme
- option primary_rgbbody '33,45,60'
- option primary_rgbs '76,157,69'
- option remarks '隔栏经典蓝色'
- option primary_opacity '0'
- option primary_rgbm_ts '0.9'
- option bkuse '0'
- option primary_rgbs_ts '0.1'
- option mode 'light'
- option use '0'
- option primary_rgbm 'blue'
-
-config theme
- option primary_rgbbody '33,45,60'
- option primary_rgbs '76,157,69'
- option primary_rgbm_ts '0.9'
- option primary_opacity '0'
- option bkuse '0'
- option remarks '无隔栏清爽绿'
- option primary_rgbs_ts '0'
- option mode 'dark'
- option use '1'
- option primary_rgbm 'green'
-
-config theme
- option primary_rgb_ts '0.3'
- option primary_rgbm '26,131,97'
- option primary_rgbs '76 157 69'
- option bkuse '1'
- option remarks '无隔栏绿'
- option primary_rgbs_ts '0'
- option primary_opacity '20'
- option primary_rgbm_ts '0.7'
- option mode 'dark'
- option use '0'
-
-config theme
- option primary_rgbm '66,139,139'
- option primary_rgbs 'rgb(55,103,128)'
- option mode 'light'
- option primary_opacity '0'
- option primary_rgbs_ts '0.1'
- option primary_rgbm_ts '1'
- option remarks '隔栏无背景蓝'
- option bkuse '0'
- option use '0'
-
-config theme
- option primary_rgbm '50 67 77'
- option primary_rgbs 'rgb(64, 68, 70)'
- option primary_opacity '0'
- option primary_rgbs_ts '0'
- option remarks '灰无隔栏无背景'
- option bkuse '0'
- option primary_rgbm_ts '0.9'
- option mode 'auto'
- option use '0'
-
-config theme
- option bkuse '1'
- option mode 'dark'
- option primary_rgbm '50 66 77'
- option primary_rgbs '55 80 90'
- option remarks '炫灰隔栏模糊'
- option primary_rgbs_ts '0'
- option use '0'
- option primary_rgbm_ts '0.5'
- option primary_opacity '4'
-
-config theme
- option mode 'light'
- option primary_rgbm '74,161,133'
- option primary_rgbs '76 157 69'
- option primary_rgbs_ts '0.2'
- option remarks '炫绿隔栏模糊'
- option primary_rgbm_ts '0.7'
- option bkuse '1'
- option primary_opacity '4'
- option use '0'
-
diff --git a/luci-app-kucat-config/root/etc/init.d/kucat b/luci-app-kucat-config/root/etc/init.d/kucat
deleted file mode 100644
index dab0644..0000000
--- a/luci-app-kucat-config/root/etc/init.d/kucat
+++ /dev/null
@@ -1,238 +0,0 @@
-#!/bin/sh /etc/rc.common
-# Copyright 2023-2024 sirpdboy team
-
-START=99
-STOP=15
-EXTRA_COMMANDS='reset'
-EXTRA_HELP=" reset Reset to default settings"
-
-reset() {
-rm -rf /etc/config/kucat
-cat </etc/config/kucat
-
-config basic
- option gossr 'bypass'
- option gouser 'kucat'
- option gohome 'overview'
- option fontmode '0'
- option font_d '1.1rem'
- option font_z '0.92rem'
- option font_x '0.875rem'
- option bklock '0'
- option setbar '1'
- option dayword '0'
- option background '0'
- option bgqs '1'
- option mode 'light'
- option primary_rgbbody '248,248,248'
- option primary_rgbm '20,109,179'
- option primary_rgbm_ts '0.9'
- option primary_opacity '0'
- option primary_rgbs_ts '0.1'
- option primary_rgbs '28,66,188'
- option bkuse '0'
-
-config theme
- option primary_rgbbody '33,45,60'
- option primary_rgbs '76,157,69'
- option remarks '隔栏经典蓝色'
- option mode 'light'
- option primary_rgbs_ts '0.1'
- option primary_opacity '0'
- option primary_rgbm_ts '0.9'
- option primary_rgbm 'blue'
- option use '1'
- option bkuse '0'
-
-config theme
- option primary_rgbbody '33,45,60'
- option mode 'light'
- option primary_rgbs '76,157,69'
- option primary_rgbm_ts '0.9'
- option primary_opacity '0'
- option primary_rgbm 'green'
- option bkuse '0'
- option primary_rgbs_ts '0'
- option remarks '无隔栏清爽绿'
- option use '0'
-
-config theme
- option primary_rgb_ts '0.3'
- option primary_rgbm '26,131,97'
- option primary_rgbs '76 157 69'
- option bkuse '1'
- option remarks '无隔栏绿'
- option primary_rgbm_ts '0.6'
- option primary_opacity '6'
- option mode 'light'
- option primary_rgbs_ts '0'
- option use '0'
-
-config theme
- option primary_rgbm '66,139,139'
- option primary_rgbs 'rgb(55,103,128)'
- option mode 'light'
- option primary_opacity '0'
- option primary_rgbs_ts '0.1'
- option primary_rgbm_ts '1'
- option remarks '隔栏无背景蓝'
- option bkuse '0'
- option use '0'
-
-config theme
- option primary_rgbm '50 67 77'
- option primary_rgbs 'rgb(64, 68, 70)'
- option primary_opacity '0'
- option mode 'dark'
- option primary_rgbs_ts '0'
- option remarks '灰无隔栏无背景'
- option bkuse '0'
- option primary_rgbm_ts '1'
- option use '0'
-
-config theme
- option bkuse '1'
- option mode 'dark'
- option primary_rgbm '50 66 77'
- option primary_rgbs '55 80 90'
- option primary_rgbm_ts '0.7'
- option remarks '炫灰隔栏模糊'
- option primary_rgbs_ts '0.5'
- option primary_opacity '4'
- option use '0'
-
-config theme
- option mode 'light'
- option primary_rgbm '74,161,133'
- option primary_rgbs '76 157 69'
- option primary_rgbs_ts '0.2'
- option remarks '炫绿隔栏模糊'
- option primary_rgbm_ts '0.7'
- option bkuse '1'
- option use '1'
- option primary_opacity '4'
-
-EOF
-}
-
-kucatset(){
-sum=$(grep -c 'theme' /etc/config/kucat)
-useset=0
-for i in $(seq 0 $((sum-1)))
-do
- use=$(uci -q get kucat.@theme[$i].use )
-
- if [ "x$use" == "x1" ]; then
- mode=`uci -q get kucat.@theme[$i].mode`
- primary_rgbm=`uci -q get kucat.@theme[$i].primary_rgbm`
- uci set kucat.@basic[0].mode=${mode}
- case "$primary_rgbm" in
- blue)
- uci set kucat.@basic[0].primary_rgbm='20,109,179'
- uci set kucat.@basic[0].bkuse=`uci -q get kucat.@theme[$i].bkuse`
- uci set kucat.@basic[0].primary_rgbm_ts='0.9'
- uci set kucat.@basic[0].primary_opacity='0'
- uci set kucat.@basic[0].primary_rgbs_ts=`uci -q get kucat.@theme[$i].primary_rgbs_ts`
- uci set kucat.@basic[0].primary_rgbs='28,66,188'
- [ $mode = dark ] && uci set kucat.@basic[0].primary_rgbbody='33,45,60' || uci set kucat.@basic[0].primary_rgbbody='248,248,248'
- ;;
- green)
- uci set kucat.@basic[0].bkuse=`uci -q get kucat.@theme[$i].bkuse`
- uci set kucat.@basic[0].primary_rgbm_ts='0.9'
- uci set kucat.@basic[0].primary_opacity='0'
- uci set kucat.@basic[0].primary_rgbm='26,131,97'
- uci set kucat.@basic[0].primary_rgbs_ts=`uci -q get kucat.@theme[$i].primary_rgbs_ts`
- uci set kucat.@basic[0].primary_rgbs='76,157,69'
- [ $mode = dark ] && uci set kucat.@basic[0].primary_rgbbody='33,45,60' || uci set kucat.@basic[0].primary_rgbbody='248,248,248'
- ;;
- orange)
- uci set kucat.@basic[0].bkuse=`uci -q get kucat.@theme[$i].bkuse`
- uci set kucat.@basic[0].primary_rgbm_ts='0.9'
- uci set kucat.@basic[0].primary_opacity='0'
- uci set kucat.@basic[0].primary_rgbm='163,71,6'
- uci set kucat.@basic[0].primary_rgbs_ts=`uci -q get kucat.@theme[$i].primary_rgbs_ts`
- uci set kucat.@basic[0].primary_rgbs='142,93,8'
- [ $mode = dark ] && uci set kucat.@basic[0].primary_rgbbody='33,45,60' || uci set kucat.@basic[0].primary_rgbbody='248,248,248'
- ;;
- red)
- uci set kucat.@basic[0].bkuse=`uci -q get kucat.@theme[$i].bkuse`
- uci set kucat.@basic[0].primary_rgbm_ts='0.9'
- uci set kucat.@basic[0].primary_opacity='0'
- uci set kucat.@basic[0].primary_rgbm='143,32,13'
- uci set kucat.@basic[0].primary_rgbs_ts=`uci -q get kucat.@theme[$i].primary_rgbs_ts`
- uci set kucat.@basic[0].primary_rgbs='243,119,72'
- [ $mode = dark ] && uci set kucat.@basic[0].primary_rgbbody='33,45,60' || uci set kucat.@basic[0].primary_rgbbody='248,248,248'
- ;;
- black)
- uci set kucat.@basic[0].bkuse=`uci -q get kucat.@theme[$i].bkuse`
- uci set kucat.@basic[0].primary_rgbm_ts='0.8'
- uci set kucat.@basic[0].primary_opacity='0'
- uci set kucat.@basic[0].primary_rgbm='25,25,26'
- uci set kucat.@basic[0].primary_rgbs_ts=`uci -q get kucat.@theme[$i].primary_rgbs_ts`
- uci set kucat.@basic[0].primary_rgbs='55,80,90'
- [ $mode = dark ] && uci set kucat.@basic[0].primary_rgbbody='33,45,60' || uci set kucat.@basic[0].primary_rgbbody='248,248,248'
- ;;
- gray)
- uci set kucat.@basic[0].bkuse=`uci -q get kucat.@theme[$i].bkuse`
- uci set kucat.@basic[0].primary_rgbm_ts='0.7'
- uci set kucat.@basic[0].primary_opacity='5'
- uci set kucat.@basic[0].primary_rgbm='50,67,77'
- uci set kucat.@basic[0].primary_rgbs_ts=`uci -q get kucat.@theme[$i].primary_rgbs_ts`
- uci set kucat.@basic[0].primary_rgbs='64,68,70'
- [ $mode = dark ] && uci set kucat.@basic[0].primary_rgbbody='33,45,60' || uci set kucat.@basic[0].primary_rgbbody='248,248,248'
- ;;
- bluets)
- uci set kucat.@basic[0].bkuse=`uci -q get kucat.@theme[$i].bkuse`
- uci set kucat.@basic[0].primary_rgbm_ts='0.6'
- uci set kucat.@basic[0].primary_opacity='3'
- uci set kucat.@basic[0].primary_rgbm='66,139,139'
- uci set kucat.@basic[0].primary_rgbs_ts=`uci -q get kucat.@theme[$i].primary_rgbs_ts`
- uci set kucat.@basic[0].primary_rgbs='55,103,128'
- [ $mode = dark ] && uci set kucat.@basic[0].primary_rgbbody='33,45,60' || uci set kucat.@basic[0].primary_rgbbody='248,248,248'
- ;;
- *)
- uci set kucat.@basic[0].bkuse=`uci -q get kucat.@theme[$i].bkuse`
- uci set kucat.@basic[0].primary_rgbm_ts=`uci -q get kucat.@theme[$i].primary_rgbm_ts`
- uci set kucat.@basic[0].primary_opacity=`uci -q get kucat.@theme[$i].primary_opacity`
- uci set kucat.@basic[0].primary_rgbm=`echo $primary_rgbm | sed 's#(##; s#)##g; s#rgb##g; s#\,\ #\,#g;s#\ #\,#g;'`
- uci set kucat.@basic[0].primary_rgbs=`uci -q get kucat.@theme[$i].primary_rgbs | sed 's#(##; s#)##g; s#rgb##g; s#\,\ #\,#g;s#\ #\,#g;'`
- uci set kucat.@basic[0].primary_rgbs_ts=`uci -q get kucat.@theme[$i].primary_rgbs_ts`
- [ $mode = dark ] && uci set kucat.@basic[0].primary_rgbbody='33,45,60' || uci set kucat.@basic[0].primary_rgbbody='248,248,248'
- ;;
- esac
- useset=1
- break
- fi
-done
-if [ "x$useset" == "x0" ] ; then
- uci set kucat.@basic[0].mode='auto'
- uci set kucat.@basic[0].primary_rgbm='74,161,133'
- uci set kucat.@basic[0].primary_rgbs_ts='0.1'
- uci set kucat.@basic[0].primary_rgbs='225,112,88'
- uci set kucat.@basic[0].bkuse='1'
- uci set kucat.@basic[0].primary_rgbm_ts='0.7'
- uci set kucat.@basic[0].primary_opacity='5'
- [ $mode == dark ] && uci set kucat.@basic[0].primary_rgbbody='33,45,60' || uci set kucat.@basic[0].primary_rgbbody='248,248,248'
-fi
-if [ "x$(uci -q get kucat.@basic[0].fontmode)" = "x1" ] ; then
- uci set kucat.@basic[0].font_d='1.3rem'
- uci set kucat.@basic[0].font_z='1.1rem'
- uci set kucat.@basic[0].font_x='0.92rem'
-else
- uci set kucat.@basic[0].font_d='1.1rem'
- uci set kucat.@basic[0].font_z='0.92rem'
- uci set kucat.@basic[0].font_x='0.875rem'
-fi
-uci commit kucat
-}
-
-start() {
- [ "x$(uci -q get kucat.@basic[0].mode)" = "x" ] && /etc/config/kucat reset
- kucatset
- rm -rf /tmp/luci-*
-}
-
-stop() {
-rm -rf /tmp/luci*
-}
-
diff --git a/luci-app-kucat-config/root/etc/uci-defaults/luci-kucat b/luci-app-kucat-config/root/etc/uci-defaults/luci-kucat
deleted file mode 100644
index c929655..0000000
--- a/luci-app-kucat-config/root/etc/uci-defaults/luci-kucat
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/bin/sh
-chmod +x /etc/init.d/kucat
-
-[ "x$(uci -q get kucat.@basic[0].mode)" = "x" ] && /etc/config/kucat reset
-[ -s /usr/lib/lua/luci/view/themes/argon/sysauth.htm ] && sed -i 's,media .. \"\/b,resource .. \"\/b,g' /usr/lib/lua/luci/view/themes/argon/sysauth.htm
-rm -f /tmp/luci-indexcache
-exit 0
diff --git a/luci-app-kucat-config/root/usr/share/rpcd/acl.d/luci-app-kucat-config.json b/luci-app-kucat-config/root/usr/share/rpcd/acl.d/luci-app-kucat-config.json
deleted file mode 100644
index 946e46f..0000000
--- a/luci-app-kucat-config/root/usr/share/rpcd/acl.d/luci-app-kucat-config.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "luci-app-kucat-config": {
- "description": "Grant UCI access for luci-app-kucat-config",
- "read": {
- "uci": [ "kucat-config" ]
- },
- "write": {
- "uci": [ "kucat-config" ]
- }
- }
-}
diff --git a/luci-app-passwall/luasrc/controller/passwall.lua b/luci-app-passwall/luasrc/controller/passwall.lua
index 267f8be..e876f47 100644
--- a/luci-app-passwall/luasrc/controller/passwall.lua
+++ b/luci-app-passwall/luasrc/controller/passwall.lua
@@ -301,7 +301,8 @@ function index_status()
end
function haproxy_status()
- local e = luci.sys.call(string.format("/bin/busybox top -bn1 | grep -v grep | grep '%s/bin/' | grep haproxy >/dev/null", appname)) == 0
+ local e = {}
+ e["status"] = luci.sys.call(string.format("/bin/busybox top -bn1 | grep -v grep | grep '%s/bin/' | grep haproxy >/dev/null", appname)) == 0
http_write_json(e)
end
@@ -407,6 +408,11 @@ function add_node()
local uuid = api.gen_short_uuid()
uci:section(appname, "nodes", uuid)
+ local group = http.formvalue("group")
+ if group and group ~= "default" then
+ uci:set(appname, uuid, "group", group)
+ end
+
if redirect == "1" then
api.uci_save(uci, appname)
http.redirect(api.url("node_config", uuid))
@@ -420,6 +426,20 @@ function set_node()
local protocol = http.formvalue("protocol")
local section = http.formvalue("section")
uci:set(appname, "@global[0]", protocol .. "_node", section)
+ if protocol == "tcp" then
+ local node_protocol = uci:get(appname, section, "protocol")
+ if node_protocol == "_shunt" then
+ local type = uci:get(appname, section, "type")
+ local dns_shunt = uci:get(appname, "@global[0]", "dns_shunt")
+ local dns_key = (dns_shunt == "smartdns") and "smartdns_dns_mode" or "dns_mode"
+ local dns_mode = uci:get(appname, "@global[0]", dns_key)
+ local new_dns_mode = (type == "Xray") and "xray" or "sing-box"
+ if dns_mode ~= new_dns_mode then
+ uci:set(appname, "@global[0]", dns_key, new_dns_mode)
+ uci:set(appname, "@global[0]", "v2ray_dns_mode", "tcp")
+ end
+ end
+ end
api.uci_save(uci, appname, true, true)
http.redirect(api.url("log"))
end
diff --git a/luci-app-passwall/luasrc/view/passwall/haproxy/status.htm b/luci-app-passwall/luasrc/view/passwall/haproxy/status.htm
index 8a6362d..af65f8b 100644
--- a/luci-app-passwall/luasrc/view/passwall/haproxy/status.htm
+++ b/luci-app-passwall/luasrc/view/passwall/haproxy/status.htm
@@ -1,26 +1,30 @@
<%
local api = require "luci.passwall.api"
+local haproxy_enable = api.uci_get_type("@global_haproxy[0]", "balancing_enable", "0")
local console_port = api.uci_get_type("@global_haproxy[0]", "console_port", "")
-%>
diff --git a/luci-app-passwall/luasrc/view/passwall/node_list/link_add_node.htm b/luci-app-passwall/luasrc/view/passwall/node_list/link_add_node.htm
index e71efdb..73eebf4 100644
--- a/luci-app-passwall/luasrc/view/passwall/node_list/link_add_node.htm
+++ b/luci-app-passwall/luasrc/view/passwall/node_list/link_add_node.htm
@@ -190,7 +190,7 @@ local api = require "luci.passwall.api"
-
<%:Group Name%>
+
<%:Group Name%>
<%:default%>
@@ -213,9 +213,8 @@ local api = require "luci.passwall.api"
-
-
-
+
+
@@ -228,6 +227,18 @@ local api = require "luci.passwall.api"
+.td.cbi-section-actions {
+ text-align: right !important;
+}
+
+.node-wrapper {
+ align-items: center;
+ display: inline-flex !important;
+ gap: 4px;
+}
+
+.node-wrapper .cbi-input-checkbox {
+ flex-grow: 0 !important;
+ flex-basis: auto;
+}
+
+.cbi-tabmenu > li {
+ margin-right: 2px !important;
+}
+
+.cbi-tabmenu > li:last-child {
+ margin-right: 0 !important;
+}
+
+
+
<% if api.is_js_luci() then -%>
+<%- else %>
+
<%- end %>
@@ -491,7 +536,7 @@ table td, .table .td {
{{tcping}}
{{url_test}}
-
+
@@ -637,7 +682,7 @@ table td, .table .td {
}
tab_ul_li_html +=
- '
' +
+ ' ' +
'' + group_name + " | " + "" + group_nodes[group].length + ' ' +
' '
tab_content_html +=
diff --git a/luci-app-passwall/root/usr/share/passwall/rule_update.lua b/luci-app-passwall/root/usr/share/passwall/rule_update.lua
index 705b8fc..286b8aa 100755
--- a/luci-app-passwall/root/usr/share/passwall/rule_update.lua
+++ b/luci-app-passwall/root/usr/share/passwall/rule_update.lua
@@ -41,14 +41,6 @@ local geo2rule = uci:get(name, "@global_rules[0]", "geo2rule") or "0"
local geoip_update_ok, geosite_update_ok = false, false
asset_location = asset_location:match("/$") and asset_location or (asset_location .. "/")
---兼容旧版本geo下载方式的配置,择机删除。
-if geoip_url:match(".*/([^/]+)$") == "latest" then
- geoip_url = "https://github.com/Loyalsoldier/geoip/releases/latest/download/geoip.dat"
-end
-if geosite_url:match(".*/([^/]+)$") == "latest" then
- geosite_url = "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat"
-end
-
if arg3 == "cron" then
arg2 = nil
end
diff --git a/luci-app-passwall/root/usr/share/passwall/rules/chnlist b/luci-app-passwall/root/usr/share/passwall/rules/chnlist
index d0d15bf..a2d11ac 100644
--- a/luci-app-passwall/root/usr/share/passwall/rules/chnlist
+++ b/luci-app-passwall/root/usr/share/passwall/rules/chnlist
@@ -9488,6 +9488,7 @@ acthd123.com
actime.net
actions-semi.com
actionsky.com
+activate.activation-v2.kaspersky.com
activation-gp.com
activation-v2.kaspersky.com
activeclub.net
@@ -27069,6 +27070,7 @@ cztrjs.com
cztv.cc
cztv.com
cztv.tv
+cztvcdn.com
cztvcloud.com
cztzkg.com
czur.com
@@ -52654,7 +52656,6 @@ imlianpu.com
imliuyi.com
imlizhi.com
immeee.com
-immersivetranslate.com
immfast.com
immi520.com
immiexpo.com
@@ -52834,7 +52835,6 @@ inferoey.com
infertilitybridge.com
infimotion.com
infineon-autoeco.com
-infineon.com
infini-ai.com
infinisign.com
infinitescript.com
@@ -60866,6 +60866,18 @@ ksmmed.com
ksmobile.com
ksmobile.net
ksmq5a9kxzmr.com
+ksn-cinfo.geoksn.kaspersky.com
+ksn-crypto-wifiplus.geoksn.kaspersky.com
+ksn-dc1-cert.geoksn.kaspersky.com
+ksn-dc1-file.geoksn.kaspersky.com
+ksn-dc1.geoksn.kaspersky.com
+ksn-file.geoksn.kaspersky.com
+ksn-info.geoksn.kaspersky.com
+ksn-kas.geoksn.kaspersky.com
+ksn-mdr.geoksn.kaspersky.com
+ksn-tcert.geoksn.kaspersky.com
+ksn-url.geoksn.kaspersky.com
+ksn-verdict.geoksn.kaspersky.com
ksndsa.com
ksnows.com
ksoapp.com
@@ -73986,7 +73998,6 @@ oogsy.com
oohdear.com
ooiii.com
oojsq.com
-ooklaserver.net
oolap.com
oomake.com
oonne.com
@@ -80421,6 +80432,7 @@ refineidea.com
refire.com
refond.com
reformdata.org
+refresh-bkg.activation-v2.kaspersky.com
reg007.com
regal-marathon.com
regal-raptor.com
@@ -92788,7 +92800,6 @@ thzab.com
thzu.cc
ti-node.com
ti-solar.com
-ti.com
ti0s.com
tiamaes.com
tian-gang.com
@@ -114581,6 +114592,7 @@ zhuanhuamao.com
zhuanhuanqi.com
zhuanhuanqi.net
zhuaniao.com
+zhuaninc.com
zhuankeapp.com
zhuankebang.com
zhuankezu.com
diff --git a/luci-app-passwall/root/usr/share/passwall/rules/chnroute b/luci-app-passwall/root/usr/share/passwall/rules/chnroute
index 1cbb565..92559e4 100644
--- a/luci-app-passwall/root/usr/share/passwall/rules/chnroute
+++ b/luci-app-passwall/root/usr/share/passwall/rules/chnroute
@@ -224,8 +224,6 @@
103.132.234.0/23
103.133.128.0/23
103.133.176.0/22
-103.134.136.0/22
-103.135.100.0/24
103.135.160.0/22
103.135.192.0/23
103.135.195.0/24
@@ -271,7 +269,6 @@
103.150.212.0/24
103.150.24.0/23
103.151.148.0/23
-103.151.179.0/24
103.151.216.0/23
103.151.228.0/23
103.152.170.0/23
@@ -375,8 +372,7 @@
103.202.96.0/20
103.203.140.0/22
103.203.216.0/22
-103.203.56.0/23
-103.203.59.0/24
+103.203.56.0/22
103.203.96.0/22
103.204.72.0/22
103.205.188.0/22
@@ -745,7 +741,7 @@
103.72.113.0/24
103.72.120.0/22
103.72.172.0/24
-103.72.224.0/24
+103.72.224.0/21
103.73.116.0/22
103.73.136.0/21
103.73.144.0/22
@@ -780,6 +776,7 @@
103.81.4.0/22
103.81.48.0/22
103.82.224.0/22
+103.82.52.0/22
103.83.120.0/22
103.83.44.0/22
103.83.64.0/22
@@ -1096,7 +1093,7 @@
113.47.112.0/20
113.47.128.0/18
113.47.204.0/22
-113.47.220.0/22
+113.47.216.0/21
113.47.232.0/21
113.47.248.0/21
113.47.64.0/19
@@ -1128,7 +1125,6 @@
114.112.104.0/22
114.112.136.0/21
114.112.144.0/21
-114.112.160.0/21
114.112.200.0/21
114.112.208.0/20
114.112.24.0/21
@@ -1163,7 +1159,6 @@
114.135.0.0/16
114.138.0.0/15
114.141.128.0/18
-114.142.136.0/23
114.196.0.0/15
114.212.0.0/14
114.216.0.0/13
@@ -1256,9 +1251,9 @@
116.16.0.0/12
116.162.0.0/16
116.163.0.0/18
+116.165.0.0/16
116.167.0.0/16
-116.168.0.0/15
-116.171.0.0/16
+116.168.0.0/14
116.172.128.0/17
116.172.64.0/18
116.174.0.0/15
@@ -1292,6 +1287,7 @@
116.198.64.0/21
116.198.72.0/22
116.198.80.0/21
+116.198.96.0/19
116.199.0.0/17
116.2.0.0/15
116.204.0.0/17
@@ -1338,7 +1334,7 @@
117.122.192.0/18
117.124.231.0/24
117.124.232.0/22
-117.124.237.0/24
+117.124.236.0/23
117.124.240.0/23
117.124.242.0/24
117.124.98.0/24
@@ -1425,22 +1421,7 @@
118.145.96.0/20
118.178.0.0/16
118.180.0.0/14
-118.184.0.0/22
-118.184.105.0/24
-118.184.106.0/23
118.184.128.0/17
-118.184.30.0/24
-118.184.40.0/21
-118.184.48.0/22
-118.184.52.0/24
-118.184.64.0/24
-118.184.66.0/23
-118.184.76.0/22
-118.184.81.0/24
-118.184.82.0/23
-118.184.84.0/22
-118.184.92.0/22
-118.184.96.0/22
118.186.0.0/19
118.186.112.0/21
118.186.128.0/18
@@ -1480,14 +1461,6 @@
118.192.68.0/23
118.192.70.0/24
118.192.96.0/19
-118.193.128.0/23
-118.193.144.0/24
-118.193.152.0/22
-118.193.160.0/23
-118.193.162.0/24
-118.193.164.0/22
-118.193.176.0/24
-118.193.188.0/22
118.193.96.0/19
118.194.128.0/21
118.194.164.0/22
@@ -1536,6 +1509,7 @@
118.64.0.0/21
118.64.248.0/21
118.66.112.0/23
+118.66.184.0/24
118.66.224.0/20
118.72.0.0/13
118.80.0.0/15
@@ -1978,10 +1952,7 @@
124.14.64.0/18
124.14.7.0/24
124.14.8.0/21
-124.151.0.0/17
-124.151.128.0/18
-124.151.193.0/24
-124.151.224.0/19
+124.151.0.0/16
124.152.0.0/16
124.16.0.0/15
124.160.0.0/13
@@ -2016,7 +1987,6 @@
124.240.0.0/17
124.240.128.0/18
124.243.192.0/18
-124.248.0.0/22
124.248.64.0/18
124.250.0.0/15
124.254.0.0/18
@@ -2154,7 +2124,7 @@
144.255.0.0/16
144.36.146.0/23
144.48.180.0/22
-144.48.184.0/24
+144.48.184.0/22
144.48.212.0/22
144.48.252.0/22
144.48.64.0/22
@@ -2210,9 +2180,6 @@
154.208.144.0/20
154.208.160.0/21
154.208.172.0/23
-154.218.6.0/23
-154.223.168.0/24
-154.48.237.0/24
154.72.42.0/24
154.72.44.0/24
154.72.47.0/24
@@ -2225,11 +2192,8 @@
156.107.170.0/24
156.107.179.0/24
156.107.181.0/24
-156.227.1.0/24
-156.227.24.0/22
156.230.11.0/24
156.231.163.0/24
-156.236.116.0/24
156.237.104.0/23
156.242.5.0/24
156.242.6.0/24
@@ -2288,6 +2252,7 @@
163.179.0.0/16
163.204.0.0/16
163.223.178.0/23
+163.227.40.0/24
163.228.0.0/16
163.244.246.0/24
163.47.4.0/22
@@ -2485,7 +2450,6 @@
193.119.4.0/24
193.119.6.0/24
193.119.8.0/24
-193.233.49.0/24
194.127.229.0/24
194.138.202.0/23
194.138.245.0/24
@@ -2691,7 +2655,6 @@
203.156.192.0/18
203.160.104.0/22
203.160.109.0/24
-203.160.110.0/23
203.160.129.0/24
203.160.192.0/24
203.160.196.0/24
@@ -2707,7 +2670,7 @@
203.166.188.0/23
203.166.191.0/24
203.168.0.0/22
-203.168.129.0/24
+203.168.128.0/23
203.168.16.0/23
203.168.18.0/24
203.168.5.0/24
@@ -3353,6 +3316,7 @@
220.154.0.0/20
220.154.128.0/22
220.154.132.0/23
+220.154.134.0/24
220.154.140.0/24
220.154.144.0/24
220.154.16.0/22
@@ -3540,6 +3504,7 @@
223.64.0.0/10
223.8.0.0/13
23.133.188.0/24
+23.140.100.0/24
23.161.8.0/24
23.236.111.0/24
27.0.128.0/24
@@ -3825,6 +3790,7 @@
43.250.32.0/22
43.250.4.0/22
43.250.96.0/22
+43.251.100.0/22
43.251.244.0/22
43.251.36.0/22
43.251.4.0/22
@@ -3863,6 +3829,7 @@
44.30.28.0/24
44.31.216.0/24
44.31.28.0/24
+44.31.42.0/24
44.31.81.0/24
44.31.96.0/24
44.32.143.0/24
@@ -3872,6 +3839,7 @@
45.112.232.0/22
45.113.20.0/22
45.113.200.0/22
+45.113.206.0/24
45.113.24.0/22
45.113.40.0/22
45.114.189.0/24
@@ -3917,12 +3885,7 @@
45.147.6.0/24
45.151.47.0/24
45.157.88.0/24
-45.192.172.0/24
-45.192.187.0/24
-45.192.189.0/24
-45.192.190.0/24
45.195.6.0/24
-45.197.131.0/24
45.202.209.0/24
45.202.210.0/23
45.202.212.0/24
diff --git a/luci-app-passwall/root/usr/share/passwall/rules/chnroute6 b/luci-app-passwall/root/usr/share/passwall/rules/chnroute6
index 024c19a..9e90cfd 100644
--- a/luci-app-passwall/root/usr/share/passwall/rules/chnroute6
+++ b/luci-app-passwall/root/usr/share/passwall/rules/chnroute6
@@ -89,13 +89,11 @@
2400:9380:9201::/48
2400:9380:9202::/48
2400:9380:9220::/47
-2400:9380:9240::/47
+2400:9380:9240::/48
2400:9380:9250::/47
2400:9380:9260::/48
2400:9380:9271::/48
-2400:9380:9272::/48
2400:9380:9280::/47
-2400:9380:9282::/48
2400:9380:92b0::/45
2400:95e0::/48
2400:9600:8800::/48
@@ -412,14 +410,13 @@
2406:840:9600::/43
2406:840:9620::/44
2406:840:9961::/48
-2406:840:9962::/47
+2406:840:9963::/48
2406:840:996c::/48
-2406:840:c200::/40
+2406:840:c400::/40
2406:840:e080::/44
2406:840:e0cf::/48
2406:840:e0e0::/46
2406:840:e0e4::/47
-2406:840:e0e8::/48
2406:840:e10f::/48
2406:840:e14f::/48
2406:840:e201::/48
@@ -429,7 +426,7 @@
2406:840:e600::/46
2406:840:e604::/47
2406:840:e606::/48
-2406:840:e60d::/48
+2406:840:e60c::/47
2406:840:e620::/47
2406:840:e666::/47
2406:840:e770::/48
@@ -468,7 +465,6 @@
2406:840:fdc0::/44
2406:840:fdd1::/48
2406:840:fe27::/48
-2406:840:fe60::/44
2406:840:fe90::/46
2406:840:fe94::/48
2406:840:fe96::/47
@@ -486,7 +482,6 @@
2406:840:fecc::/47
2406:840:fecf::/48
2406:840:fed1::/48
-2406:840:fed6::/48
2406:840:fed8::/48
2406:840:fedb::/48
2406:840:fedc::/48
@@ -655,82 +650,66 @@
2408:8378::/31
2408:837a::/32
2408:8406:100::/41
-2408:8406:1800::/42
+2408:8406:1800::/43
2408:8406:180::/42
-2408:8406:1840::/43
2408:8406:18c0::/42
2408:8406:1900::/41
2408:8406:1980::/42
-2408:8406:2400::/42
-2408:8406:2440::/43
+2408:8406:2400::/43
2408:8406:24c0::/42
2408:8406:2500::/41
2408:8406:2580::/42
-2408:8406:3000::/42
-2408:8406:3040::/43
+2408:8406:3000::/43
2408:8406:30c0::/42
2408:8406:3100::/41
2408:8406:3180::/42
-2408:8406:3c00::/42
-2408:8406:3c40::/43
+2408:8406:3c00::/43
2408:8406:3cc0::/42
2408:8406:3d00::/41
2408:8406:3d80::/42
-2408:8406:40::/43
-2408:8406:4800::/42
-2408:8406:4840::/43
+2408:8406:4800::/43
2408:8406:48c0::/42
2408:8406:4900::/41
2408:8406:4980::/42
-2408:8406:5400::/42
-2408:8406:5440::/43
+2408:8406:5400::/43
2408:8406:54c0::/42
2408:8406:5500::/41
2408:8406:5580::/42
-2408:8406:6000::/42
-2408:8406:6040::/43
+2408:8406:6000::/43
2408:8406:60c0::/42
2408:8406:6100::/41
2408:8406:6180::/42
-2408:8406:6c00::/42
-2408:8406:6c40::/43
+2408:8406:6c00::/43
2408:8406:6cc0::/42
2408:8406:6d00::/41
2408:8406:6d80::/42
-2408:8406:7800::/42
-2408:8406:7840::/43
+2408:8406:7800::/43
2408:8406:78c0::/42
2408:8406:7900::/41
2408:8406:7980::/42
-2408:8406:8400::/42
-2408:8406:8440::/43
+2408:8406:8400::/43
2408:8406:84c0::/42
2408:8406:8500::/41
2408:8406:8580::/42
-2408:8406:9000::/42
-2408:8406:9040::/43
+2408:8406:9000::/43
2408:8406:90c0::/42
2408:8406:9100::/41
2408:8406:9180::/42
-2408:8406:9c00::/42
-2408:8406:9c40::/43
+2408:8406:9c00::/43
2408:8406:9cc0::/42
2408:8406:9d00::/41
2408:8406:9d80::/42
-2408:8406::/42
-2408:8406:a800::/42
-2408:8406:a840::/43
+2408:8406::/43
+2408:8406:a800::/43
2408:8406:a8c0::/42
2408:8406:a900::/41
2408:8406:a980::/42
-2408:8406:b400::/42
-2408:8406:b440::/43
+2408:8406:b400::/43
2408:8406:b4c0::/42
2408:8406:b500::/41
2408:8406:b580::/42
-2408:8406:c00::/42
+2408:8406:c00::/43
2408:8406:c0::/42
-2408:8406:c40::/43
2408:8406:cc0::/42
2408:8406:d00::/41
2408:8406:d80::/42
@@ -1214,15 +1193,16 @@
240e::/20
2602:2e0:ff::/48
2602:f7ee:ee::/48
+2602:f92a:1314::/48
2602:f92a:a471::/48
2602:f92a:a473::/48
2602:f92a:a478::/48
2602:f92a:dead::/48
2602:f92a:e100::/44
-2602:f93b:400::/38
2602:f9ba:10c::/48
2602:f9ba:a8::/48
2602:fab0:11::/48
+2602:fed2:7051::/48
2602:feda:1bf::/48
2602:feda:1d1::/48
2602:feda:1df::/48
@@ -1244,7 +1224,6 @@
2620:57:4004::/48
2804:1e48:9001::/48
2804:1e48:9002::/48
-2a01:f100:100::/48
2a01:f100:1f8::/48
2a04:3e00:1002::/48
2a04:f580:8010::/47
@@ -1271,13 +1250,10 @@
2a04:f580:9280::/48
2a04:f580:9290::/48
2a05:1087::/32
-2a05:dfc3:ff00::/40
-2a06:1180:1000::/48
2a06:1281:8000::/36
2a06:3603::/32
2a06:3604::/30
2a06:9f81:4600::/43
-2a06:9f81:4620::/44
2a06:9f81:4640::/43
2a06:a005:1c40::/44
2a06:a005:260::/43
@@ -1298,14 +1274,18 @@
2a0a:2846::/48
2a0a:6040:ec00::/40
2a0a:6044:6600::/39
-2a0a:6044:7a00::/40
2a0b:2542::/48
2a0b:4b81:1001::/48
2a0b:4e07:b8::/47
+2a0c:9a40:84e0::/48
2a0c:9a40:8fc1::/48
2a0c:9a40:8fc2::/47
2a0c:9a40:8fc4::/48
2a0c:b641:571::/48
+2a0c:b641:722::/47
+2a0c:b641:725::/48
+2a0c:b641:726::/47
+2a0c:b641:728::/47
2a0e:97c0:5ef::/48
2a0e:97c0:83f::/48
2a0e:9b00::/29
@@ -1324,7 +1304,6 @@
2a0e:aa07:e030::/48
2a0e:aa07:e035::/48
2a0e:aa07:e039::/48
-2a0e:aa07:e044::/48
2a0e:aa07:e0e0::/44
2a0e:aa07:e151::/48
2a0e:aa07:e155::/48
@@ -1333,14 +1312,15 @@
2a0e:aa07:e162::/48
2a0e:aa07:e16a::/48
2a0e:aa07:e1a0::/44
-2a0e:aa07:e1e0::/44
+2a0e:aa07:e1e1::/48
+2a0e:aa07:e1e2::/48
+2a0e:aa07:e1e6::/48
2a0e:aa07:e200::/44
2a0e:aa07:e210::/48
2a0e:aa07:e21c::/47
2a0e:aa07:e220::/44
2a0e:aa07:f0d0::/46
2a0e:aa07:f0d4::/47
-2a0e:aa07:f0d8::/48
2a0e:aa07:f0de::/47
2a0e:b107:12b::/48
2a0e:b107:14a0::/44
@@ -1351,23 +1331,26 @@
2a0e:b107:c10::/48
2a0e:b107:da0::/44
2a0e:b107:dce::/48
-2a0f:1cc5:f00::/47
-2a0f:1cc5:f02::/48
-2a0f:1cc5:f05::/48
-2a0f:5707:ac00::/47
+2a0f:1cc5:10::/48
+2a0f:1cc5:20::/44
+2a0f:1cc5:600::/48
+2a0f:1cc5:f00::/46
+2a0f:1cc6:b100::/48
+2a0f:1cc6:b102::/47
+2a0f:1cc6:b104::/48
+2a0f:5707:ac01::/48
+2a0f:6284:3000::/36
+2a0f:6284:300::/40
+2a0f:6284:400::/42
+2a0f:6284:440::/43
2a0f:6284:4b00::/40
+2a0f:6284:4c20::/44
2a0f:6284:4c30::/48
2a0f:6284:4c40::/43
2a0f:6284:4c60::/44
2a0f:6284:4c80::/43
2a0f:7803:e300::/40
-2a0f:7803:f5d0::/44
-2a0f:7803:f5e0::/43
-2a0f:7803:f680::/43
-2a0f:7803:f6a0::/44
-2a0f:7803:f7c0::/42
-2a0f:7803:f800::/43
-2a0f:7803:f840::/44
+2a0f:7803:f680::/44
2a0f:7803:fa21::/48
2a0f:7803:fa22::/47
2a0f:7803:fa24::/46
@@ -1381,13 +1364,12 @@
2a0f:7d07::/32
2a0f:85c1:ba5::/48
2a0f:85c1:bfe::/48
-2a0f:85c1:ca0::/44
2a0f:85c1:ce1::/48
2a0f:85c1:cf1::/48
-2a0f:85c1:d90::/48
2a0f:9400:6110::/48
2a0f:9400:7700::/48
2a0f:ac00::/29
+2a0f:ea47:fc1d::/48
2a10:2f00:15a::/48
2a10:ccc0:d00::/46
2a10:ccc0:d0a::/47
@@ -1411,18 +1393,25 @@
2a13:a5c7:23c0::/48
2a13:a5c7:2801::/48
2a13:a5c7:3108::/48
-2a13:a5c7:31a0::/43
+2a13:a5c7:31a0::/44
+2a13:a5c7:31b0::/46
+2a13:a5c7:31b4::/47
+2a13:a5c7:31b6::/48
+2a13:a5c7:31b8::/45
2a13:a5c7:3301::/48
2a13:a5c7:3304::/48
2a13:a5c7:3306::/47
2a13:aac4:f000::/44
2a14:4c41::/32
2a14:67c1:20::/44
-2a14:67c1:702::/47
+2a14:67c1:702::/48
2a14:67c1:704::/48
2a14:67c1:70::/48
2a14:67c1:73::/48
2a14:67c1:74::/48
+2a14:67c1:800::/48
+2a14:67c1:802::/47
+2a14:67c1:804::/46
2a14:67c1:a010::/44
2a14:67c1:a020::/48
2a14:67c1:a023::/48
@@ -1452,6 +1441,7 @@
2a14:67c1:b4c0::/45
2a14:67c1:b4e0::/43
2a14:67c1:b500::/47
+2a14:67c1:b514::/48
2a14:67c1:b549::/48
2a14:67c1:b561::/48
2a14:67c1:b563::/48
@@ -1468,7 +1458,6 @@
2a14:7580:750::/47
2a14:7580:9208::/48
2a14:7580:9220::/44
-2a14:7580:9400::/39
2a14:7580:d000::/37
2a14:7580:d800::/39
2a14:7580:da00::/40
@@ -1477,15 +1466,14 @@
2a14:7580:fff4::/48
2a14:7580:fff7::/48
2a14:7580:fffa::/48
-2a14:7581:30b5::/48
+2a14:7581:30b6::/48
2a14:7581:3100::/40
2a14:7581:3401::/48
2a14:7581:b12::/47
2a14:7581:b14::/47
2a14:7581:b20::/46
-2a14:7581:b40::/48
-2a14:7581:b44::/48
-2a14:7581:b60::/48
+2a14:7581:b40::/45
+2a14:7581:b60::/44
2a14:7581:ba0::/48
2a14:7581:bbb::/48
2a14:7581:bcd::/48
@@ -1493,6 +1481,7 @@
2a14:7581:ffb::/48
2a14:7583:f201::/48
2a14:7583:f203::/48
+2a14:7583:f204::/48
2a14:7583:f300::/46
2a14:7583:f304::/47
2a14:7583:f460::/44
@@ -1505,8 +1494,8 @@
2a14:7583:f708::/47
2a14:7583:f70a::/48
2a14:7583:f743::/48
+2a14:7583:f744::/48
2a14:7583:f764::/48
-2a14:7584::/36
2a14:7c0:4a01::/48
2a14:7c0:5103::/48
2c0f:f7a8:8011::/48
diff --git a/luci-app-passwall/root/usr/share/passwall/rules/gfwlist b/luci-app-passwall/root/usr/share/passwall/rules/gfwlist
index 02e24eb..22ddc69 100644
--- a/luci-app-passwall/root/usr/share/passwall/rules/gfwlist
+++ b/luci-app-passwall/root/usr/share/passwall/rules/gfwlist
@@ -1,6 +1,7 @@
000webhost.com
030buy.com
0rz.tw
+1-apple.com.tw
1000giri.net
10beasts.net
10conditionsoflove.com
@@ -297,6 +298,7 @@ aofriend.com
aojiao.org
aomedia.org
aomiwang.com
+aoxvpn.com
apartmentratings.com
apartments.com
apat1989.org
@@ -514,6 +516,7 @@ bestvpnusa.com
bet365.com
betaclouds.net
betfair.com
+bettergpt.chat
betterhash.net
betternet.co
bettervpn.com
@@ -816,6 +819,7 @@ cams.com
cams.org.sg
canadameet.com
canalporno.com
+canva.com
canyu.org
caobian.info
caochangqing.com
@@ -854,6 +858,7 @@ cccat.cc
cccat.co
ccfd.org.tw
cchere.com
+cchostvps.xyz
ccim.org
cclife.ca
cclife.org
@@ -916,9 +921,11 @@ chanworld.org
chaoex.com
chaos.social
character.ai
+chat.com
chat.lmsys.org
chatgpt.com
chatnook.com
+chatpdf.com
chaturbate.com
checkgfw.com
chengmingmag.com
@@ -1236,6 +1243,7 @@ danke4china.net
daodu14.jigsy.com
daolan.net
darktech.org
+darmau.co
darpa.mil
darrenliuwei.com
dashlane.com
@@ -1247,6 +1255,7 @@ dawangidc.com
daxa.cn
daylife.com
db.tt
+dbgjd.com
dcard.tw
dcmilitary.com
ddc.com.tw
@@ -1271,9 +1280,7 @@ deezer.com
definebabe.com
deja.com
delcamp.net
-delicious.com
demo.unlock-music.dev
-democrats.org
demosisto.hk
deno.dev
depositphotos.com
@@ -1707,6 +1714,7 @@ fhreports.net
fiddle.jshell.net
figprayer.com
fileflyer.com
+files.oaiusercontent.com
fileserve.com
filesor.com
fillthesquare.org
@@ -1731,6 +1739,7 @@ flagsonline.it
flecheinthepeche.fr
fleshbot.com
fleursdeslettres.com
+flexclip.com
flexpool.io
flgjustice.org
flickr.com
@@ -1742,6 +1751,7 @@ flipkart.com
flitto.com
flnet.org
flog.tw
+flowgpt.com
flowhongkong.net
flypool.org
flyvpn.com
@@ -1755,6 +1765,7 @@ focusvpn.com
fofg.org
fooooo.com
forbes.com
+forefront.ai
foreignaffairs.com
foreignpolicy.com
form.new
@@ -1793,6 +1804,7 @@ freebrowser.org
freechal.com
freechina.net
freechina.news
+freechinaweibo.com
freeddns.com
freeddns.org
freedomcollection.org
@@ -1830,6 +1842,7 @@ freevpn.nl
freewallpaper4.me
freewebs.com
freewechat.com
+freeweibo.com
freewww.info
freexinwen.com
freeyellow.com
@@ -1949,6 +1962,7 @@ getfoxyproxy.org
getgom.com
geti2p.net
getiton.com
+getjetso.com
getlantern.org
getmalus.com
getmdl.io
@@ -2391,6 +2405,7 @@ hgseav.com
hhdcb3office.org
hhthesakyatrizin.org
hi-on.org.tw
+hicairo.com
hiccears.com
hidden-advent.org
hide.me
@@ -2580,6 +2595,7 @@ hwayue.org.tw
hxwk.org
hxwq.org
hybrid-analysis.com
+hyperbeam.com
hyperrate.com
hypothes.is
i-cable.com
@@ -2644,6 +2660,7 @@ imagefap.com
imageflea.com
imageglass.org
images-gaytube.com
+images.prismic.io
imageshack.us
imagevenue.com
imagezilla.net
@@ -2817,7 +2834,6 @@ jex.com
jgoodies.com
jiangweiping.com
jiaoyou8.com
-jichangtj.com
jiehua.cz
jieshibaobao.com
jifangge.com
@@ -3013,6 +3029,7 @@ lenwhite.com
lesoir.be
letou.com
letscorp.net
+lexica.art
lflink.com
lflinkup.com
lflinkup.net
@@ -3052,6 +3069,7 @@ linkedin.com
linkideo.com
linktr.ee
linkuswell.com
+linux.do
linux.org.hk
linuxtoy.org
lionsroar.com
@@ -3076,6 +3094,7 @@ livevideo.com
livingstream.com
lizhizhuangbi.com
lkcn.net
+lmarena.ai
load.to
lobsangwangyal.com
localbitcoins.com
@@ -3105,6 +3124,7 @@ lsmkorean.org
lsmradio.com
lsmwebcast.com
lsxszzg.com
+lt.sntp.uk
ltn.com.tw
luckydesigner.space
luckymobile.ca
@@ -3958,9 +3978,11 @@ post852.com
postadult.com
posts.careerengine.us
potato.im
+potatso.com
potvpn.com
pourquoi.tw
power.com
+powerapple.com
powercx.com
powerphoto.org
ppy.sh
@@ -4142,6 +4164,7 @@ relay.firefox.com
releaseinternational.org
religionnews.com
renminbao.com
+rentry.co
renyurenquan.org
resilio.com
resistchina.org
@@ -4478,6 +4501,7 @@ songjianjun.com
sonidodelaesperanza.org
sopcast.com
sopcast.org
+sora.com
sorting-algorithms.com
sos.org
sosad.fun
@@ -4744,11 +4768,13 @@ telegraph.co.uk
telesco.pe
tellapart.com
tellme.pw
+temu.com
tenacy.com
tenor.com
tensorflow.org
tenzinpalmo.com
terabox.com
+terobox.com
tew.org
textnow.com
textnow.me
@@ -5295,6 +5321,7 @@ vewas.net
vft.com.tw
viber.com
vica.info
+victimsofcommunism.org
vid.me
vidble.com
video.aol.ca
@@ -5501,6 +5528,7 @@ whatbrowser.org
whats.new
whatsapp.com
whatsapp.net
+whatsonweibo.com
wheelockslatin.com
whereiswerner.com
wheretowatch.com
diff --git a/luci-app-passwall2/luasrc/controller/passwall2.lua b/luci-app-passwall2/luasrc/controller/passwall2.lua
index 7ed0d91..991bc92 100644
--- a/luci-app-passwall2/luasrc/controller/passwall2.lua
+++ b/luci-app-passwall2/luasrc/controller/passwall2.lua
@@ -85,6 +85,7 @@ function index()
entry({"admin", "services", appname, "subscribe_del_all"}, call("subscribe_del_all")).leaf = true
entry({"admin", "services", appname, "subscribe_manual"}, call("subscribe_manual")).leaf = true
entry({"admin", "services", appname, "subscribe_manual_all"}, call("subscribe_manual_all")).leaf = true
+ entry({"admin", "services", appname, "flush_set"}, call("flush_set")).leaf = true
--[[Components update]]
entry({"admin", "services", appname, "check_passwall2"}, call("app_check")).leaf = true
@@ -256,7 +257,8 @@ function index_status()
end
function haproxy_status()
- local e = luci.sys.call(string.format("/bin/busybox top -bn1 | grep -v grep | grep '%s/bin/' | grep haproxy >/dev/null", appname)) == 0
+ local e = {}
+ e["status"] = luci.sys.call(string.format("/bin/busybox top -bn1 | grep -v grep | grep '%s/bin/' | grep haproxy >/dev/null", appname)) == 0
http_write_json(e)
end
@@ -335,6 +337,11 @@ function add_node()
local uuid = api.gen_short_uuid()
uci:section(appname, "nodes", uuid)
+ local group = http.formvalue("group")
+ if group then
+ uci:set(appname, uuid, "group", group)
+ end
+
if redirect == "1" then
api.uci_save(uci, appname)
http.redirect(api.url("node_config", uuid))
@@ -765,3 +772,17 @@ function subscribe_manual_all()
luci.sys.call("lua /usr/share/" .. appname .. "/subscribe.lua start all manual >/dev/null 2>&1 &")
http_write_json({ success = true, msg = "Subscribe triggered." })
end
+
+function flush_set()
+ local redirect = http.formvalue("redirect") or "0"
+ local reload = http.formvalue("reload") or "0"
+ if reload == "1" then
+ uci:set(appname, '@global[0]', "flush_set", "1")
+ api.uci_save(uci, appname, true, true)
+ else
+ api.sh_uci_set(appname, "@global[0]", "flush_set", "1", true)
+ end
+ if redirect == "1" then
+ http.redirect(api.url("log"))
+ end
+end
diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/global.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/global.lua
index d3e54bc..da3879f 100644
--- a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/global.lua
+++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/global.lua
@@ -350,16 +350,14 @@ o = s:taboption("DNS", Flag, "dns_redirect", translate("DNS Redirect"), translat
o.default = "1"
o.rmempty = false
-if (m:get("@global_forwarding[0]", "use_nft") or "0") == "1" then
- o = s:taboption("DNS", Button, "clear_ipset", translate("Clear NFTSet"), translate("Try this feature if the rule modification does not take effect."))
-else
- o = s:taboption("DNS", Button, "clear_ipset", translate("Clear IPSet"), translate("Try this feature if the rule modification does not take effect."))
-end
-o.inputstyle = "remove"
-function o.write(e, e)
- m:set("@global[0]", "flush_set", "1")
- api.uci_save(m.uci, appname, true, true)
- luci.http.redirect(api.url("log"))
+local use_nft = m:get("@global_forwarding[0]", "use_nft") == "1"
+local set_title = api.i18n.translate(use_nft and "Clear NFTSET" or "Clear IPSET")
+o = s:taboption("DNS", DummyValue, "clear_ipset", set_title, translate("Try this feature if the rule modification does not take effect."))
+o.rawhtml = true
+function o.cfgvalue(self, section)
+ return string.format(
+ [[
%s ]],
+ api.url("flush_set") .. "?redirect=1&reload=1", set_title)
end
o = s:taboption("DNS", DummyValue, "_xray_node", "")
diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe.lua
index e89a67d..cd7cc81 100644
--- a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe.lua
+++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe.lua
@@ -187,7 +187,7 @@ o.validate = function(self, value, section)
end
o.write = function(self, section, value)
local old = m:get(section, self.option) or ""
- if old:lower() ~= value:lower() then
+ if old ~= value then
m.uci:foreach(appname, "nodes", function(e)
if e["group"] and e["group"]:lower() == old:lower() then
m.uci:set(appname, e[".name"], "group", value)
diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe_config.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe_config.lua
index 214f824..4274272 100644
--- a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe_config.lua
+++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe_config.lua
@@ -93,7 +93,7 @@ o.validate = function(self, value, section)
end
o.write = function(self, section, value)
local old = m:get(section, self.option) or ""
- if old:lower() ~= value:lower() then
+ if old ~= value then
m.uci:foreach(appname, "nodes", function(e)
if e["group"] and e["group"]:lower() == old:lower() then
m.uci:set(appname, e[".name"], "group", value)
diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/rule.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/rule.lua
index 020e741..efcf87d 100644
--- a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/rule.lua
+++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/rule.lua
@@ -31,6 +31,13 @@ if api.is_finded("geoview") then
.. "
" .. translate("Analyzes and preloads GeoIP/Geosite data to enhance the shunt performance of Sing-box/Xray.") .. " "
.. "
" .. translate("Note: Increases resource usage.") .. " "
.. ""
+ function o.write(self, section, value)
+ local old = m:get(section, self.option) or "0"
+ if old ~= value then
+ m:set(section, "flush_set", "1")
+ end
+ return Flag.write(self, section, value)
+ end
end
s:append(Template(appname .. "/rule/rule_version"))
diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/shunt_rules.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/shunt_rules.lua
index b021a1b..5900c24 100644
--- a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/shunt_rules.lua
+++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/shunt_rules.lua
@@ -10,6 +10,10 @@ if not arg[1] or not m:get(arg[1]) then
luci.http.redirect(m.redirect)
end
+function m.on_before_save(self)
+ m:set("@global[0]", "flush_set", "1")
+end
+
-- Add inline CSS to map description
m.description = (m.description or "") .. [[
<% if api.is_js_luci() then -%>
@@ -100,6 +125,21 @@ table td, .table .td {
return false
}
+<%- else %>
+
<%- end %>
@@ -490,7 +536,7 @@ table td, .table .td {
{{tcping}}
{{url_test}}
-
+
@@ -636,7 +682,7 @@ table td, .table .td {
}
tab_ul_li_html +=
- '
' +
+ ' ' +
'' + group_name + " | " + "" + group_nodes[group].length + ' ' +
' '
tab_content_html +=
diff --git a/luci-app-passwall2/po/zh-cn/passwall2.po b/luci-app-passwall2/po/zh-cn/passwall2.po
index c646232..b0ebdae 100644
--- a/luci-app-passwall2/po/zh-cn/passwall2.po
+++ b/luci-app-passwall2/po/zh-cn/passwall2.po
@@ -178,11 +178,11 @@ msgstr "DNS 重定向"
msgid "Force special DNS server to need proxy devices."
msgstr "强制需要代理的设备使用专用 DNS 服务器。"
-msgid "Clear IPSet"
-msgstr "清空 IPSet"
+msgid "Clear IPSET"
+msgstr "清空 IPSET"
-msgid "Clear NFTSet"
-msgstr "清空 NFTSet"
+msgid "Clear NFTSET"
+msgstr "清空 NFTSET"
msgid "Try this feature if the rule modification does not take effect."
msgstr "如果修改规则后没有生效,请尝试此功能。"
@@ -1114,6 +1114,12 @@ msgstr "预定义域名列表:由'geosite:'开头,余下部分是一个名
msgid "Annotation: Begining with #"
msgstr "注释: 由 # 开头"
+msgid "Invert match result."
+msgstr "反选匹配结果。"
+
+msgid "Only support Sing-Box."
+msgstr "只支持 Sing-Box。"
+
msgid "IP: such as '127.0.0.1'."
msgstr "IP: 形如'127.0.0.1'。"
diff --git a/luci-app-passwall2/po/zh-tw/passwall2.po b/luci-app-passwall2/po/zh-tw/passwall2.po
index 50caac1..7553d50 100644
--- a/luci-app-passwall2/po/zh-tw/passwall2.po
+++ b/luci-app-passwall2/po/zh-tw/passwall2.po
@@ -178,11 +178,11 @@ msgstr "DNS 重定向"
msgid "Force special DNS server to need proxy devices."
msgstr "强制需要代理的設備使用专用 DNS 服務器。"
-msgid "Clear IPSet"
-msgstr "清空 IPSet"
+msgid "Clear IPSET"
+msgstr "清空 IPSET"
-msgid "Clear NFTSet"
-msgstr "清空 NFTSet"
+msgid "Clear NFTSET"
+msgstr "清空 NFTSET"
msgid "Try this feature if the rule modification does not take effect."
msgstr "如果修改規則後没有生效,请尝試此功能。"
@@ -1114,6 +1114,12 @@ msgstr "预定义域名列表:由'geosite:'開头,余下部分是一个名
msgid "Annotation: Begining with #"
msgstr "注释: 由 # 開头"
+msgid "Invert match result."
+msgstr "反選匹配結果。"
+
+msgid "Only support Sing-Box."
+msgstr "只支持 Sing-Box。"
+
msgid "IP: such as '127.0.0.1'."
msgstr "IP: 形如'127.0.0.1'。"
diff --git a/luci-app-ssr-plus/root/usr/bin/ssr-rules b/luci-app-ssr-plus/root/usr/bin/ssr-rules
index c580ea6..75b8471 100755
--- a/luci-app-ssr-plus/root/usr/bin/ssr-rules
+++ b/luci-app-ssr-plus/root/usr/bin/ssr-rules
@@ -14,7 +14,7 @@ detect_firewall() {
! grep -q "fw3" /etc/init.d/firewall 2>/dev/null; then
USE_NFT=1
NFT="nft"
- FWI=$(uci get firewall.shadowsocksr.path 2>/dev/null) # firewall include file
+ FWI=$(uci get firewall.shadowsocksr.path 2>/dev/null) # firewall include file
else
USE_NFT=0
IPT="iptables -t nat" # alias of iptables
@@ -82,32 +82,59 @@ flush_r() {
}
flush_nftables() {
- # Remove nftables rules and sets more carefully
- $NFT delete table inet ss_spec 2>/dev/null
- $NFT delete table ip ss_spec 2>/dev/null
- $NFT delete table ip ss_spec_mangle 2>/dev/null
-
- # Clean up routing rules
+ # 删除 inet ss_spec 表
+ if $NFT list table inet ss_spec >/dev/null 2>&1; then
+ # 删除所有链
+ local CHAINS=$($NFT list table inet ss_spec | awk '/chain [a-zA-Z0-9_-]+/ {print $2}' | sort -u)
+ for chain in $CHAINS; do
+ $NFT flush chain inet ss_spec $chain 2>/dev/null
+ $NFT delete chain inet ss_spec $chain 2>/dev/null
+ done
+
+ # 删除所有集合(set)
+ local SETS=$($NFT list table inet ss_spec | awk '/set [a-zA-Z0-9_-]+/ {print $2}' | sort -u)
+ for setname in $SETS; do
+ $NFT flush set inet ss_spec $setname 2>/dev/null
+ $NFT delete set inet ss_spec $setname 2>/dev/null
+ done
+
+ # 删除整个表
+ $NFT delete table inet ss_spec 2>/dev/null
+ fi
+
+ # 删除 ip ss_spec_mangle 表(如果存在)
+ if $NFT list table ip ss_spec_mangle >/dev/null 2>&1; then
+ # 删除所有链
+ local CHAINS=$($NFT list table ip ss_spec_mangle | awk '/chain [a-zA-Z0-9_-]+/ {print $2}' | sort -u)
+ for chain in $CHAINS; do
+ $NFT flush chain ip ss_spec_mangle $chain 2>/dev/null
+ $NFT delete chain ip ss_spec_mangle $chain 2>/dev/null
+ done
+
+ # 删除所有集合(set)
+ local SETS=$($NFT list table ip ss_spec_mangle | awk '/set [a-zA-Z0-9_-]+/ {print $2}' | sort -u)
+ for setname in $SETS; do
+ $NFT flush set ip ss_spec_mangle $setname 2>/dev/null
+ $NFT delete set ip ss_spec_mangle $setname 2>/dev/null
+ done
+
+ # 删除整个表
+ $NFT delete table ip ss_spec_mangle 2>/dev/null
+ fi
+
+ # 删除策略路由标记规则
ip rule del fwmark 0x01/0x01 table 100 2>/dev/null
ip route del local 0.0.0.0/0 dev lo table 100 2>/dev/null
- # 删除 nftables 集合
- $NFT delete set inet ss_spec ss_spec_lan_ac 2>/dev/null
- $NFT delete set inet ss_spec ss_spec_wan_ac 2>/dev/null
- $NFT delete set inet ss_spec ssr_gen_router 2>/dev/null
- $NFT delete set inet ss_spec fplan 2>/dev/null
- $NFT delete set inet ss_spec bplan 2>/dev/null
- $NFT delete set inet ss_spec gmlan 2>/dev/null
- $NFT delete set inet ss_spec oversea 2>/dev/null
- $NFT delete set inet ss_spec whitelist 2>/dev/null
- $NFT delete set inet ss_spec blacklist 2>/dev/null
- $NFT delete set inet ss_spec netflix 2>/dev/null
- $NFT delete set inet ss_spec gfwlist 2>/dev/null
- $NFT delete set inet ss_spec china 2>/dev/null
- $NFT delete set inet ss_spec music 2>/dev/null
+ # 可选:强制删除所有 ss_spec 相关的集合(即使表被误删)
+ for setname in ss_spec_lan_ac ss_spec_wan_ac ssr_gen_router fplan bplan gmlan oversea whitelist blacklist netflix gfwlist china music; do
+ $NFT delete set inet ss_spec $setname 2>/dev/null
+ $NFT delete set ip ss_spec_mangle $setname 2>/dev/null
+ done
+ # 重置防火墙 include 文件
[ -n "$FWI" ] && echo '#!/bin/sh' >"$FWI"
-
+
return 0
}
@@ -148,13 +175,13 @@ ipset_r() {
}
ipset_nft() {
- [ -f "$IGNORE_LIST" ] && /usr/share/shadowsocksr/chinaipset.sh "$IGNORE_LIST"
-
# Create nftables table and sets
- $NFT list table inet ss_spec >/dev/null 2>&1 || $NFT add table inet ss_spec
+ if ! $NFT list table inet ss_spec >/dev/null 2>&1; then
+ $NFT add table inet ss_spec 2>/dev/null
+ fi
# Create necessary collections
- for setname in ss_spec_wan_ac gmlan fplan bplan whitelist blacklist netflix; do
+ for setname in ss_spec_wan_ac china gmlan fplan bplan whitelist blacklist netflix; do
if ! $NFT list set inet ss_spec $setname >/dev/null 2>&1; then
$NFT add set inet ss_spec $setname '{ type ipv4_addr; flags interval; auto-merge; }' 2>/dev/null
else
@@ -162,6 +189,11 @@ ipset_nft() {
fi
done
+ # 批量导入中国IP列表
+ if [ -f "${china_ip:=/etc/ssrplus/china_ssr.txt}" ]; then
+ $NFT add element inet ss_spec china { $(tr '\n' ',' < "${china_ip}" | sed 's/,$//') } 2>/dev/null
+ fi
+
# Add IP addresses to sets
for ip in $LAN_GM_IP; do
[ -n "$ip" ] && $NFT add element inet ss_spec gmlan "{ $ip }" 2>/dev/null
@@ -181,7 +213,7 @@ ipset_nft() {
# Create main chain for WAN access control
if ! $NFT list chain inet ss_spec ss_spec_wan_ac >/dev/null 2>&1; then
- $NFT add chain inet ss_spec ss_spec_wan_ac '{ type nat hook prerouting priority dstnat - 1; policy accept; }' 2>/dev/null
+ $NFT add chain inet ss_spec ss_spec_wan_ac 2>/dev/null
fi
$NFT flush chain inet ss_spec ss_spec_wan_ac 2>/dev/null
@@ -197,9 +229,7 @@ ipset_nft() {
# Add basic rules
$NFT add rule inet ss_spec ss_spec_wan_ac tcp dport 53 ip daddr 127.0.0.0/8 return
- $NFT add rule inet ss_spec ss_spec_wan_ac udp dport 53 ip daddr 127.0.0.0/8 return
$NFT add rule inet ss_spec ss_spec_wan_ac tcp dport != 53 ip daddr "$server" return
- $NFT add rule inet ss_spec ss_spec_wan_ac udp dport != 53 ip daddr "$server" return
# Add special IP ranges to WAN AC set
for ip in $(gen_spec_iplist); do
@@ -217,7 +247,9 @@ ipset_nft() {
fi
;;
gfw)
- $NFT add set inet ss_spec gfwlist '{ type ipv4_addr; flags interval; }' 2>/dev/null
+ if ! $NFT list set inet ss_spec gfwlist >/dev/null 2>&1; then
+ $NFT add set inet ss_spec gfwlist '{ type ipv4_addr; flags interval; auto-merge; }' 2>/dev/null
+ fi
$NFT add rule inet ss_spec ss_spec_wan_ac ip daddr @china return 2>/dev/null
$NFT add rule inet ss_spec ss_spec_wan_ac ip daddr @gfwlist jump ss_spec_wan_fw 2>/dev/null
if $NFT list chain inet ss_spec ss_spec_wan_fw >/dev/null 2>&1; then
@@ -225,7 +257,9 @@ ipset_nft() {
fi
;;
oversea)
- $NFT add set inet ss_spec oversea '{ type ipv4_addr; flags interval; }' 2>/dev/null
+ if ! $NFT list set inet ss_spec oversea >/dev/null 2>&1; then
+ $NFT add set inet ss_spec oversea '{ type ipv4_addr; flags interval; auto-merge; }' 2>/dev/null
+ fi
if $NFT list chain inet ss_spec ss_spec_wan_fw >/dev/null 2>&1; then
$NFT insert rule inet ss_spec ss_spec_wan_ac ip daddr @oversea jump SS_SPEC_WAN_FW 2>/dev/null
$NFT add rule inet ss_spec ss_spec_wan_ac ip saddr @gmlan jump ss_spec_wan_fw 2>/dev/null
@@ -309,10 +343,10 @@ ipset_iptables() {
esac
ipset -N fplan hash:net 2>/dev/null
for ip in $LAN_FP_IP; do ipset -! add fplan "$ip"; done
- $IPT -I SS_SPEC_WAN_AC -m set --match-set fplan src -j SS_SPEC_WAN_FW
+ $IPT -I SS_SPEC_WAN_AC -m set --match-set fplan src -j SS_SPEC_WAN_FW
ipset -N bplan hash:net 2>/dev/null
for ip in $LAN_BP_IP; do ipset -! add bplan "$ip"; done
- $IPT -I SS_SPEC_WAN_AC -m set --match-set bplan src -j RETURN
+ $IPT -I SS_SPEC_WAN_AC -m set --match-set bplan src -j RETURN
ipset -N whitelist hash:net 2>/dev/null
ipset -N blacklist hash:net 2>/dev/null
$IPT -I SS_SPEC_WAN_AC -m set --match-set blacklist dst -j SS_SPEC_WAN_FW
@@ -354,31 +388,24 @@ fw_rule() {
fw_rule_nft() {
# Exclude special local addresses
- $NFT add rule inet ss_spec ss_spec_wan_fw ip daddr 0.0.0.0/8 return
- $NFT add rule inet ss_spec ss_spec_wan_fw ip daddr 10.0.0.0/8 return
- $NFT add rule inet ss_spec ss_spec_wan_fw ip daddr 127.0.0.0/8 return
- $NFT add rule inet ss_spec ss_spec_wan_fw ip daddr 169.254.0.0/16 return
- $NFT add rule inet ss_spec ss_spec_wan_fw ip daddr 172.16.0.0/12 return
- $NFT add rule inet ss_spec ss_spec_wan_fw ip daddr 192.168.0.0/16 return
- $NFT add rule inet ss_spec ss_spec_wan_fw ip daddr 224.0.0.0/4 return
- $NFT add rule inet ss_spec ss_spec_wan_fw ip daddr 240.0.0.0/4 return
+ if $NFT list chain inet ss_spec ss_spec_wan_fw >/dev/null 2>&1; then
+ for net in 0.0.0.0/8 10.0.0.0/8 127.0.0.0/8 169.254.0.0/16 172.16.0.0/12 192.168.0.0/16 224.0.0.0/4 240.0.0.0/4; do
+ $NFT add rule inet ss_spec ss_spec_wan_fw ip daddr $net return 2>/dev/null
+ done
+ fi
# redirect/translation: when PROXY_PORTS present, redirect those tcp ports to local_port
if [ -n "$PROXY_PORTS" ]; then
PORTS=$(echo "$PROXY_PORTS" | sed 's/-m multiport --dports //')
- if ! $NFT list chain inet ss_spec ss_spec_wan_fw 2>/dev/null | grep -q "tcp dport { $PORTS } redirect to :$local_port"; then
- if ! $NFT add rule inet ss_spec ss_spec_wan_fw tcp dport { $PORTS } redirect to :"$local_port" 2>/dev/null; then
- loger 3 "Can't redirect, please check nftables."
- return 1
- fi
- fi
+ RULE="tcp dport { $PORTS } redirect to :$local_port"
else
# default: redirect everything except ssh(22)
- if ! $NFT list chain inet ss_spec ss_spec_wan_fw 2>/dev/null | grep -q "tcp dport != 22 redirect to :$local_port"; then
- if ! $NFT add rule inet ss_spec ss_spec_wan_fw tcp dport != 22 redirect to :$local_port 2>/dev/null; then
- loger 3 "Can't redirect, please check nftables."
- return 1
- fi
+ RULE="tcp dport != 22 redirect to :$local_port"
+ fi
+ if ! $NFT list chain inet ss_spec ss_spec_wan_fw 2>/dev/null | grep -q "$RULE"; then
+ if ! $NFT add rule inet ss_spec ss_spec_wan_fw $RULE 2>/dev/null; then
+ loger 3 "Can't redirect, please check nftables."
+ return 1
fi
fi
@@ -416,7 +443,12 @@ ac_rule_nft() {
if [ -n "$LAN_AC_IP" ]; then
# Create LAN access control set if needed
- $NFT add set inet ss_spec ss_spec_lan_ac '{ type ipv4_addr; flags interval; }' 2>/dev/null
+ if ! $NFT list set inet ss_spec ss_spec_lan_ac >/dev/null 2>&1; then
+ $NFT add set inet ss_spec ss_spec_lan_ac '{ type ipv4_addr; flags interval; }' 2>/dev/null
+ else
+ $NFT flush set inet ss_spec ss_spec_lan_ac 2>/dev/null
+ fi
+
for ip in ${LAN_AC_IP#?}; do
[ -n "$ip" ] && $NFT add element inet ss_spec ss_spec_lan_ac "{ $ip }" 2>/dev/null
done
@@ -436,25 +468,29 @@ ac_rule_nft() {
fi
# 创建ss_spec_prerouting链
- if ! $NFT list chain inet ss_spec_prerouting >/dev/null 2>&1; then
- $NFT add chain inet ss_spec ss_spec_prerouting '{ type filter hook prerouting priority -1; policy accept; }'
+ if ! $NFT list chain inet ss_spec ss_spec_prerouting >/dev/null 2>&1; then
+ $NFT add chain inet ss_spec ss_spec_prerouting '{ type nat hook prerouting priority -150; policy accept; }'
fi
$NFT flush chain inet ss_spec ss_spec_prerouting 2>/dev/null
# 创建ss_spec_output链
if ! $NFT list chain inet ss_spec ss_spec_output >/dev/null 2>&1; then
- $NFT add chain inet ss_spec ss_spec_output '{ type nat hook output priority -1; policy accept; }'
+ $NFT add chain inet ss_spec ss_spec_output '{ type nat hook output priority -100; policy accept; }'
fi
$NFT flush chain inet ss_spec ss_spec_output 2>/dev/null
# Build a rule in the prerouting hook chain that jumps to business chain with conditions
+ if [ -n "$PROXY_PORTS" ]; then
+ EXT_ARGS=$(echo "$PROXY_PORTS" | sed 's/-m multiport --dports //')
+ fi
+
if [ -z "$Interface" ]; then
# generic prerouting jump already exists (see ipset_nft), but if we have MATCH_SET_CONDITION we add a more specific rule
if [ -n "$MATCH_SET" ]; then
# add a more specific rule at the top of ss_spec_prerouting
- $NFT insert rule inet ss_spec ss_spec_prerouting tcp dport $EXT_ARGS $MATCH_SET comment "\"$TAG\"" jump ss_spec_wan_ac 2>/dev/null
+ $NFT insert rule inet ss_spec ss_spec_prerouting meta l4proto tcp th dport { $EXT_ARGS } $MATCH_SET jump ss_spec_wan_ac comment "\"$TAG\"" 2>/dev/null
else
- $NFT insert rule inet ss_spec ss_spec_prerouting tcp dport $EXT_ARGS comment "\"$TAG\"" jump ss_spec_wan_ac
+ $NFT insert rule inet ss_spec ss_spec_prerouting meta l4proto tcp th dport { $EXT_ARGS } jump ss_spec_wan_ac comment "\"$TAG\"" 2>/dev/null
fi
else
# For each Interface, find its actual ifname and add an iifname-limited prerouting rule
@@ -463,9 +499,9 @@ ac_rule_nft() {
[ -z "$IFNAME" ] && IFNAME=$(uci -P /var/state get network."$name".device 2>/dev/null)
if [ -n "$IFNAME" ]; then
if [ -n "$MATCH_SET" ]; then
- $NFT insert rule inet ss_spec ss_spec_prerouting iifname "$IFNAME" tcp dport $EXT_ARGS $MATCH_SET comment "\"$TAG\"" jump ss_spec_wan_ac 2>/dev/null
+ $NFT insert rule inet ss_spec ss_spec_prerouting meta iifname "$IFNAME" meta l4proto tcp th dport { $EXT_ARGS } $MATCH_SET jump ss_spec_wan_ac comment "\"$TAG\"" 2>/dev/null
else
- $NFT insert rule inet ss_spec ss_spec_prerouting iifname "$IFNAME" tcp dport $EXT_ARGS comment "\"$TAG\"" jump ss_spec_wan_ac 2>/dev/null
+ $NFT insert rule inet ss_spec ss_spec_prerouting meta iifname "$IFNAME" meta l4proto tcp th dport { $EXT_ARGS } jump ss_spec_wan_ac comment "\"$TAG\"" 2>/dev/null
fi
fi
done
@@ -474,7 +510,7 @@ ac_rule_nft() {
case "$OUTPUT" in
1)
# create output hook chain & route output traffic into router chain
- $NFT add rule inet ss_spec ss_spec_output tcp dport $EXT_ARGS comment "\"$TAG\"" jump ss_spec_wan_ac 2>/dev/null
+ $NFT insert rule inet ss_spec ss_spec_output meta l4proto tcp th dport { $EXT_ARGS } jump ss_spec_wan_ac comment "\"$TAG\"" 2>/dev/null
;;
2)
# router mode output chain: create ssr_gen_router set & router chain
@@ -485,7 +521,7 @@ ac_rule_nft() {
$NFT add chain inet ss_spec ss_spec_router 2>/dev/null
$NFT add rule inet ss_spec ss_spec_router ip daddr @ssr_gen_router return 2>/dev/null
$NFT add rule inet ss_spec ss_spec_router jump ss_spec_wan_fw 2>/dev/null
- $NFT add rule inet ss_spec ss_spec_output tcp dport $EXT_ARGS comment "\"$TAG\"" jump ss_spec_router 2>/dev/null
+ $NFT add rule inet ss_spec ss_spec_output meta l4proto tcp th dport { $EXT_ARGS } jump ss_spec_router comment "\"$TAG\"" 2>/dev/null
;;
esac
return 0
@@ -549,89 +585,170 @@ tp_rule() {
}
tp_rule_nft() {
- [ -n "$TPROXY" ] || return 0
-
# set up routing table for tproxy
ip rule add fwmark 0x01/0x01 table 100 2>/dev/null
ip route add local 0.0.0.0/0 dev lo table 100 2>/dev/null
# create mangle table and tproxy chain
- $NFT add table ip ss_spec_mangle 2>/dev/null
+ if ! $NFT list table ip ss_spec_mangle >/dev/null 2>&1; then
+ $NFT add table ip ss_spec_mangle 2>/dev/null
+ fi
+
+ local MATCH_SET=""
+ local EXT_ARGS=""
+
+ if [ -n "$PROXY_PORTS" ]; then
+ EXT_ARGS=$(echo "$PROXY_PORTS" | sed 's/-m multiport --dports //')
+ fi
+
+ if [ -n "$LAN_AC_IP" ]; then
+ # Create LAN access control set if needed
+ if ! $NFT list set ip ss_spec_mangle ss_spec_lan_ac >/dev/null 2>&1; then
+ $NFT add set ip ss_spec_mangle ss_spec_lan_ac '{ type ipv4_addr; flags interval; auto-merge; }' 2>/dev/null
+ else
+ $NFT flush set ip ss_spec_mangle ss_spec_lan_ac 2>/dev/null
+ fi
+
+ for ip in ${LAN_AC_IP#?}; do
+ [ -n "$ip" ] && $NFT add element ip ss_spec_mangle ss_spec_lan_ac "{ $ip }" 2>/dev/null
+ done
+
+ case "${LAN_AC_IP%${LAN_AC_IP#?}}" in
+ w | W)
+ MATCH_SET="ip saddr @ss_spec_lan_ac"
+ ;;
+ b | B)
+ MATCH_SET="ip saddr != @ss_spec_lan_ac"
+ ;;
+ *)
+ loger 3 "Bad argument \`-a $LAN_AC_IP\`."
+ return 2
+ ;;
+ esac
+ fi
+
+ # Create necessary collections
+ for setname in ss_spec_wan_ac china gmlan fplan bplan whitelist; do
+ if ! $NFT list set ip ss_spec_mangle $setname >/dev/null 2>&1; then
+ $NFT add set ip ss_spec_mangle $setname '{ type ipv4_addr; flags interval; auto-merge; }'
+ else
+ $NFT flush set ip ss_spec_mangle $setname 2>/dev/null
+ fi
+ done
+
+ # 批量导入中国IP列表
+ if [ -f "${china_ip:=/etc/ssrplus/china_ssr.txt}" ]; then
+ $NFT add element ip ss_spec_mangle china { $(tr '\n' ',' < "${china_ip}" | sed 's/,$//') } 2>/dev/null
+ fi
+
# use priority mangle for compatibility with other rules
- $NFT add chain ip ss_spec_mangle ss_spec_tproxy '{ type filter hook prerouting priority mangle; }' 2>/dev/null
+ if ! $NFT list chain ip ss_spec_mangle ss_spec_tproxy >/dev/null 2>&1; then
+ $NFT add chain ip ss_spec_mangle ss_spec_tproxy 2>/dev/null
+ else
+ $NFT flush chain ip ss_spec_mangle ss_spec_tproxy 2>/dev/null
+ fi
# basic return rules in tproxy chain
$NFT add rule ip ss_spec_mangle ss_spec_tproxy udp dport 53 return 2>/dev/null
- $NFT add rule ip ss_spec_mangle ss_spec_tproxy udp ip daddr 0.0.0.0/8 return 2>/dev/null
- $NFT add rule ip ss_spec_mangle ss_spec_tproxy udp ip daddr 10.0.0.0/8 return 2>/dev/null
- $NFT add rule ip ss_spec_mangle ss_spec_tproxy udp ip daddr 127.0.0.0/8 return 2>/dev/null
- $NFT add rule ip ss_spec_mangle ss_spec_tproxy udp ip daddr 169.254.0.0/16 return 2>/dev/null
- $NFT add rule ip ss_spec_mangle ss_spec_tproxy udp ip daddr 172.16.0.0/12 return 2>/dev/null
- $NFT add rule ip ss_spec_mangle ss_spec_tproxy udp ip daddr 192.168.0.0/16 return 2>/dev/null
- $NFT add rule ip ss_spec_mangle ss_spec_tproxy udp ip daddr 224.0.0.0/4 return 2>/dev/null
- $NFT add rule ip ss_spec_mangle ss_spec_tproxy udp ip daddr 240.0.0.0/4 return 2>/dev/null
+
+ if $NFT list chain ip ss_spec_mangle ss_spec_tproxy >/dev/null 2>&1; then
+ for net in 0.0.0.0/8 10.0.0.0/8 127.0.0.0/8 169.254.0.0/16 172.16.0.0/12 192.168.0.0/16 224.0.0.0/4 240.0.0.0/4; do
+ $NFT add rule ip ss_spec_mangle ss_spec_tproxy ip daddr $net return 2>/dev/null
+ done
+ fi
# avoid redirecting to udp server address
- $NFT add rule ip ss_spec_mangle ss_spec_tproxy udp dport != 53 ip daddr "$server" return 2>/dev/null
+ if [ -n "$server" ]; then
+ $NFT add rule ip ss_spec_mangle ss_spec_tproxy udp dport != 53 ip daddr "$server" return 2>/dev/null
+ fi
# if server != SERVER add SERVER to whitelist set (so tproxy won't touch it)
- if [ "$server" != "$SERVER" ]; then
- $NFT add element inet ss_spec whitelist "{ $SERVER }" 2>/dev/null
+ if [ -n "$server" ]; then
+ $NFT add rule ip ss_spec_mangle ss_spec_tproxy ip daddr "$server" return 2>/dev/null
+ fi
+ if [ -n "$SERVER" ] && [ "$server" != "$SERVER" ]; then
+ $NFT add element ip ss_spec_mangle whitelist "{ $SERVER }" 2>/dev/null
fi
# access control and tproxy rules
- $NFT add rule ip ss_spec_mangle ss_spec_tproxy udp ip saddr @bplan return 2>/dev/null
- if [ -n "$PROXY_PORTS" ]; then
- PORTS=$(echo "$PROXY_PORTS" | sed 's/-m multiport --dports //')
- $NFT add rule ip ss_spec_mangle ss_spec_tproxy udp dport { $PORTS } ip saddr @fplan tproxy to :"$LOCAL_PORT" meta mark set 0x01 2>/dev/null
+ $NFT add rule ip ss_spec_mangle ss_spec_tproxy meta l4proto udp ip saddr @bplan return 2>/dev/null
+
+ if [ -n "$EXT_ARGS" ]; then
+ $NFT add rule ip ss_spec_mangle ss_spec_tproxy udp dport { $EXT_ARGS } ip saddr @fplan tproxy to :"$LOCAL_PORT" meta mark set 0x01
else
- $NFT add rule ip ss_spec_mangle ss_spec_tproxy udp ip saddr @fplan tproxy to :"$LOCAL_PORT" meta mark set 0x01 2>/dev/null
+ $NFT add rule ip ss_spec_mangle ss_spec_tproxy meta l4proto udp ip saddr @fplan tproxy to :"$LOCAL_PORT" meta mark set 0x01 2>/dev/null
fi
-
+
# Handle different run modes for nftables
case "$RUNMODE" in
router)
- $NFT add rule ip ss_spec_mangle ss_spec_tproxy udp ip daddr @ss_spec_wan_ac return 2>/dev/null
- $NFT add rule ip ss_spec_mangle ss_spec_tproxy udp ip daddr @china return 2>/dev/null
+ $NFT add rule ip ss_spec_mangle ss_spec_tproxy meta l4proto udp ip daddr @ss_spec_wan_ac return 2>/dev/null
+ $NFT add rule ip ss_spec_mangle ss_spec_tproxy meta l4proto udp ip daddr @china return 2>/dev/null
$NFT add rule ip ss_spec_mangle ss_spec_tproxy udp dport 80 drop 2>/dev/null
- $NFT add rule ip ss_spec_mangle ss_spec_tproxy udp ip saddr @gmlan ip daddr != @china tproxy to :"$LOCAL_PORT" meta mark set 0x01 2>/dev/null
- if [ -n "$PROXY_PORTS" ]; then
- PORTS=$(echo "$PROXY_PORTS" | sed 's/-m multiport --dports //')
- $NFT add rule ip ss_spec_mangle ss_spec_tproxy udp dport { $PORTS } ip daddr != @ss_spec_wan_ac tproxy to :"$LOCAL_PORT" meta mark set 0x01 2>/dev/null
+ $NFT add rule ip ss_spec_mangle ss_spec_tproxy meta l4proto udp ip saddr @gmlan ip daddr != @china tproxy to :"$LOCAL_PORT" meta mark set 0x01 2>/dev/null
+ if [ -n "$EXT_ARGS" ]; then
+ $NFT add rule ip ss_spec_mangle ss_spec_tproxy udp dport { $EXT_ARGS } ip daddr != @ss_spec_wan_ac tproxy to :"$LOCAL_PORT" meta mark set 0x01 2>/dev/null
else
- $NFT add rule ip ss_spec_mangle ss_spec_tproxy udp ip daddr != @ss_spec_wan_ac tproxy to :"$LOCAL_PORT" meta mark set 0x01 2>/dev/null
+ $NFT add rule ip ss_spec_mangle ss_spec_tproxy meta l4proto udp ip daddr != @ss_spec_wan_ac tproxy to :"$LOCAL_PORT" meta mark set 0x01 2>/dev/null
fi
;;
gfw)
- $NFT add rule ip ss_spec_mangle ss_spec_tproxy udp ip daddr @china return 2>/dev/null
+ $NFT add rule ip ss_spec_mangle ss_spec_tproxy meta l4proto udp ip daddr @china return 2>/dev/null
$NFT add rule ip ss_spec_mangle ss_spec_tproxy udp dport 80 drop 2>/dev/null
- if [ -n "$PROXY_PORTS" ]; then
- PORTS=$(echo "$PROXY_PORTS" | sed 's/-m multiport --dports //')
- $NFT add rule ip ss_spec_mangle ss_spec_tproxy udp dport { $PORTS } ip daddr @gfwlist tproxy to :"$LOCAL_PORT" meta mark set 0x01 2>/dev/null
+ if [ -n "$EXT_ARGS" ]; then
+ $NFT add rule ip ss_spec_mangle ss_spec_tproxy udp dport { $EXT_ARGS } ip daddr @gfwlist tproxy to :"$LOCAL_PORT" meta mark set 0x01 2>/dev/null
fi
- $NFT add rule ip ss_spec_mangle ss_spec_tproxy udp ip saddr @gmlan ip daddr != @china tproxy to :"$LOCAL_PORT" meta mark set 0x01 2>/dev/null
+ $NFT add rule ip ss_spec_mangle ss_spec_tproxy meta l4proto udp ip saddr @gmlan ip daddr != @china tproxy to :"$LOCAL_PORT" meta mark set 0x01 2>/dev/null
;;
oversea)
- if [ -n "$PROXY_PORTS" ]; then
- PORTS=$(echo "$PROXY_PORTS" | sed 's/-m multiport --dports //')
- $NFT add rule ip ss_spec_mangle ss_spec_tproxy udp dport { $PORTS } ip saddr @oversea tproxy to :"$LOCAL_PORT" meta mark set 0x01 2>/dev/null
- $NFT add rule ip ss_spec_mangle ss_spec_tproxy udp dport { $PORTS } ip daddr @china tproxy to :"$LOCAL_PORT" meta mark set 0x01 2>/dev/null
+ if ! $NFT list set ip ss_spec_mangle oversea >/dev/null 2>&1; then
+ $NFT add set ip ss_spec_mangle oversea '{ type ipv4_addr; flags interval; auto-merge; }' 2>/dev/null
fi
- $NFT add rule ip ss_spec_mangle ss_spec_tproxy udp ip saddr @gmlan tproxy to :"$LOCAL_PORT" meta mark set 0x01 2>/dev/null
+ if ! $NFT list set ip ss_spec_mangle china >/dev/null 2>&1; then
+ $NFT add set ip ss_spec_mangle china '{ type ipv4_addr; flags interval; auto-merge; }' 2>/dev/null
+ fi
+ if [ -n "$EXT_ARGS" ]; then
+ $NFT add rule ip ss_spec_mangle ss_spec_tproxy udp dport { $EXT_ARGS } ip saddr @oversea tproxy to :"$LOCAL_PORT" meta mark set 0x01 2>/dev/null
+ $NFT add rule ip ss_spec_mangle ss_spec_tproxy udp dport { $EXT_ARGS } ip daddr @china tproxy to :"$LOCAL_PORT" meta mark set 0x01 2>/dev/null
+ fi
+ $NFT add rule ip ss_spec_mangle ss_spec_tproxy meta l4proto udp ip saddr @gmlan tproxy to :"$LOCAL_PORT" meta mark set 0x01 2>/dev/null
;;
all)
- if [ -n "$PROXY_PORTS" ]; then
- PORTS=$(echo "$PROXY_PORTS" | sed 's/-m multiport --dports //')
- $NFT add rule ip ss_spec_mangle ss_spec_tproxy udp dport { $PORTS } tproxy to :"$LOCAL_PORT" meta mark set 0x01 2>/dev/null
+ if [ -n "$EXT_ARGS" ]; then
+ $NFT add rule ip ss_spec_mangle ss_spec_tproxy udp dport { $EXT_ARGS } tproxy to :"$LOCAL_PORT" meta mark set 0x01 2>/dev/null
else
$NFT add rule ip ss_spec_mangle ss_spec_tproxy udp tproxy to :"$LOCAL_PORT" meta mark set 0x01 2>/dev/null
fi
;;
esac
- # insert jump from ip prerouting to our tproxy chain
- PORTS=$(echo "$PROXY_PORTS" | sed 's/-m multiport --dports //')
- $NFT add rule ip ss_spec_mangle prerouting udp dport { $PORTS } comment "\"$TAG\"" jump ss_spec_tproxy 2>/dev/null
+ # 创建 prerouting 链(hook prerouting)
+ if ! $NFT list chain ip ss_spec_mangle prerouting >/dev/null 2>&1; then
+ $NFT add chain ip ss_spec_mangle prerouting '{ type filter hook prerouting priority mangle; policy accept; }'
+ fi
+
+ # 添加规则到 prerouting 链
+ if [ -z "$Interface" ]; then
+ # 全局规则
+ if [ -n "$MATCH_SET" ]; then
+ $NFT add rule ip ss_spec_mangle prerouting udp dport { $EXT_ARGS } $MATCH_SET jump ss_spec_tproxy comment "\"$TAG\"" 2>/dev/null
+ else
+ $NFT add rule ip ss_spec_mangle prerouting udp dport { $EXT_ARGS } jump ss_spec_tproxy comment "\"$TAG\"" 2>/dev/null
+ fi
+ else
+ # 指定接口
+ for name in $Interface; do
+ IFNAME=$(uci -P /var/state get network."$name".ifname 2>/dev/null)
+ [ -z "$IFNAME" ] && IFNAME=$(uci -P /var/state get network."$name".device 2>/dev/null)
+ if [ -n "$IFNAME" ]; then
+ if [ -n "$MATCH_SET" ]; then
+ $NFT add rule ip ss_spec_mangle prerouting meta iifname "$IFNAME" udp dport { $EXT_ARGS } $MATCH_SET jump ss_spec_tproxy comment "\"$TAG\"" 2>/dev/null
+ else
+ $NFT add rule ip ss_spec_mangle prerouting meta iifname "$IFNAME" udp dport { $EXT_ARGS } jump ss_spec_tproxy comment "\"$TAG\"" 2>/dev/null
+ fi
+ fi
+ done
+ fi
return $?
}
@@ -734,12 +851,12 @@ gen_include_nft() {
[ -n "$FWI" ] && echo '#!/bin/sh' >"$FWI"
cat <<-'EOF' >>"$FWI"
# Clear existing ss_spec tables
- nft add table inet ss_spec 2>/dev/null
- nft add table ip ss_spec 2>/dev/null
- nft add table ip ss_spec_mangle 2>/dev/null
+ nft delete table inet ss_spec 2>/dev/null
+ nft delete table ip ss_spec 2>/dev/null
+ nft delete table ip ss_spec_mangle 2>/dev/null
# Restore shadowsocks nftables rules
- nft list ruleset | awk '/table (inet|ip) ss_spec/{flag=1} flag'
+ nft list ruleset | awk '/^table (inet|ip) ss_spec/{flag=1} /^table / && !/^table (inet|ip) ss_spec/{flag=0} flag'
EOF
chmod +x "$FWI"
}
@@ -867,7 +984,7 @@ case "$TPROXY" in
;;
esac
-# 首先检查nftables是否正常工作
+# First check whether nftables is working properly
if [ "$USE_NFT" = "1" ]; then
if ! $NFT list tables 2>/dev/null; then
loger 3 "nftables is not working properly, check if nftables is installed and running"
diff --git a/luci-app-ssr-plus/root/usr/share/shadowsocksr/chinaipset.sh b/luci-app-ssr-plus/root/usr/share/shadowsocksr/chinaipset.sh
index a15adc0..4015644 100755
--- a/luci-app-ssr-plus/root/usr/share/shadowsocksr/chinaipset.sh
+++ b/luci-app-ssr-plus/root/usr/share/shadowsocksr/chinaipset.sh
@@ -1,22 +1,7 @@
#!/bin/sh
[ -f "$1" ] && china_ip=$1
-
-if command -v nft >/dev/null 2>&1; then
- # 确保表和集合存在
- nft add table inet ss_spec 2>/dev/null
- nft add set inet ss_spec china '{ type ipv4_addr; flags interval; auto-merge; }' 2>/dev/null
- nft flush set inet ss_spec china 2>/dev/null
-
- # 批量导入
- if [ -f "${china_ip:=/etc/ssrplus/china_ssr.txt}" ]; then
- echo "批量导入中国IP列表..."
- nft add element inet ss_spec china { $(tr '\n' ',' < "${china_ip}" | sed 's/,$//') } 2>/dev/null
- echo "中国IP集合导入完成"
- fi
-else
- ipset -! flush china 2>/dev/null
- ipset -! -R <<-EOF || exit 1
- create china hash:net
- $(cat ${china_ip:=/etc/ssrplus/china_ssr.txt} | sed -e "s/^/add china /")
+ipset -! flush china 2>/dev/null
+ipset -! -R <<-EOF || exit 1
+ create china hash:net
+ $(cat ${china_ip:=/etc/ssrplus/china_ssr.txt} | sed -e "s/^/add china /")
EOF
-fi
diff --git a/luci-app-ssr-plus/root/usr/share/shadowsocksr/gfw2ipset.sh b/luci-app-ssr-plus/root/usr/share/shadowsocksr/gfw2ipset.sh
index 8c1f07b..33808cc 100755
--- a/luci-app-ssr-plus/root/usr/share/shadowsocksr/gfw2ipset.sh
+++ b/luci-app-ssr-plus/root/usr/share/shadowsocksr/gfw2ipset.sh
@@ -2,14 +2,18 @@
. $IPKG_INSTROOT/etc/init.d/shadowsocksr
+if command -v nft >/dev/null 2>&1; then
+ nft_support=1
+fi
+
netflix() {
if [ -f "$TMP_DNSMASQ_PATH/gfw_list.conf" ]; then
for line in $(cat /etc/ssrplus/netflix.list); do sed -i "/$line/d" $TMP_DNSMASQ_PATH/gfw_list.conf; done
for line in $(cat /etc/ssrplus/netflix.list); do sed -i "/$line/d" $TMP_DNSMASQ_PATH/gfw_base.conf; done
fi
- if command -v nft >/dev/null 2>&1; then
+ if [ "$nft_support" = "1" ]; then
# 移除 ipset
- cat /etc/ssrplus/netflix.list | sed '/^$/d' | sed '/#/d' | sed "/.*/s/.*/server=\/&\/127.0.0.1#$1/" >$TMP_DNSMASQ_PATH/netflix_forward.conf
+ cat /etc/ssrplus/netflix.list | sed '/^$/d' | sed '/#/d' | sed "/.*/s/.*/server=\/&\/127.0.0.1#$1\nnftset=\/&\/4#inet#ss_spec#netflix/" >$TMP_DNSMASQ_PATH/netflix_forward.conf
else
cat /etc/ssrplus/netflix.list | sed '/^$/d' | sed '/#/d' | sed "/.*/s/.*/server=\/&\/127.0.0.1#$1\nipset=\/&\/netflix/" >$TMP_DNSMASQ_PATH/netflix_forward.conf
fi
@@ -22,11 +26,11 @@ else
cp -rf /etc/ssrplus/gfw_base.conf $TMP_DNSMASQ_PATH/
fi
-if command -v nft >/dev/null 2>&1; then
+if [ "$nft_support" = "1" ]; then
# 移除 ipset 指令
for conf_file in gfw_base.conf gfw_list.conf; do
if [ -f "$TMP_DNSMASQ_PATH/$conf_file" ]; then
- sed -i '/ipset=/d' "$TMP_DNSMASQ_PATH/$conf_file"
+ sed -i 's|ipset=/\([^/]*\)/\([^[:space:]]*\)|nftset=/\1/4#inet#ss_spec#\2|g' "$TMP_DNSMASQ_PATH/$conf_file"
fi
done
fi
@@ -59,9 +63,9 @@ while read line; do sed -i "/$line/d" $TMP_DNSMASQ_PATH/gfw_list.conf; done < /e
while read line; do sed -i "/$line/d" $TMP_DNSMASQ_PATH/gfw_base.conf; done < /etc/ssrplus/deny.list
# 此处直接使用 cat 因为有 sed '/#/d' 删除了 数据
-if command -v nft >/dev/null 2>&1; then
- cat /etc/ssrplus/black.list | sed '/^$/d' | sed '/#/d' | sed "/.*/s/.*/server=\/&\/127.0.0.1#$dns_port/" >$TMP_DNSMASQ_PATH/blacklist_forward.conf
- cat /etc/ssrplus/white.list | sed '/^$/d' | sed '/#/d' | sed "/.*/s/.*/server=\/&\/127.0.0.1/" >$TMP_DNSMASQ_PATH/whitelist_forward.conf
+if [ "$nft_support" = "1" ]; then
+ cat /etc/ssrplus/black.list | sed '/^$/d' | sed '/#/d' | sed "/.*/s/.*/server=\/&\/127.0.0.1#$dns_port\nnftset=\/&\/4#inet#ss_spec#blacklist/" >$TMP_DNSMASQ_PATH/blacklist_forward.conf
+ cat /etc/ssrplus/white.list | sed '/^$/d' | sed '/#/d' | sed "/.*/s/.*/server=\/&\/127.0.0.1\nnftset=\/&\/4#inet#ss_spec#whitelist/" >$TMP_DNSMASQ_PATH/whitelist_forward.conf
else
cat /etc/ssrplus/black.list | sed '/^$/d' | sed '/#/d' | sed "/.*/s/.*/server=\/&\/127.0.0.1#$dns_port\nipset=\/&\/blacklist/" >$TMP_DNSMASQ_PATH/blacklist_forward.conf
cat /etc/ssrplus/white.list | sed '/^$/d' | sed '/#/d' | sed "/.*/s/.*/server=\/&\/127.0.0.1\nipset=\/&\/whitelist/" >$TMP_DNSMASQ_PATH/whitelist_forward.conf