🤞 Sync 2026-09-30 09:28:31

This commit is contained in:
github-actions[bot] committed 2026-09-30 09:28:31 +08:00
1 parent 0108cfa10a
commit 3d9edb1c37
78 files changed
+4594 -1815

No files matched your search

+1 -1
View File
@@ -1,2 +1,2 @@
c3d5b719f5337ceefa8b5b1f6c1d7b96965a3812
80de46ce77221bee421a3c19f7d9d0190ee1b7cd
e192c5983e50169ed984d48b094798869a0a780b
+168
View File
@@ -0,0 +1,168 @@
msgid ""
msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Language: pt\n"
"Content-Transfer-Encoding: 8bit\n"
"Last-Translator: Pavel Borecki <pavel.borecki@gmail.com>\n"
"Language-Team: Portuguese <https://hosted.weblate.org/projects/openwrt/"
"luciapplicationsadguardhome/pt/>\n"
"Plural-Forms: nplurals=2; plural=n > 1;\n"
"PO-Revision-Date: 2026-09-29 12:10+0000\n"
"X-Generator: Weblate 2026.10.dev0\n"
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:244
msgid ""
"A soft memory cap for the Go runtime, allowing the garbage collector to run "
"more frequently as usage approaches the limit to prevent Out-of-Memory (OOM) "
"kills."
msgstr ""
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:113
#: applications/luci-app-adguardhome/root/usr/share/luci/menu.d/luci-app-adguardhome.json:3
msgid "AdGuard Home"
msgstr "AdGuard Home"
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:137
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:199
msgid "Advanced Settings"
msgstr "Configurações avançadas"
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:146
msgid "Configuration file"
msgstr ""
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:89
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:147
msgid ""
"Configuration file must be stored in its own directory, and not in '/etc'."
msgstr ""
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:159
msgid "Directory where filters, logs, and statistics are stored."
msgstr ""
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:132
msgid "File System Access"
msgstr ""
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:133
msgid ""
"Files and directories that AdGuard Home should have read-only or read-write "
"access to."
msgstr ""
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:129
msgid "General Settings"
msgstr "Configurações gerais"
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:138
msgid ""
"Go environment variables that tune garbage collector and memory management."
msgstr ""
#: applications/luci-app-adguardhome/root/usr/share/rpcd/acl.d/luci-app-adguardhome.json:3
msgid "Grant permissions for the AdGuard Home LuCI app"
msgstr ""
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:181
msgid "Group the service runs under."
msgstr ""
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:149
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:161
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:171
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:181
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:217
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:232
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:247
msgid "If empty, defaults to"
msgstr ""
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:139
msgid "Modify at your own risk."
msgstr ""
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:24
msgid "Not running"
msgstr "Não em execução"
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:148
msgid "Parent directory will be owned by the service user."
msgstr ""
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:83
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:99
msgid "Path must be absolute."
msgstr ""
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:86
msgid "Path must not end with a slash."
msgstr ""
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:207
msgid "Read-only access"
msgstr ""
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:208
msgid "Read-write access"
msgstr ""
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:23
msgid "Running"
msgstr "A executar"
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:122
msgid "Service Status"
msgstr "Estado do Serviço"
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:180
msgid "Service group"
msgstr ""
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:170
msgid "Service user"
msgstr ""
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:230
msgid ""
"The maximum number of operating system threads that can execute user-level "
"Go code simultaneously."
msgstr ""
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:215
msgid ""
"Tunes the garbage collector's aggressiveness by setting the percentage of "
"heap growth allowed before the next collection cycle triggers."
msgstr ""
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:171
msgid "User the service runs under."
msgstr ""
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:191
msgid "Verbose logging"
msgstr "Registo de eventos verboso"
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:119
msgid "Version"
msgstr "Versão"
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:160
msgid "Will be owned by the service user."
msgstr ""
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:158
msgid "Working directory"
msgstr ""
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:247
msgid "unset"
msgstr ""
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:217
msgid "unset and 100"
msgstr ""
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:232
msgid "unset and matching the number of CPUs"
msgstr ""
@@ -0,0 +1,168 @@
msgid ""
msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Language: pt_BR\n"
"Content-Transfer-Encoding: 8bit\n"
"Last-Translator: Pavel Borecki <pavel.borecki@gmail.com>\n"
"Language-Team: Portuguese (Brazil) <https://hosted.weblate.org/projects/"
"openwrt/luciapplicationsadguardhome/pt_BR/>\n"
"Plural-Forms: nplurals=2; plural=n > 1;\n"
"PO-Revision-Date: 2026-09-29 13:09+0000\n"
"X-Generator: Weblate 2026.10.dev0\n"
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:244
msgid ""
"A soft memory cap for the Go runtime, allowing the garbage collector to run "
"more frequently as usage approaches the limit to prevent Out-of-Memory (OOM) "
"kills."
msgstr ""
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:113
#: applications/luci-app-adguardhome/root/usr/share/luci/menu.d/luci-app-adguardhome.json:3
msgid "AdGuard Home"
msgstr "AdGuard Home"
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:137
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:199
msgid "Advanced Settings"
msgstr "Configurações avançadas"
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:146
msgid "Configuration file"
msgstr ""
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:89
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:147
msgid ""
"Configuration file must be stored in its own directory, and not in '/etc'."
msgstr ""
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:159
msgid "Directory where filters, logs, and statistics are stored."
msgstr ""
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:132
msgid "File System Access"
msgstr ""
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:133
msgid ""
"Files and directories that AdGuard Home should have read-only or read-write "
"access to."
msgstr ""
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:129
msgid "General Settings"
msgstr "Configurações gerais"
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:138
msgid ""
"Go environment variables that tune garbage collector and memory management."
msgstr ""
#: applications/luci-app-adguardhome/root/usr/share/rpcd/acl.d/luci-app-adguardhome.json:3
msgid "Grant permissions for the AdGuard Home LuCI app"
msgstr ""
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:181
msgid "Group the service runs under."
msgstr ""
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:149
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:161
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:171
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:181
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:217
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:232
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:247
msgid "If empty, defaults to"
msgstr ""
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:139
msgid "Modify at your own risk."
msgstr ""
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:24
msgid "Not running"
msgstr ""
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:148
msgid "Parent directory will be owned by the service user."
msgstr ""
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:83
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:99
msgid "Path must be absolute."
msgstr ""
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:86
msgid "Path must not end with a slash."
msgstr ""
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:207
msgid "Read-only access"
msgstr ""
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:208
msgid "Read-write access"
msgstr ""
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:23
msgid "Running"
msgstr "Em execução"
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:122
msgid "Service Status"
msgstr "Condição do Serviço"
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:180
msgid "Service group"
msgstr ""
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:170
msgid "Service user"
msgstr ""
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:230
msgid ""
"The maximum number of operating system threads that can execute user-level "
"Go code simultaneously."
msgstr ""
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:215
msgid ""
"Tunes the garbage collector's aggressiveness by setting the percentage of "
"heap growth allowed before the next collection cycle triggers."
msgstr ""
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:171
msgid "User the service runs under."
msgstr ""
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:191
msgid "Verbose logging"
msgstr ""
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:119
msgid "Version"
msgstr "Versão"
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:160
msgid "Will be owned by the service user."
msgstr ""
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:158
msgid "Working directory"
msgstr ""
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:247
msgid "unset"
msgstr ""
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:217
msgid "unset and 100"
msgstr ""
#: applications/luci-app-adguardhome/htdocs/luci-static/resources/view/adguardhome/config.js:232
msgid "unset and matching the number of CPUs"
msgstr ""
+1 -1
View File
@@ -12,7 +12,7 @@ LUCI_DEPENDS:=+luci-base +logd +jsonfilter +coreutils-timeout
LUCI_PKGARCH:=all
PKG_VERSION:=0.1.47
PKG_RELEASE:=50
PKG_RELEASE:=51
# Reproducible build: honor SOURCE_DATE_EPOCH from the build environment (set by docker-sdk.sh / CI).
ifdef SOURCE_DATE_EPOCH
@@ -886,6 +886,8 @@ return view.extend({
return _('No WAN zone found; cannot toggle logging without one.');
if (res && res.error === 'lock_failed')
return _('Could not acquire the logging lock.');
if (res && res.error === 'rollback_tracking_failed')
return _('Could not track the logging change safely; logging was not changed.');
if (res && res.error === 'baseline_snapshot_failed')
return _('Could not snapshot the current logging state.');
if (res && res.error === 'firewall_reload_failed')
@@ -927,6 +929,8 @@ return view.extend({
return _('No WAN zone found; cannot toggle logging without one.');
if (res && res.error === 'lock_failed')
return _('Could not acquire the logging lock.');
if (res && res.error === 'rollback_tracking_failed')
return _('Could not track the logging change safely; logging was not changed.');
if (res && res.error === 'firewall_reload_failed')
return _('The firewall did not reload; saved and live logging may differ.');
if (res && res.error === 'uci_set_failed')
+151 -146
View File
@@ -12,16 +12,16 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1635
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1639
msgid "%d matching · %d/%d stored"
msgstr "%d Treffer · %d/%d gespeichert"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1429
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1433
msgid "%s · requested up to %d raw lines · %d firewall messages returned"
msgstr ""
"%s · bis zu %d Rohzeilen angefordert · %d Firewall-Meldungen zurückgegeben"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1419
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1423
msgid ""
"%s · requested up to %d raw lines · server limited fetch to %d · %d firewall "
"messages returned"
@@ -29,11 +29,11 @@ msgstr ""
"%s · bis zu %d Rohzeilen angefordert · Server begrenzte Abruf auf %d · %d "
"Firewall-Meldungen zurückgegeben"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/log.js:502
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/log.js:543
msgid "%s: %s"
msgstr "%s: %s"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1637
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1641
msgid "0 matching · %d/%d stored"
msgstr "0 Treffer · %d/%d gespeichert"
@@ -41,17 +41,17 @@ msgstr "0 Treffer · %d/%d gespeichert"
msgid "Accessible (teal/orange)"
msgstr "Barrierefrei (teal/orange)"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/log.js:362
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/log.js:403
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/table.js:37
msgid "Action"
msgstr "Aktion"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:945
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:949
msgid "Administrator access is required to disable logging."
msgstr ""
"Zum Deaktivieren der Protokollierung sind Administratorrechte erforderlich."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:912
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:914
msgid "Administrator access is required to enable logging."
msgstr ""
"Zum Aktivieren der Protokollierung sind Administratorrechte erforderlich."
@@ -60,19 +60,19 @@ msgstr ""
msgid "Allow/deny rules, LAN logging, or anything else."
msgstr "Allow/Deny-Regeln, LAN-Protokollierung oder irgendetwas anderes."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1829
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1833
msgid "Also seen"
msgstr "Weitere"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:883
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:924
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:926
msgid "Another change is staged for the firewall; apply or revert it first."
msgstr ""
"Eine andere Änderung an der Firewall ist vorgemerkt; zuerst anwenden oder "
"verwerfen."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:901
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:940
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:903
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:944
msgid ""
"Another change overwrote WAN logging after it was saved; check the current "
"state."
@@ -80,16 +80,16 @@ msgstr ""
"Eine andere Änderung hat die WAN-Protokollierung nach dem Speichern "
"überschrieben; prüfen Sie den aktuellen Zustand."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2638
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2642
msgid "Any action"
msgstr "Beliebige Aktion"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1822
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1826
msgid "Any protocol"
msgstr "Beliebiges Protokoll"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1410
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1470
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1414
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1474
msgid "Auto"
msgstr "Auto"
@@ -111,7 +111,7 @@ msgstr "Änderungen:"
msgid "Classic (green/red)"
msgstr "Klassisch (grün/rot)"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2369
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2373
msgid "Classic uses green/red; Accessible uses teal/orange"
msgstr "Klassisch verwendet grün/rot; Barrierefrei verwendet teal/orange"
@@ -119,7 +119,7 @@ msgstr "Klassisch verwendet grün/rot; Barrierefrei verwendet teal/orange"
msgid "Clear all"
msgstr "Alle löschen"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2710
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2714
msgid ""
"Click a cell to filter · ≠ on a chip to exclude · Ctrl+click a rule for "
"firewall settings · in Simple view, click a row for the full message"
@@ -128,7 +128,7 @@ msgstr ""
"auf eine Regel für Firewall-Einstellungen · in der Einfachen Ansicht Zeile "
"anklicken für die vollständige Meldung"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2437
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2441
msgid ""
"Click a row (Time or other non-link cells) to see the full log line (Simple "
"view)."
@@ -140,7 +140,7 @@ msgstr ""
msgid "Click a row for the full message"
msgstr "Zeile anklicken für die vollständige Meldung"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2442
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2446
msgid ""
"Click an IP, action, or protocol to filter; use the Protocol menu (or ≠ on a "
"chip) to exclude."
@@ -148,29 +148,29 @@ msgstr ""
"Klicken Sie auf eine IP, Aktion oder ein Protokoll, um zu filtern; nutzen "
"Sie das Protokoll-Menü (oder ≠ auf einem Chip) zum Ausschließen."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1823
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1827
msgid "Common"
msgstr "Häufig"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1654
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1658
msgid "Connection lost — retrying…"
msgstr "Verbindung verloren — neuer Versuch…"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:888
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:929
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:931
msgid "Could not acquire the logging lock."
msgstr "Die Protokollierungssperre konnte nicht erworben werden."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:896
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:935
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:898
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:939
msgid "Could not clear the WAN zone log option."
msgstr "Die WAN-Zonen-Log-Option konnte nicht gelöscht werden."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:942
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:946
msgid "Could not disable logging."
msgstr "Protokollierung konnte nicht deaktiviert werden."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:903
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:905
msgid "Could not enable logging."
msgstr "Protokollierung konnte nicht aktiviert werden."
@@ -184,21 +184,26 @@ msgstr ""
"Protokollstatus konnte nicht aktualisiert werden; zeige den zuletzt "
"bekannten Zustand."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:898
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:937
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:900
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:941
msgid "Could not save the firewall configuration."
msgstr "Die Firewall-Konfiguration konnte nicht gespeichert werden."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:890
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:892
msgid "Could not snapshot the current logging state."
msgstr "Der aktuelle Protokollierungszustand konnte nicht gesichert werden."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:894
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:890
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:933
msgid "Could not track the logging change safely; logging was not changed."
msgstr "Die Änderung der Protokollierung konnte nicht sicher nachverfolgt werden; die Protokollierung wurde nicht geändert."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:896
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:937
msgid "Could not write the WAN zone log option."
msgstr "Die WAN-Zonen-Log-Option konnte nicht geschrieben werden."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2665
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2669
msgid "Custom protocol (prefix ! to exclude). Overrides the menu when set."
msgstr ""
"Eigenes Protokoll (! zum Ausschließen). Überschreibt das Menü, wenn gesetzt."
@@ -207,34 +212,34 @@ msgstr ""
msgid "DPort"
msgstr "Ziel-Port"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1272
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1276
msgid "Degraded — sampling"
msgstr "Eingeschränkt — Stichproben"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1441
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1445
msgid "Degraded — sampling (slow poll RTT; cadence reduced)."
msgstr ""
"Eingeschränkt — Stichproben (langsame Poll-Roundtrip-Zeit; Frequenz "
"reduziert)."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/log.js:366
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/log.js:407
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/table.js:45
msgid "Destination"
msgstr "Ziel"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2695
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2699
msgid "Destination IP contains (! to exclude)"
msgstr "Ziel-IP enthält (! zum Ausschließen)"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/log.js:368
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/log.js:409
msgid "Destination port"
msgstr "Ziel-Port"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2700
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2704
msgid "Destination port (! to exclude)"
msgstr "Ziel-Port (! zum Ausschließen)"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2553
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2557
msgid "Detail"
msgstr "Detail"
@@ -246,15 +251,15 @@ msgstr "Richtung"
msgid "Disabling…"
msgstr "Wird deaktiviert…"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1268
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1272
msgid "Display limited to %d rows on this device"
msgstr "Anzeige auf %d Zeilen auf diesem Gerät begrenzt"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2310
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2314
msgid "Display options"
msgstr "Anzeigeoptionen"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2402
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2406
msgid "Display options on the bar set Limit, row tint, palette, and hostnames."
msgstr ""
"Anzeigeoptionen in der Leiste setzen Limit, Zeilenfärbung, Palette und "
@@ -282,7 +287,7 @@ msgstr ""
msgid "Enable logging"
msgstr "Protokollierung aktivieren"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2398
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2402
msgid ""
"Enable logging turns on WAN zone drop/reject logging only (same as Network → "
"Firewall). It does not add rules or log normal LAN browsing."
@@ -296,7 +301,7 @@ msgstr ""
msgid "Enabling…"
msgstr "Wird aktiviert…"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1836
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1840
msgid "Exclude"
msgstr "Ausschließen"
@@ -304,11 +309,11 @@ msgstr "Ausschließen"
msgid "Exclude instead"
msgstr "Stattdessen ausschließen"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2324
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2328
msgid "Fetch budget"
msgstr "Abrufbudget"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2406
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2410
msgid ""
"Fetch budget controls raw log lines per poll. Auto derives from Limit; "
"Manual selects a bounded maximum."
@@ -328,7 +333,7 @@ msgstr ""
"Logs nach Regel filtern (Hinweis: %s). Strg+Klick öffnet die Firewall-"
"Einstellungen."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2480
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2484
#: openwrt-feed/luci-app-fwlive/root/usr/share/luci/menu.d/luci-app-fwlive.json:3
msgid "Firewall Live View"
msgstr "Firewall-Live-Ansicht"
@@ -341,7 +346,7 @@ msgstr "Flags"
msgid "Flow"
msgstr "Fluss"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2416
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2420
msgid ""
"For a responsive table on a weak device, keep Limit at 250 rows or below. "
"The weak-device cap affects rendered rows; the buffer can still retain more."
@@ -358,15 +363,15 @@ msgstr "Weitergeleitet"
msgid "Grant access to firewall live log view"
msgstr "Zugriff auf die Live-Ansicht der Firewall-Protokolle gewähren"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2389
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2393
msgid "Help"
msgstr "Hilfe"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1548
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1552
msgid "Hide rows"
msgstr "Zeilen ausblenden"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1603
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1607
msgid ""
"High event rate — table refresh is throttled to protect the browser. The "
"buffer still updates; refresh will resume automatically."
@@ -375,11 +380,11 @@ msgstr ""
"Browser zu schonen. Der Puffer wird weiter aktualisiert; die Anzeige wird "
"automatisch fortgesetzt."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1448
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1452
msgid "Hostname resolve paused while the router is under load."
msgstr "Hostnamenauflösung pausiert, solange der Router ausgelastet ist."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1827
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1831
msgid "ICMPv6"
msgstr "ICMPv6"
@@ -387,7 +392,7 @@ msgstr "ICMPv6"
msgid "IN"
msgstr "EINGANG"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2453
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2457
msgid ""
"If Row tint looks missing, the active LuCI theme may omit success/error or "
"info/warn CSS variables; fwlive falls back to local colors (air-gapped, no "
@@ -421,16 +426,16 @@ msgstr ""
"Installieren Sie kmod-nf-log und kmod-nf-log6 mit dem Befehl für Ihre "
"OpenWrt-Version und laden Sie dann die Firewall neu."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1653
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1657
msgid "Installation is incomplete. Reinstall luci-app-fwlive."
msgstr "Installation unvollständig. Installieren Sie luci-app-fwlive neu."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/log.js:363
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/log.js:404
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/table.js:39
msgid "Interface"
msgstr "Schnittstelle"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2680
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2684
msgid "Interface (prefix ! to exclude)"
msgstr "Schnittstelle (! voranstellen zum Ausschließen)"
@@ -447,7 +452,7 @@ msgstr "Kernel-Logmodule fehlen"
msgid "Len"
msgstr "Länge"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2313
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2317
msgid "Limit"
msgstr "Limit"
@@ -461,16 +466,16 @@ msgstr ""
msgid "Logging is off on this router"
msgstr "Protokollierung ist auf diesem Router deaktiviert"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1410
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1471
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1414
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1475
msgid "Manual"
msgstr "Manuell"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2342
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2346
msgid "Manual still uses server protection and poll cadence"
msgstr "Manuell nutzt weiterhin Serverschutz und Abfrageintervall"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2411
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2415
msgid ""
"Manual still uses server protection and poll cadence; it changes the fetch "
"budget, not the polling interval."
@@ -482,18 +487,18 @@ msgstr ""
msgid "Manual test (System → Terminal):"
msgstr "Manueller Test (System → Terminal):"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2336
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2340
msgid "Maximum raw lines"
msgstr "Maximale Rohzeilen"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/table.js:51
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/table.js:344
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2565
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2572
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2569
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2576
msgid "Message"
msgstr "Nachricht"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2675
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2679
msgid "More filters"
msgstr "Weitere Filter"
@@ -513,7 +518,7 @@ msgid "No WAN zone found"
msgstr "Keine WAN-Zone gefunden"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:886
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:927
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:929
msgid "No WAN zone found; cannot toggle logging without one."
msgstr ""
"Keine WAN-Zone gefunden; Protokollierung kann ohne eine solche nicht "
@@ -531,7 +536,7 @@ msgstr "Nichts ändert sich, bis Sie auf Aktivieren klicken."
msgid "OUT"
msgstr "AUSGANG"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2595
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2599
msgid "One line"
msgstr "Einzeilig"
@@ -561,29 +566,29 @@ msgstr ""
msgid "Outbound"
msgstr "Ausgehend"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2363
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2367
msgid "Palette"
msgstr "Palette"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1702
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2519
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1706
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2523
msgid "Pause"
msgstr "Pause"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1701
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1705
msgid "Paused"
msgstr "Pausiert"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/log.js:364
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/log.js:405
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/table.js:43
msgid "Proto"
msgstr "Protokoll"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2656
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2660
msgid "Protocol — common values"
msgstr "Protokoll — häufige Werte"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2635
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2639
msgid "Quick search"
msgstr "Schnellsuche"
@@ -591,15 +596,15 @@ msgstr "Schnellsuche"
msgid "Remove filter"
msgstr "Filter entfernen"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1702
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1706
msgid "Resume"
msgstr "Fortsetzen"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2353
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2357
msgid "Row tint"
msgstr "Zeileneinfärbung"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2447
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2451
msgid ""
"Row tint shows pass/deny row backgrounds when checked. Choose Classic (green/"
"red, default) or Accessible (teal/orange). Action text stays colored either "
@@ -609,7 +614,7 @@ msgstr ""
"Klassisch (grün/rot, Standard) oder Barrierefrei (teal/orange). Aktionstext "
"bleibt in beiden Fällen farbig."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2503
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2507
msgid ""
"Row tint used a local color fallback because the active LuCI theme did not "
"apply pass/deny backgrounds."
@@ -637,81 +642,81 @@ msgstr "Regel-Labels nicht verfügbar — temporäre Datei fehlgeschlagen"
msgid "SPort"
msgstr "Quell-Port"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/log.js:361
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/log.js:402
msgid "Search"
msgstr "Suche"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1439
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1443
msgid "Server adaptive protection is disabled."
msgstr "Adaptiver Serverschutz ist deaktiviert."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1437
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1441
msgid "Server protection state is unknown."
msgstr "Serverschutz-Status unbekannt."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1444
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1448
msgid "Server shedding — at most %d log lines per poll."
msgstr "Server reduziert — höchstens %d Protokollzeilen pro Abfrage."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1446
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1450
msgid "Server truncated this poll (adaptive cap)."
msgstr "Server hat diese Abfrage gekürzt (adaptives Limit)."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2380
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2384
msgid "Show hostnames"
msgstr "Hostnamen anzeigen"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2351
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2355
msgid "Show pass/deny row background colors"
msgstr "Pass/Deny-Zeilenhintergrundfarben anzeigen"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1548
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2618
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1552
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2622
msgid "Show rows"
msgstr "Zeilen anzeigen"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1505
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1509
msgid "Showing a compact summary of the top of the shown sample."
msgstr "Eine kompakte Zusammenfassung des Anfangs der angezeigten Stichprobe."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2542
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2546
msgid "Simple"
msgstr "Einfach"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/log.js:365
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/log.js:406
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/table.js:44
msgid "Source"
msgstr "Quelle"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2685
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2689
msgid "Source IP contains (! to exclude)"
msgstr "Quell-IP enthält (! zum Ausschließen)"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/log.js:367
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/log.js:408
msgid "Source port"
msgstr "Quell-Port"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2690
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2694
msgid "Source port (! to exclude)"
msgstr "Quell-Port (! zum Ausschließen)"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1507
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1511
msgid "Summary data is unavailable; show rows to inspect the sample."
msgstr ""
"Zusammenfassungsdaten nicht verfügbar; Zeilen anzeigen, um die Stichprobe zu "
"prüfen."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2609
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2613
msgid "Summary mode"
msgstr "Zusammenfassungsmodus"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1509
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1513
msgid "Summary was bounded before all categories could be included."
msgstr ""
"Die Zusammenfassung wurde begrenzt, bevor alle Kategorien aufgenommen werden "
"konnten."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2421
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2425
msgid ""
"Switching tabs pauses polling; returning performs one catch-up poll. "
"Hostnames are off by default because lookups add work."
@@ -720,14 +725,14 @@ msgstr ""
"eine einmalige Aufholabfrage. Hostnamen sind standardmäßig deaktiviert, da "
"Auflösungen zusätzliche Arbeit verursachen."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:892
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:931
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:894
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:935
msgid "The firewall did not reload; saved and live logging may differ."
msgstr ""
"Die Firewall wurde nicht neu geladen; gespeicherte und aktive "
"Protokollierung können voneinander abweichen."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2431
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2435
msgid ""
"The rate shown for WAN logging is the firewall zone log_limit. OpenWrt "
"defaults to 10/minute when no explicit limit is configured; fwlive does not "
@@ -737,7 +742,7 @@ msgstr ""
"Standard ist 10/Minute ohne explizites Limit; fwlive setzt diese Grenze "
"nicht."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2426
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2430
msgid ""
"The router log ring may evict older events before fwlive reads them. fwlive "
"cannot recover evicted entries or change forwarding behavior."
@@ -746,7 +751,7 @@ msgstr ""
"fwlive sie liest. fwlive kann verdrängte Einträge nicht wiederherstellen und "
"ändert das Weiterleitungsverhalten nicht."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2393
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2397
msgid ""
"The table updates automatically when your firewall logs traffic. Use Pause "
"if it moves too fast."
@@ -754,7 +759,7 @@ msgstr ""
"Die Tabelle aktualisiert sich automatisch, wenn Ihre Firewall Verkehr "
"protokolliert. Pause nutzen, wenn es zu schnell wird."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2506
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2510
msgid "Theme tint fallback"
msgstr "Theme-Farb-Fallback"
@@ -773,15 +778,15 @@ msgstr ""
msgid "Time"
msgstr "Zeit"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1514
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1518
msgid "Top drops"
msgstr "Häufigste Drops"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1517
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1521
msgid "Top rules"
msgstr "Häufigste Regeln"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1511
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1515
msgid "Top talkers"
msgstr "Häufigste Absender"
@@ -811,17 +816,17 @@ msgstr ""
msgid "Undo:"
msgstr "Rückgängig:"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2450
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2454
msgid "Use Detail for all columns (flags, length, raw message)."
msgstr ""
"Verwenden Sie „Detail“ für alle Spalten (Flags, Länge, rohe Nachricht)."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2524
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2530
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2528
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2534
msgid "View"
msgstr "Ansicht"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:944
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:948
msgid "WAN drop/reject logging is off."
msgstr "WAN-Drop/Reject-Protokollierung ist aus."
@@ -833,7 +838,7 @@ msgstr ""
"WAN-Drop/Reject-Protokollierung ist an. Blockierter eingehender WAN-Verkehr "
"erscheint hier. Normales LAN-Surfen nicht."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:908
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:910
msgid ""
"WAN drop/reject logging is on. Blocked inbound traffic should appear here as "
"it happens — not normal LAN browsing."
@@ -841,7 +846,7 @@ msgstr ""
"WAN-Drop/Reject-Protokollierung ist an. Blockierter eingehender Verkehr "
"sollte hier in Echtzeit erscheinen — nicht normales LAN-Surfen."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:910
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:912
msgid "WAN logging is already enabled."
msgstr "WAN-Protokollierung ist bereits aktiviert."
@@ -870,25 +875,25 @@ msgstr "WAN-Protokollierung nicht verfügbar: keine WAN-Zone"
msgid "Waiting for firewall events"
msgstr "Warte auf Firewall-Ereignisse"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1701
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2494
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1705
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2498
msgid "Watching"
msgstr "Beobachtet"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2584
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2588
msgid "Wrap"
msgstr "Umbrechen"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/table.js:71
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2640
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2644
msgid "block"
msgstr "blockiert"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1259
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1263
msgid "buffer filling"
msgstr "Puffer füllt sich"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1263
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1267
msgid "buffer full"
msgstr "Puffer voll"
@@ -905,7 +910,7 @@ msgid "does not contain"
msgstr "enthält nicht"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/table.js:72
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2641
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2645
msgid "drop"
msgstr "verworfen"
@@ -913,16 +918,16 @@ msgstr "verworfen"
msgid "is"
msgstr "ist"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1258
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1262
msgid "loading buffer"
msgstr "Lade Puffer"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1633
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1637
msgid "loading…"
msgstr "lädt…"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1487
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1496
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1491
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1500
msgid "none"
msgstr "keine"
@@ -930,94 +935,94 @@ msgstr "keine"
msgid "not"
msgstr "nicht"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1844
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1848
msgid "not AH"
msgstr "nicht AH"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1843
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1847
msgid "not ESP"
msgstr "nicht ESP"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1842
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1846
msgid "not GRE"
msgstr "nicht GRE"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1839
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1843
msgid "not ICMP"
msgstr "nicht ICMP"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1840
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1844
msgid "not ICMPv6"
msgstr "nicht ICMPv6"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1841
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1845
msgid "not IGMP"
msgstr "nicht IGMP"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1845
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1849
msgid "not SCTP"
msgstr "nicht SCTP"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1837
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1841
msgid "not TCP"
msgstr "nicht TCP"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1838
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1842
msgid "not UDP"
msgstr "nicht UDP"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2646
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2650
msgid "not block"
msgstr "nicht blockieren"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2645
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2649
msgid "not drop"
msgstr "nicht verwerfen"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2644
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2648
msgid "not pass"
msgstr "nicht erlauben"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2647
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2651
msgid "not reject"
msgstr "nicht zurückweisen"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2648
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2652
msgid "not unknown"
msgstr "nicht unbekannt"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2663
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2667
msgid "or type…"
msgstr "oder tippen…"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/table.js:70
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2639
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2643
msgid "pass"
msgstr "erlaubt"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/table.js:73
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2642
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2646
msgid "reject"
msgstr "abgelehnt"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1265
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1269
msgid "render paused (high rate)"
msgstr "Rendering pausiert (hohe Rate)"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1274
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1278
msgid "resolve paused (load)"
msgstr "Auflösung pausiert (Last)"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1276
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1280
msgid "scroll frozen — scroll to top to follow live"
msgstr "Scrollen eingefroren — nach oben scrollen für Live-Ansicht"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1273
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1277
msgid "truncated"
msgstr "gekürzt"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/table.js:62
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2643
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2647
msgid "unknown"
msgstr "unbekannt"
+151 -146
View File
@@ -13,15 +13,15 @@ msgstr ""
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1635
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1639
msgid "%d matching · %d/%d stored"
msgstr "%d совпад. · сохранено %d/%d"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1429
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1433
msgid "%s · requested up to %d raw lines · %d firewall messages returned"
msgstr "%s · запрошено до %d сырых строк · возвращено %d сообщений firewall"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1419
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1423
msgid ""
"%s · requested up to %d raw lines · server limited fetch to %d · %d firewall "
"messages returned"
@@ -29,11 +29,11 @@ msgstr ""
"%s · запрошено до %d сырых строк · сервер ограничил выборку до %d · "
"возвращено %d сообщений firewall"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/log.js:502
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/log.js:543
msgid "%s: %s"
msgstr "%s: %s"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1637
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1641
msgid "0 matching · %d/%d stored"
msgstr "0 совпад. · сохранено %d/%d"
@@ -41,16 +41,16 @@ msgstr "0 совпад. · сохранено %d/%d"
msgid "Accessible (teal/orange)"
msgstr "Доступный (бирюзовый/оранжевый)"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/log.js:362
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/log.js:403
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/table.js:37
msgid "Action"
msgstr "Действие"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:945
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:949
msgid "Administrator access is required to disable logging."
msgstr "Для отключения журналирования требуются права администратора."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:912
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:914
msgid "Administrator access is required to enable logging."
msgstr "Для включения журналирования требуются права администратора."
@@ -58,19 +58,19 @@ msgstr "Для включения журналирования требуютс
msgid "Allow/deny rules, LAN logging, or anything else."
msgstr "Правила allow/deny, журналирование LAN и любые другие настройки."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1829
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1833
msgid "Also seen"
msgstr "Также встречаются"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:883
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:924
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:926
msgid "Another change is staged for the firewall; apply or revert it first."
msgstr ""
"В брандмауэре уже есть другое незафиксированное изменение; сначала примените "
"или отмените его."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:901
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:940
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:903
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:944
msgid ""
"Another change overwrote WAN logging after it was saved; check the current "
"state."
@@ -78,16 +78,16 @@ msgstr ""
"Другое изменение перезаписало журналирование WAN после сохранения; проверьте "
"текущее состояние."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2638
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2642
msgid "Any action"
msgstr "Любое действие"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1822
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1826
msgid "Any protocol"
msgstr "Любой протокол"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1410
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1470
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1414
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1474
msgid "Auto"
msgstr "Авто"
@@ -108,7 +108,7 @@ msgstr "Изменения:"
msgid "Classic (green/red)"
msgstr "Классический (зелёный/красный)"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2369
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2373
msgid "Classic uses green/red; Accessible uses teal/orange"
msgstr "Классический — зелёный/красный; доступный — бирюзовый/оранжевый"
@@ -116,7 +116,7 @@ msgstr "Классический — зелёный/красный; доступ
msgid "Clear all"
msgstr "Очистить всё"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2710
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2714
msgid ""
"Click a cell to filter · ≠ on a chip to exclude · Ctrl+click a rule for "
"firewall settings · in Simple view, click a row for the full message"
@@ -125,7 +125,7 @@ msgstr ""
"правилу для настроек МЭ · в Простом виде щёлкните строку для полного "
"сообщения"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2437
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2441
msgid ""
"Click a row (Time or other non-link cells) to see the full log line (Simple "
"view)."
@@ -137,7 +137,7 @@ msgstr ""
msgid "Click a row for the full message"
msgstr "Щёлкните строку для полного сообщения"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2442
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2446
msgid ""
"Click an IP, action, or protocol to filter; use the Protocol menu (or ≠ on a "
"chip) to exclude."
@@ -145,29 +145,29 @@ msgstr ""
"Щёлкните по IP, действию или протоколу для фильтрации; для исключения "
"используйте меню «Протокол» (или ≠ на чипе)."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1823
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1827
msgid "Common"
msgstr "Частые"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1654
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1658
msgid "Connection lost — retrying…"
msgstr "Соединение потеряно — повторная попытка…"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:888
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:929
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:931
msgid "Could not acquire the logging lock."
msgstr "Не удалось получить блокировку журналирования."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:896
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:935
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:898
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:939
msgid "Could not clear the WAN zone log option."
msgstr "Не удалось очистить параметр журнала зоны WAN."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:942
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:946
msgid "Could not disable logging."
msgstr "Не удалось отключить журналирование."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:903
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:905
msgid "Could not enable logging."
msgstr "Не удалось включить журналирование."
@@ -181,21 +181,26 @@ msgstr ""
"Не удалось обновить состояние журналирования; показан последний известный "
"статус."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:898
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:937
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:900
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:941
msgid "Could not save the firewall configuration."
msgstr "Не удалось сохранить конфигурацию межсетевого экрана."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:890
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:892
msgid "Could not snapshot the current logging state."
msgstr "Не удалось сохранить снимок текущего состояния журналирования."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:894
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:890
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:933
msgid "Could not track the logging change safely; logging was not changed."
msgstr "Не удалось безопасно отследить изменение журналирования; журналирование не изменено."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:896
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:937
msgid "Could not write the WAN zone log option."
msgstr "Не удалось записать параметр журнала зоны WAN."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2665
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2669
msgid "Custom protocol (prefix ! to exclude). Overrides the menu when set."
msgstr "Свой протокол (! для исключения). Переопределяет меню, если задан."
@@ -203,32 +208,32 @@ msgstr "Свой протокол (! для исключения). Переоп
msgid "DPort"
msgstr "Порт назн."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1272
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1276
msgid "Degraded — sampling"
msgstr "Упрощённый режим — выборка"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1441
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1445
msgid "Degraded — sampling (slow poll RTT; cadence reduced)."
msgstr "Упрощённый режим — выборка (медленный RTT опроса; интервал увеличен)."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/log.js:366
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/log.js:407
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/table.js:45
msgid "Destination"
msgstr "Назначение"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2695
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2699
msgid "Destination IP contains (! to exclude)"
msgstr "IP назначения содержит (! для исключения)"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/log.js:368
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/log.js:409
msgid "Destination port"
msgstr "Порт назначения"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2700
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2704
msgid "Destination port (! to exclude)"
msgstr "Порт назначения (! для исключения)"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2553
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2557
msgid "Detail"
msgstr "Подробно"
@@ -240,15 +245,15 @@ msgstr "Напр."
msgid "Disabling…"
msgstr "Отключение…"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1268
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1272
msgid "Display limited to %d rows on this device"
msgstr "На этом устройстве отображение ограничено %d строками"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2310
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2314
msgid "Display options"
msgstr "Параметры отображения"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2402
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2406
msgid "Display options on the bar set Limit, row tint, palette, and hostnames."
msgstr ""
"Параметры отображения на панели задают лимит, окраску строк, палитру и имена "
@@ -276,7 +281,7 @@ msgstr ""
msgid "Enable logging"
msgstr "Включить журнал"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2398
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2402
msgid ""
"Enable logging turns on WAN zone drop/reject logging only (same as Network → "
"Firewall). It does not add rules or log normal LAN browsing."
@@ -290,7 +295,7 @@ msgstr ""
msgid "Enabling…"
msgstr "Включение…"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1836
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1840
msgid "Exclude"
msgstr "Исключить"
@@ -298,11 +303,11 @@ msgstr "Исключить"
msgid "Exclude instead"
msgstr "Исключить вместо этого"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2324
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2328
msgid "Fetch budget"
msgstr "Бюджет выборки"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2406
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2410
msgid ""
"Fetch budget controls raw log lines per poll. Auto derives from Limit; "
"Manual selects a bounded maximum."
@@ -322,7 +327,7 @@ msgstr ""
"Фильтр журналов по правилу (подсказка: %s). Ctrl+клик открывает настройки "
"файрвола."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2480
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2484
#: openwrt-feed/luci-app-fwlive/root/usr/share/luci/menu.d/luci-app-fwlive.json:3
msgid "Firewall Live View"
msgstr "Просмотр файрвола в реальном времени"
@@ -335,7 +340,7 @@ msgstr "Флаги"
msgid "Flow"
msgstr "Поток"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2416
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2420
msgid ""
"For a responsive table on a weak device, keep Limit at 250 rows or below. "
"The weak-device cap affects rendered rows; the buffer can still retain more."
@@ -352,15 +357,15 @@ msgstr "Транзитный"
msgid "Grant access to firewall live log view"
msgstr "Предоставить доступ к живому просмотру журнала межсетевого экрана"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2389
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2393
msgid "Help"
msgstr "Справка"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1548
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1552
msgid "Hide rows"
msgstr "Скрыть строки"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1603
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1607
msgid ""
"High event rate — table refresh is throttled to protect the browser. The "
"buffer still updates; refresh will resume automatically."
@@ -368,11 +373,11 @@ msgstr ""
"Высокая частота событий — обновление таблицы ограничено для защиты браузера. "
"Буфер продолжает обновляться; отображение возобновится автоматически."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1448
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1452
msgid "Hostname resolve paused while the router is under load."
msgstr "Разрешение имён приостановлено из-за нагрузки на маршрутизатор."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1827
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1831
msgid "ICMPv6"
msgstr "ICMPv6"
@@ -380,7 +385,7 @@ msgstr "ICMPv6"
msgid "IN"
msgstr "ВХОД"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2453
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2457
msgid ""
"If Row tint looks missing, the active LuCI theme may omit success/error or "
"info/warn CSS variables; fwlive falls back to local colors (air-gapped, no "
@@ -414,16 +419,16 @@ msgstr ""
"Установите kmod-nf-log и kmod-nf-log6 командой для вашей версии OpenWrt, "
"затем перезагрузите файрвол."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1653
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1657
msgid "Installation is incomplete. Reinstall luci-app-fwlive."
msgstr "Установка не завершена. Переустановите luci-app-fwlive."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/log.js:363
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/log.js:404
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/table.js:39
msgid "Interface"
msgstr "Интерфейс"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2680
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2684
msgid "Interface (prefix ! to exclude)"
msgstr "Интерфейс (! в начале для исключения)"
@@ -440,7 +445,7 @@ msgstr "Отсутствуют модули журналирования ядр
msgid "Len"
msgstr "Длина"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2313
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2317
msgid "Limit"
msgstr "Лимит"
@@ -454,16 +459,16 @@ msgstr ""
msgid "Logging is off on this router"
msgstr "Журналирование на этом роутере отключено"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1410
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1471
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1414
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1475
msgid "Manual"
msgstr "Вручную"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2342
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2346
msgid "Manual still uses server protection and poll cadence"
msgstr "«Вручную» по-прежнему использует защиту сервера и частоту опроса"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2411
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2415
msgid ""
"Manual still uses server protection and poll cadence; it changes the fetch "
"budget, not the polling interval."
@@ -475,18 +480,18 @@ msgstr ""
msgid "Manual test (System → Terminal):"
msgstr "Ручной тест (Система → Терминал):"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2336
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2340
msgid "Maximum raw lines"
msgstr "Максимум сырых строк"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/table.js:51
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/table.js:344
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2565
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2572
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2569
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2576
msgid "Message"
msgstr "Сообщение"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2675
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2679
msgid "More filters"
msgstr "Больше фильтров"
@@ -506,7 +511,7 @@ msgid "No WAN zone found"
msgstr "WAN-зона не найдена"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:886
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:927
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:929
msgid "No WAN zone found; cannot toggle logging without one."
msgstr "WAN-зона не найдена; без неё нельзя переключить журналирование."
@@ -522,7 +527,7 @@ msgstr "Ничего не изменится, пока вы не нажмёте
msgid "OUT"
msgstr "ВЫХОД"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2595
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2599
msgid "One line"
msgstr "Одна строка"
@@ -552,29 +557,29 @@ msgstr ""
msgid "Outbound"
msgstr "Исходящий"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2363
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2367
msgid "Palette"
msgstr "Палитра"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1702
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2519
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1706
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2523
msgid "Pause"
msgstr "Пауза"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1701
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1705
msgid "Paused"
msgstr "Пауза"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/log.js:364
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/log.js:405
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/table.js:43
msgid "Proto"
msgstr "Протокол"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2656
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2660
msgid "Protocol — common values"
msgstr "Протокол — частые значения"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2635
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2639
msgid "Quick search"
msgstr "Быстрый поиск"
@@ -582,15 +587,15 @@ msgstr "Быстрый поиск"
msgid "Remove filter"
msgstr "Удалить фильтр"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1702
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1706
msgid "Resume"
msgstr "Продолжить"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2353
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2357
msgid "Row tint"
msgstr "Цвет строк"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2447
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2451
msgid ""
"Row tint shows pass/deny row backgrounds when checked. Choose Classic (green/"
"red, default) or Accessible (teal/orange). Action text stays colored either "
@@ -600,7 +605,7 @@ msgstr ""
"классический (зелёный/красный, по умолчанию) или доступный (бирюзовый/"
"оранжевый). Цвет текста действия сохраняется в любом случае."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2503
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2507
msgid ""
"Row tint used a local color fallback because the active LuCI theme did not "
"apply pass/deny backgrounds."
@@ -628,77 +633,77 @@ msgstr "Метки правил недоступны — ошибка време
msgid "SPort"
msgstr "Порт ист."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/log.js:361
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/log.js:402
msgid "Search"
msgstr "Поиск"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1439
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1443
msgid "Server adaptive protection is disabled."
msgstr "Адаптивная защита сервера отключена."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1437
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1441
msgid "Server protection state is unknown."
msgstr "Состояние защиты сервера неизвестно."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1444
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1448
msgid "Server shedding — at most %d log lines per poll."
msgstr "Сервер ограничивает выдачу — не более %d строк журнала за опрос."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1446
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1450
msgid "Server truncated this poll (adaptive cap)."
msgstr "Сервер сократил этот опрос (адаптивный предел)."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2380
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2384
msgid "Show hostnames"
msgstr "Показывать имена узлов"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2351
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2355
msgid "Show pass/deny row background colors"
msgstr "Показывать цвета фона строк pass/deny"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1548
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2618
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1552
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2622
msgid "Show rows"
msgstr "Показать строки"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1505
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1509
msgid "Showing a compact summary of the top of the shown sample."
msgstr "Показана краткая сводка верхней части отображаемой выборки."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2542
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2546
msgid "Simple"
msgstr "Простой"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/log.js:365
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/log.js:406
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/table.js:44
msgid "Source"
msgstr "Источник"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2685
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2689
msgid "Source IP contains (! to exclude)"
msgstr "IP источника содержит (! для исключения)"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/log.js:367
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/log.js:408
msgid "Source port"
msgstr "Порт источника"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2690
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2694
msgid "Source port (! to exclude)"
msgstr "Порт источника (! для исключения)"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1507
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1511
msgid "Summary data is unavailable; show rows to inspect the sample."
msgstr "Данные сводки недоступны; покажите строки для проверки выборки."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2609
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2613
msgid "Summary mode"
msgstr "Режим сводки"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1509
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1513
msgid "Summary was bounded before all categories could be included."
msgstr "Сводка ограничена до включения всех категорий."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2421
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2425
msgid ""
"Switching tabs pauses polling; returning performs one catch-up poll. "
"Hostnames are off by default because lookups add work."
@@ -707,14 +712,14 @@ msgstr ""
"один догоняющий опрос. Имена хостов по умолчанию отключены, поскольку их "
"разрешение создаёт дополнительную нагрузку."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:892
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:931
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:894
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:935
msgid "The firewall did not reload; saved and live logging may differ."
msgstr ""
"Брандмауэр не перезагрузился; сохранённые и активные настройки "
"журналирования могут различаться."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2431
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2435
msgid ""
"The rate shown for WAN logging is the firewall zone log_limit. OpenWrt "
"defaults to 10/minute when no explicit limit is configured; fwlive does not "
@@ -723,7 +728,7 @@ msgstr ""
"Показанная частота WAN-журнала — log_limit зоны. По умолчанию OpenWrt: 10/"
"мин без явного лимита; fwlive сам лимит не задаёт."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2426
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2430
msgid ""
"The router log ring may evict older events before fwlive reads them. fwlive "
"cannot recover evicted entries or change forwarding behavior."
@@ -732,7 +737,7 @@ msgstr ""
"fwlive их прочитает. fwlive не может восстановить вытесненные записи и не "
"изменяет пересылку."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2393
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2397
msgid ""
"The table updates automatically when your firewall logs traffic. Use Pause "
"if it moves too fast."
@@ -740,7 +745,7 @@ msgstr ""
"Таблица обновляется автоматически, когда файрвол пишет в журнал. Используйте "
"Паузу, если слишком быстро."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2506
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2510
msgid "Theme tint fallback"
msgstr "Запасной цвет темы"
@@ -759,15 +764,15 @@ msgstr ""
msgid "Time"
msgstr "Время"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1514
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1518
msgid "Top drops"
msgstr "Частые сбросы"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1517
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1521
msgid "Top rules"
msgstr "Частые правила"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1511
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1515
msgid "Top talkers"
msgstr "Частые источники"
@@ -796,17 +801,17 @@ msgstr ""
msgid "Undo:"
msgstr "Отмена:"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2450
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2454
msgid "Use Detail for all columns (flags, length, raw message)."
msgstr ""
"Используйте «Подробно» для всех столбцов (флаги, длина, исходное сообщение)."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2524
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2530
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2528
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2534
msgid "View"
msgstr "Вид"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:944
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:948
msgid "WAN drop/reject logging is off."
msgstr "Журналирование drop/reject для WAN отключено."
@@ -818,7 +823,7 @@ msgstr ""
"Журналирование drop/reject для WAN включено. Заблокированный входящий WAN-"
"трафик будет отображаться здесь. Обычный LAN-трафик — нет."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:908
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:910
msgid ""
"WAN drop/reject logging is on. Blocked inbound traffic should appear here as "
"it happens — not normal LAN browsing."
@@ -827,7 +832,7 @@ msgstr ""
"должен появляться здесь по мере поступления — обычный LAN-трафик не "
"отображается."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:910
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:912
msgid "WAN logging is already enabled."
msgstr "WAN-журналирование уже включено."
@@ -856,25 +861,25 @@ msgstr "WAN-журналирование недоступно: нет зоны W
msgid "Waiting for firewall events"
msgstr "Ожидание событий межсетевого экрана"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1701
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2494
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1705
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2498
msgid "Watching"
msgstr "Наблюдение"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2584
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2588
msgid "Wrap"
msgstr "Перенос"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/table.js:71
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2640
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2644
msgid "block"
msgstr "заблокирован"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1259
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1263
msgid "buffer filling"
msgstr "буфер заполняется"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1263
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1267
msgid "buffer full"
msgstr "буфер заполнен"
@@ -891,7 +896,7 @@ msgid "does not contain"
msgstr "не содержит"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/table.js:72
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2641
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2645
msgid "drop"
msgstr "отброшен"
@@ -899,16 +904,16 @@ msgstr "отброшен"
msgid "is"
msgstr "есть"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1258
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1262
msgid "loading buffer"
msgstr "загрузка буфера"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1633
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1637
msgid "loading…"
msgstr "загрузка…"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1487
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1496
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1491
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1500
msgid "none"
msgstr "нет"
@@ -916,95 +921,95 @@ msgstr "нет"
msgid "not"
msgstr "не"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1844
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1848
msgid "not AH"
msgstr "не AH"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1843
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1847
msgid "not ESP"
msgstr "не ESP"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1842
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1846
msgid "not GRE"
msgstr "не GRE"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1839
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1843
msgid "not ICMP"
msgstr "не ICMP"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1840
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1844
msgid "not ICMPv6"
msgstr "не ICMPv6"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1841
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1845
msgid "not IGMP"
msgstr "не IGMP"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1845
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1849
msgid "not SCTP"
msgstr "не SCTP"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1837
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1841
msgid "not TCP"
msgstr "не TCP"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1838
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1842
msgid "not UDP"
msgstr "не UDP"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2646
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2650
msgid "not block"
msgstr "не блокировать"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2645
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2649
msgid "not drop"
msgstr "не отбрасывать"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2644
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2648
msgid "not pass"
msgstr "не пропускать"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2647
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2651
msgid "not reject"
msgstr "не отклонять"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2648
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2652
msgid "not unknown"
msgstr "не неизвестно"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2663
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2667
msgid "or type…"
msgstr "или введите…"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/table.js:70
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2639
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2643
msgid "pass"
msgstr "разрешён"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/table.js:73
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2642
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2646
msgid "reject"
msgstr "отклонён"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1265
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1269
msgid "render paused (high rate)"
msgstr "рендеринг приостановлен (высокая частота)"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1274
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1278
msgid "resolve paused (load)"
msgstr "Разрешение приостановлено (нагрузка)"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1276
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1280
msgid "scroll frozen — scroll to top to follow live"
msgstr ""
"прокрутка заморожена — прокрутите вверх, чтобы следить в реальном времени"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1273
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1277
msgid "truncated"
msgstr "обрезано"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/table.js:62
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2643
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2647
msgid "unknown"
msgstr "неизвестно"
+142 -137
View File
@@ -1,15 +1,15 @@
msgid ""
msgstr "Content-Type: text/plain; charset=UTF-8"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1635
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1639
msgid "%d matching · %d/%d stored"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1429
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1433
msgid "%s · requested up to %d raw lines · %d firewall messages returned"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1419
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1423
msgid ""
"%s · requested up to %d raw lines · server limited fetch to %d · %d firewall "
"messages returned"
@@ -19,7 +19,7 @@ msgstr ""
msgid "%s: %s"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1637
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1641
msgid "0 matching · %d/%d stored"
msgstr ""
@@ -32,11 +32,11 @@ msgstr ""
msgid "Action"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:945
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:949
msgid "Administrator access is required to disable logging."
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:912
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:914
msgid "Administrator access is required to enable logging."
msgstr ""
@@ -44,32 +44,32 @@ msgstr ""
msgid "Allow/deny rules, LAN logging, or anything else."
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1829
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1833
msgid "Also seen"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:883
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:924
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:926
msgid "Another change is staged for the firewall; apply or revert it first."
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:901
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:940
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:903
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:944
msgid ""
"Another change overwrote WAN logging after it was saved; check the current "
"state."
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2638
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2642
msgid "Any action"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1822
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1826
msgid "Any protocol"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1410
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1470
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1414
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1474
msgid "Auto"
msgstr ""
@@ -89,7 +89,7 @@ msgstr ""
msgid "Classic (green/red)"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2369
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2373
msgid "Classic uses green/red; Accessible uses teal/orange"
msgstr ""
@@ -97,13 +97,13 @@ msgstr ""
msgid "Clear all"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2710
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2714
msgid ""
"Click a cell to filter · ≠ on a chip to exclude · Ctrl+click a rule for "
"firewall settings · in Simple view, click a row for the full message"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2437
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2441
msgid ""
"Click a row (Time or other non-link cells) to see the full log line (Simple "
"view)."
@@ -113,35 +113,35 @@ msgstr ""
msgid "Click a row for the full message"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2442
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2446
msgid ""
"Click an IP, action, or protocol to filter; use the Protocol menu (or ≠ on a "
"chip) to exclude."
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1823
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1827
msgid "Common"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1654
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1658
msgid "Connection lost — retrying…"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:888
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:929
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:931
msgid "Could not acquire the logging lock."
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:896
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:935
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:898
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:939
msgid "Could not clear the WAN zone log option."
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:942
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:946
msgid "Could not disable logging."
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:903
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:905
msgid "Could not enable logging."
msgstr ""
@@ -153,21 +153,26 @@ msgstr ""
msgid "Could not refresh logging status; showing the last known state."
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:898
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:937
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:900
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:941
msgid "Could not save the firewall configuration."
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:890
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:892
msgid "Could not snapshot the current logging state."
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:894
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:890
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:933
msgid "Could not track the logging change safely; logging was not changed."
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:896
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:937
msgid "Could not write the WAN zone log option."
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2665
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2669
msgid "Custom protocol (prefix ! to exclude). Overrides the menu when set."
msgstr ""
@@ -175,11 +180,11 @@ msgstr ""
msgid "DPort"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1272
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1276
msgid "Degraded — sampling"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1441
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1445
msgid "Degraded — sampling (slow poll RTT; cadence reduced)."
msgstr ""
@@ -188,7 +193,7 @@ msgstr ""
msgid "Destination"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2695
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2699
msgid "Destination IP contains (! to exclude)"
msgstr ""
@@ -196,11 +201,11 @@ msgstr ""
msgid "Destination port"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2700
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2704
msgid "Destination port (! to exclude)"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2553
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2557
msgid "Detail"
msgstr ""
@@ -212,15 +217,15 @@ msgstr ""
msgid "Disabling…"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1268
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1272
msgid "Display limited to %d rows on this device"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2310
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2314
msgid "Display options"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2402
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2406
msgid "Display options on the bar set Limit, row tint, palette, and hostnames."
msgstr ""
@@ -244,7 +249,7 @@ msgstr ""
msgid "Enable logging"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2398
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2402
msgid ""
"Enable logging turns on WAN zone drop/reject logging only (same as Network → "
"Firewall). It does not add rules or log normal LAN browsing."
@@ -255,7 +260,7 @@ msgstr ""
msgid "Enabling…"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1836
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1840
msgid "Exclude"
msgstr ""
@@ -263,11 +268,11 @@ msgstr ""
msgid "Exclude instead"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2324
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2328
msgid "Fetch budget"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2406
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2410
msgid ""
"Fetch budget controls raw log lines per poll. Auto derives from Limit; "
"Manual selects a bounded maximum."
@@ -283,7 +288,7 @@ msgstr ""
msgid "Filter logs by rule (hint: %s). Ctrl+click to open firewall settings."
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2480
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2484
#: openwrt-feed/luci-app-fwlive/root/usr/share/luci/menu.d/luci-app-fwlive.json:3
msgid "Firewall Live View"
msgstr ""
@@ -296,7 +301,7 @@ msgstr ""
msgid "Flow"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2416
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2420
msgid ""
"For a responsive table on a weak device, keep Limit at 250 rows or below. "
"The weak-device cap affects rendered rows; the buffer can still retain more."
@@ -310,25 +315,25 @@ msgstr ""
msgid "Grant access to firewall live log view"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2389
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2393
msgid "Help"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1548
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1552
msgid "Hide rows"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1603
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1607
msgid ""
"High event rate — table refresh is throttled to protect the browser. The "
"buffer still updates; refresh will resume automatically."
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1448
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1452
msgid "Hostname resolve paused while the router is under load."
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1827
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1831
msgid "ICMPv6"
msgstr ""
@@ -336,7 +341,7 @@ msgstr ""
msgid "IN"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2453
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2457
msgid ""
"If Row tint looks missing, the active LuCI theme may omit success/error or "
"info/warn CSS variables; fwlive falls back to local colors (air-gapped, no "
@@ -363,7 +368,7 @@ msgid ""
"release, then reload the firewall."
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1653
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1657
msgid "Installation is incomplete. Reinstall luci-app-fwlive."
msgstr ""
@@ -372,7 +377,7 @@ msgstr ""
msgid "Interface"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2680
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2684
msgid "Interface (prefix ! to exclude)"
msgstr ""
@@ -389,7 +394,7 @@ msgstr ""
msgid "Len"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2313
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2317
msgid "Limit"
msgstr ""
@@ -401,16 +406,16 @@ msgstr ""
msgid "Logging is off on this router"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1410
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1471
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1414
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1475
msgid "Manual"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2342
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2346
msgid "Manual still uses server protection and poll cadence"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2411
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2415
msgid ""
"Manual still uses server protection and poll cadence; it changes the fetch "
"budget, not the polling interval."
@@ -420,18 +425,18 @@ msgstr ""
msgid "Manual test (System → Terminal):"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2336
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2340
msgid "Maximum raw lines"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/table.js:51
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/table.js:344
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2565
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2572
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2569
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2576
msgid "Message"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2675
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2679
msgid "More filters"
msgstr ""
@@ -449,7 +454,7 @@ msgid "No WAN zone found"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:886
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:927
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:929
msgid "No WAN zone found; cannot toggle logging without one."
msgstr ""
@@ -465,7 +470,7 @@ msgstr ""
msgid "OUT"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2595
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2599
msgid "One line"
msgstr ""
@@ -492,16 +497,16 @@ msgstr ""
msgid "Outbound"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2363
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2367
msgid "Palette"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1702
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2519
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1706
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2523
msgid "Pause"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1701
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1705
msgid "Paused"
msgstr ""
@@ -510,11 +515,11 @@ msgstr ""
msgid "Proto"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2656
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2660
msgid "Protocol — common values"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2635
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2639
msgid "Quick search"
msgstr ""
@@ -522,22 +527,22 @@ msgstr ""
msgid "Remove filter"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1702
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1706
msgid "Resume"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2353
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2357
msgid "Row tint"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2447
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2451
msgid ""
"Row tint shows pass/deny row backgrounds when checked. Choose Classic (green/"
"red, default) or Accessible (teal/orange). Action text stays colored either "
"way."
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2503
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2507
msgid ""
"Row tint used a local color fallback because the active LuCI theme did not "
"apply pass/deny backgrounds."
@@ -567,40 +572,40 @@ msgstr ""
msgid "Search"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1439
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1443
msgid "Server adaptive protection is disabled."
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1437
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1441
msgid "Server protection state is unknown."
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1444
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1448
msgid "Server shedding — at most %d log lines per poll."
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1446
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1450
msgid "Server truncated this poll (adaptive cap)."
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2380
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2384
msgid "Show hostnames"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2351
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2355
msgid "Show pass/deny row background colors"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1548
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2618
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1552
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2622
msgid "Show rows"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1505
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1509
msgid "Showing a compact summary of the top of the shown sample."
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2542
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2546
msgid "Simple"
msgstr ""
@@ -609,7 +614,7 @@ msgstr ""
msgid "Source"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2685
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2689
msgid "Source IP contains (! to exclude)"
msgstr ""
@@ -617,53 +622,53 @@ msgstr ""
msgid "Source port"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2690
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2694
msgid "Source port (! to exclude)"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1507
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1511
msgid "Summary data is unavailable; show rows to inspect the sample."
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2609
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2613
msgid "Summary mode"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1509
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1513
msgid "Summary was bounded before all categories could be included."
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2421
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2425
msgid ""
"Switching tabs pauses polling; returning performs one catch-up poll. "
"Hostnames are off by default because lookups add work."
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:892
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:931
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:894
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:935
msgid "The firewall did not reload; saved and live logging may differ."
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2431
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2435
msgid ""
"The rate shown for WAN logging is the firewall zone log_limit. OpenWrt "
"defaults to 10/minute when no explicit limit is configured; fwlive does not "
"impose this cap."
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2426
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2430
msgid ""
"The router log ring may evict older events before fwlive reads them. fwlive "
"cannot recover evicted entries or change forwarding behavior."
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2393
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2397
msgid ""
"The table updates automatically when your firewall logs traffic. Use Pause "
"if it moves too fast."
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2506
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2510
msgid "Theme tint fallback"
msgstr ""
@@ -680,15 +685,15 @@ msgstr ""
msgid "Time"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1514
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1518
msgid "Top drops"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1517
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1521
msgid "Top rules"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1511
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1515
msgid "Top talkers"
msgstr ""
@@ -711,16 +716,16 @@ msgstr ""
msgid "Undo:"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2450
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2454
msgid "Use Detail for all columns (flags, length, raw message)."
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2524
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2530
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2528
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2534
msgid "View"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:944
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:948
msgid "WAN drop/reject logging is off."
msgstr ""
@@ -730,13 +735,13 @@ msgid ""
"here. Normal LAN browsing will not."
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:908
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:910
msgid ""
"WAN drop/reject logging is on. Blocked inbound traffic should appear here as "
"it happens — not normal LAN browsing."
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:910
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:912
msgid "WAN logging is already enabled."
msgstr ""
@@ -765,25 +770,25 @@ msgstr ""
msgid "Waiting for firewall events"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1701
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2494
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1705
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2498
msgid "Watching"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2584
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2588
msgid "Wrap"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/table.js:71
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2640
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2644
msgid "block"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1259
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1263
msgid "buffer filling"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1263
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1267
msgid "buffer full"
msgstr ""
@@ -800,7 +805,7 @@ msgid "does not contain"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/table.js:72
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2641
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2645
msgid "drop"
msgstr ""
@@ -808,16 +813,16 @@ msgstr ""
msgid "is"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1258
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1262
msgid "loading buffer"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1633
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1637
msgid "loading…"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1487
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1496
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1491
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1500
msgid "none"
msgstr ""
@@ -825,94 +830,94 @@ msgstr ""
msgid "not"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1844
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1848
msgid "not AH"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1843
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1847
msgid "not ESP"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1842
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1846
msgid "not GRE"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1839
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1843
msgid "not ICMP"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1840
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1844
msgid "not ICMPv6"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1841
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1845
msgid "not IGMP"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1845
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1849
msgid "not SCTP"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1837
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1841
msgid "not TCP"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1838
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1842
msgid "not UDP"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2646
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2650
msgid "not block"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2645
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2649
msgid "not drop"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2644
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2648
msgid "not pass"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2647
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2651
msgid "not reject"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2648
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2652
msgid "not unknown"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2663
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2667
msgid "or type…"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/table.js:70
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2639
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2643
msgid "pass"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/table.js:73
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2642
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2646
msgid "reject"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1265
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1269
msgid "render paused (high rate)"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1274
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1278
msgid "resolve paused (load)"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1276
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1280
msgid "scroll frozen — scroll to top to follow live"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1273
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1277
msgid "truncated"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/table.js:62
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2643
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2647
msgid "unknown"
msgstr ""
+153 -148
View File
@@ -12,26 +12,26 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1635
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1639
msgid "%d matching · %d/%d stored"
msgstr "%d 条匹配 · 已存 %d/%d"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1429
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1433
msgid "%s · requested up to %d raw lines · %d firewall messages returned"
msgstr "%s · 请求最多 %d 行原始日志 · 返回 %d 条防火墙消息"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1419
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1423
msgid ""
"%s · requested up to %d raw lines · server limited fetch to %d · %d firewall "
"messages returned"
msgstr ""
"%s · 请求最多 %d 行原始日志 · 服务器将抓取限制为 %d · 返回 %d 条防火墙消息"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/log.js:502
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/log.js:543
msgid "%s: %s"
msgstr "%s: %s"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1637
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1641
msgid "0 matching · %d/%d stored"
msgstr "0 条匹配 · 已存 %d/%d"
@@ -39,16 +39,16 @@ msgstr "0 条匹配 · 已存 %d/%d"
msgid "Accessible (teal/orange)"
msgstr "无障碍(青绿/橙色)"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/log.js:362
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/log.js:403
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/table.js:37
msgid "Action"
msgstr "动作"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:945
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:949
msgid "Administrator access is required to disable logging."
msgstr "需要管理员权限才能禁用日志记录。"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:912
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:914
msgid "Administrator access is required to enable logging."
msgstr "需要管理员权限才能启用日志记录。"
@@ -56,32 +56,32 @@ msgstr "需要管理员权限才能启用日志记录。"
msgid "Allow/deny rules, LAN logging, or anything else."
msgstr "允许/拒绝规则、LAN 日志记录或任何其他设置。"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1829
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1833
msgid "Also seen"
msgstr "其他常见"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:883
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:924
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:926
msgid "Another change is staged for the firewall; apply or revert it first."
msgstr "防火墙另有未提交的更改;请先应用或还原。"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:901
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:940
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:903
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:944
msgid ""
"Another change overwrote WAN logging after it was saved; check the current "
"state."
msgstr "保存后另有更改覆盖了 WAN 日志设置;请检查当前状态。"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2638
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2642
msgid "Any action"
msgstr "所有动作"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1822
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1826
msgid "Any protocol"
msgstr "任意协议"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1410
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1470
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1414
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1474
msgid "Auto"
msgstr "自动"
@@ -101,7 +101,7 @@ msgstr "变更:"
msgid "Classic (green/red)"
msgstr "经典(绿/红)"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2369
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2373
msgid "Classic uses green/red; Accessible uses teal/orange"
msgstr "经典使用绿/红;无障碍使用青绿/橙色"
@@ -109,7 +109,7 @@ msgstr "经典使用绿/红;无障碍使用青绿/橙色"
msgid "Clear all"
msgstr "全部清除"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2710
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2714
msgid ""
"Click a cell to filter · ≠ on a chip to exclude · Ctrl+click a rule for "
"firewall settings · in Simple view, click a row for the full message"
@@ -117,7 +117,7 @@ msgstr ""
"点击单元格筛选 · 芯片上的 ≠ 排除 · Ctrl+点击规则打开防火墙设置 · 在简单视图中"
"点击行查看完整消息"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2437
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2441
msgid ""
"Click a row (Time or other non-link cells) to see the full log line (Simple "
"view)."
@@ -127,36 +127,36 @@ msgstr "点击一行(时间或其他非链接单元格)可查看完整日志
msgid "Click a row for the full message"
msgstr "点击行可查看完整消息"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2442
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2446
msgid ""
"Click an IP, action, or protocol to filter; use the Protocol menu (or ≠ on a "
"chip) to exclude."
msgstr ""
"点击 IP、动作或协议进行筛选;使用协议菜单(或筛选标签上的 ≠)进行排除。"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1823
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1827
msgid "Common"
msgstr "常用"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1654
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1658
msgid "Connection lost — retrying…"
msgstr "连接已断开 — 正在重试…"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:888
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:929
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:931
msgid "Could not acquire the logging lock."
msgstr "无法获取日志记录锁。"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:896
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:935
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:898
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:939
msgid "Could not clear the WAN zone log option."
msgstr "无法清除 WAN 区域日志选项。"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:942
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:946
msgid "Could not disable logging."
msgstr "无法禁用日志记录。"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:903
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:905
msgid "Could not enable logging."
msgstr "无法启用日志记录。"
@@ -168,21 +168,26 @@ msgstr "无法加载日志状态。"
msgid "Could not refresh logging status; showing the last known state."
msgstr "无法刷新日志状态;显示上次已知状态。"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:898
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:937
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:900
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:941
msgid "Could not save the firewall configuration."
msgstr "无法保存防火墙配置。"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:890
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:892
msgid "Could not snapshot the current logging state."
msgstr "无法快照当前日志记录状态。"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:894
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:890
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:933
msgid "Could not track the logging change safely; logging was not changed."
msgstr "无法安全跟踪日志记录的更改;日志记录未更改。"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:896
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:937
msgid "Could not write the WAN zone log option."
msgstr "无法写入 WAN 区域日志选项。"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2665
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2669
msgid "Custom protocol (prefix ! to exclude). Overrides the menu when set."
msgstr "自定义协议(! 排除)。有内容时覆盖菜单。"
@@ -190,32 +195,32 @@ msgstr "自定义协议(! 排除)。有内容时覆盖菜单。"
msgid "DPort"
msgstr "目的端口"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1272
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1276
msgid "Degraded — sampling"
msgstr "降级 — 抽样"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1441
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1445
msgid "Degraded — sampling (slow poll RTT; cadence reduced)."
msgstr "降级 — 抽样(轮询 RTT 较慢;已降低频率)。"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/log.js:366
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/log.js:407
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/table.js:45
msgid "Destination"
msgstr "目的地址"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2695
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2699
msgid "Destination IP contains (! to exclude)"
msgstr "目的 IP 包含(! 表示排除)"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/log.js:368
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/log.js:409
msgid "Destination port"
msgstr "目标端口"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2700
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2704
msgid "Destination port (! to exclude)"
msgstr "目的端口(! 表示排除)"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2553
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2557
msgid "Detail"
msgstr "详细"
@@ -227,15 +232,15 @@ msgstr "方向"
msgid "Disabling…"
msgstr "正在禁用…"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1268
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1272
msgid "Display limited to %d rows on this device"
msgstr "此设备上的显示限制为 %d 行"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2310
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2314
msgid "Display options"
msgstr "显示选项"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2402
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2406
msgid "Display options on the bar set Limit, row tint, palette, and hostnames."
msgstr "栏上的显示选项可设置行数限制、行着色、调色板和主机名。"
@@ -259,7 +264,7 @@ msgstr "启用 WAN 区域的丢弃/拒绝日志记录(同 网络 → 防火墙
msgid "Enable logging"
msgstr "启用日志"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2398
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2402
msgid ""
"Enable logging turns on WAN zone drop/reject logging only (same as Network → "
"Firewall). It does not add rules or log normal LAN browsing."
@@ -272,7 +277,7 @@ msgstr ""
msgid "Enabling…"
msgstr "正在启用…"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1836
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1840
msgid "Exclude"
msgstr "排除"
@@ -280,11 +285,11 @@ msgstr "排除"
msgid "Exclude instead"
msgstr "改为排除"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2324
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2328
msgid "Fetch budget"
msgstr "抓取配额"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2406
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2410
msgid ""
"Fetch budget controls raw log lines per poll. Auto derives from Limit; "
"Manual selects a bounded maximum."
@@ -302,7 +307,7 @@ msgstr "按 %s 筛选"
msgid "Filter logs by rule (hint: %s). Ctrl+click to open firewall settings."
msgstr "按规则筛选日志(提示:%s)。Ctrl+点击可打开防火墙设置。"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2480
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2484
#: openwrt-feed/luci-app-fwlive/root/usr/share/luci/menu.d/luci-app-fwlive.json:3
msgid "Firewall Live View"
msgstr "防火墙实时视图"
@@ -315,7 +320,7 @@ msgstr "标志"
msgid "Flow"
msgstr "流向"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2416
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2420
msgid ""
"For a responsive table on a weak device, keep Limit at 250 rows or below. "
"The weak-device cap affects rendered rows; the buffer can still retain more."
@@ -331,26 +336,26 @@ msgstr "已转发"
msgid "Grant access to firewall live log view"
msgstr "授予防火墙实时日志视图的访问权限"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2389
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2393
msgid "Help"
msgstr "帮助"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1548
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1552
msgid "Hide rows"
msgstr "隐藏行"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1603
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1607
msgid ""
"High event rate — table refresh is throttled to protect the browser. The "
"buffer still updates; refresh will resume automatically."
msgstr ""
"事件速率过高 — 已限制表格刷新以保护浏览器。缓冲区仍在更新;刷新将自动恢复。"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1448
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1452
msgid "Hostname resolve paused while the router is under load."
msgstr "路由器负载较高,已暂停主机名解析。"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1827
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1831
msgid "ICMPv6"
msgstr "ICMPv6"
@@ -358,7 +363,7 @@ msgstr "ICMPv6"
msgid "IN"
msgstr "IN"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2453
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2457
msgid ""
"If Row tint looks missing, the active LuCI theme may omit success/error or "
"info/warn CSS variables; fwlive falls back to local colors (air-gapped, no "
@@ -388,19 +393,19 @@ msgid ""
"Install kmod-nf-log and kmod-nf-log6 with the command for your OpenWrt "
"release, then reload the firewall."
msgstr ""
"请使用适用于您 OpenWrt 版本的命令安装 kmod-nf-log 和 kmod-nf-log6,"
"然后重新加载防火墙。"
"请使用适用于您 OpenWrt 版本的命令安装 kmod-nf-log 和 kmod-nf-log6,然后重新加"
"载防火墙。"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1653
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1657
msgid "Installation is incomplete. Reinstall luci-app-fwlive."
msgstr "安装不完整。请重新安装 luci-app-fwlive。"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/log.js:363
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/log.js:404
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/table.js:39
msgid "Interface"
msgstr "接口"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2680
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2684
msgid "Interface (prefix ! to exclude)"
msgstr "接口(前缀 ! 表示排除)"
@@ -417,7 +422,7 @@ msgstr "缺少内核日志模块"
msgid "Len"
msgstr "长度"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2313
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2317
msgid "Limit"
msgstr "行数限制"
@@ -429,16 +434,16 @@ msgstr "实时视图可能不完整 — 已注册旧版 iptables 表"
msgid "Logging is off on this router"
msgstr "此路由器的日志记录已关闭"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1410
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1471
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1414
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1475
msgid "Manual"
msgstr "手动"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2342
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2346
msgid "Manual still uses server protection and poll cadence"
msgstr "手动模式仍受服务器保护和轮询节奏约束"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2411
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2415
msgid ""
"Manual still uses server protection and poll cadence; it changes the fetch "
"budget, not the polling interval."
@@ -449,18 +454,18 @@ msgstr ""
msgid "Manual test (System → Terminal):"
msgstr "手动测试(系统 → 终端):"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2336
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2340
msgid "Maximum raw lines"
msgstr "最大原始行数"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/table.js:51
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/table.js:344
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2565
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2572
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2569
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2576
msgid "Message"
msgstr "消息"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2675
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2679
msgid "More filters"
msgstr "更多筛选"
@@ -479,7 +484,7 @@ msgid "No WAN zone found"
msgstr "未找到 WAN 区域"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:886
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:927
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:929
msgid "No WAN zone found; cannot toggle logging without one."
msgstr "未找到 WAN 区域;没有它无法切换日志记录。"
@@ -495,7 +500,7 @@ msgstr "点击「启用」之前不会有任何更改。"
msgid "OUT"
msgstr "出接口"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2595
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2599
msgid "One line"
msgstr "单行"
@@ -524,29 +529,29 @@ msgstr ""
msgid "Outbound"
msgstr "出站"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2363
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2367
msgid "Palette"
msgstr "配色"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1702
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2519
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1706
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2523
msgid "Pause"
msgstr "暂停"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1701
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1705
msgid "Paused"
msgstr "已暂停"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/log.js:364
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/log.js:405
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/table.js:43
msgid "Proto"
msgstr "协议"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2656
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2660
msgid "Protocol — common values"
msgstr "协议 — 常用值"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2635
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2639
msgid "Quick search"
msgstr "快速搜索"
@@ -554,15 +559,15 @@ msgstr "快速搜索"
msgid "Remove filter"
msgstr "移除筛选"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1702
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1706
msgid "Resume"
msgstr "继续"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2353
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2357
msgid "Row tint"
msgstr "行着色"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2447
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2451
msgid ""
"Row tint shows pass/deny row backgrounds when checked. Choose Classic (green/"
"red, default) or Accessible (teal/orange). Action text stays colored either "
@@ -571,7 +576,7 @@ msgstr ""
"勾选后行着色显示放行/拒绝行背景。可选经典(绿/红,默认)或无障碍(青绿/橙)。"
"操作文本在两种模式下均保持着色。"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2503
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2507
msgid ""
"Row tint used a local color fallback because the active LuCI theme did not "
"apply pass/deny backgrounds."
@@ -597,77 +602,77 @@ msgstr "规则标签不可用 — 临时文件创建失败"
msgid "SPort"
msgstr "源端口"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/log.js:361
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/log.js:402
msgid "Search"
msgstr "搜索"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1439
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1443
msgid "Server adaptive protection is disabled."
msgstr "服务器自适应保护已禁用。"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1437
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1441
msgid "Server protection state is unknown."
msgstr "服务器保护状态未知。"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1444
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1448
msgid "Server shedding — at most %d log lines per poll."
msgstr "服务器正在削减 — 每次轮询最多 %d 行日志。"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1446
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1450
msgid "Server truncated this poll (adaptive cap)."
msgstr "服务器已截断本次轮询(自适应上限)。"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2380
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2384
msgid "Show hostnames"
msgstr "显示主机名"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2351
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2355
msgid "Show pass/deny row background colors"
msgstr "显示放行/拒绝行背景色"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1548
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2618
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1552
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2622
msgid "Show rows"
msgstr "显示行"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1505
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1509
msgid "Showing a compact summary of the top of the shown sample."
msgstr "显示当前样本顶部的简要摘要。"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2542
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2546
msgid "Simple"
msgstr "简洁"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/log.js:365
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/log.js:406
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/table.js:44
msgid "Source"
msgstr "源地址"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2685
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2689
msgid "Source IP contains (! to exclude)"
msgstr "源 IP 包含(! 表示排除)"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/log.js:367
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/log.js:408
msgid "Source port"
msgstr "源端口"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2690
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2694
msgid "Source port (! to exclude)"
msgstr "源端口(! 表示排除)"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1507
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1511
msgid "Summary data is unavailable; show rows to inspect the sample."
msgstr "摘要数据不可用;显示行以检查样本。"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2609
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2613
msgid "Summary mode"
msgstr "摘要模式"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1509
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1513
msgid "Summary was bounded before all categories could be included."
msgstr "摘要已受限,无法包含所有类别。"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2421
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2425
msgid ""
"Switching tabs pauses polling; returning performs one catch-up poll. "
"Hostnames are off by default because lookups add work."
@@ -675,12 +680,12 @@ msgstr ""
"切换到其他标签页会暂停轮询;返回时执行一次追赶轮询。主机名默认关闭,因为解析"
"会增加工作量。"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:892
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:931
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:894
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:935
msgid "The firewall did not reload; saved and live logging may differ."
msgstr "防火墙未能重新加载;已保存的日志设置与当前生效状态可能不一致。"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2431
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2435
msgid ""
"The rate shown for WAN logging is the firewall zone log_limit. OpenWrt "
"defaults to 10/minute when no explicit limit is configured; fwlive does not "
@@ -689,7 +694,7 @@ msgstr ""
"WAN 日志显示的速率是防火墙区域的 log_limit。未显式配置时 OpenWrt 默认为 10 "
"条/分钟;此上限并非 fwlive 施加。"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2426
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2430
msgid ""
"The router log ring may evict older events before fwlive reads them. fwlive "
"cannot recover evicted entries or change forwarding behavior."
@@ -697,13 +702,13 @@ msgstr ""
"路由器日志环形缓冲区可能在 fwlive 读取前淘汰较旧事件。fwlive 无法恢复已淘汰的"
"条目,也不会改变转发行为。"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2393
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2397
msgid ""
"The table updates automatically when your firewall logs traffic. Use Pause "
"if it moves too fast."
msgstr "防火墙记录流量时表格会自动更新。如果滚动太快,请使用「暂停」。"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2506
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2510
msgid "Theme tint fallback"
msgstr "主题着色回退"
@@ -720,15 +725,15 @@ msgstr "此路由器报告了 Live View 尚无法识别的日志阻止项。"
msgid "Time"
msgstr "时间"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1514
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1518
msgid "Top drops"
msgstr "主要丢弃"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1517
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1521
msgid "Top rules"
msgstr "主要规则"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1511
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1515
msgid "Top talkers"
msgstr "主要通信方"
@@ -753,16 +758,16 @@ msgstr "开启 WAN 防火墙区域的日志记录并重新加载防火墙。"
msgid "Undo:"
msgstr "撤销:"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2450
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2454
msgid "Use Detail for all columns (flags, length, raw message)."
msgstr "使用「详细」可查看全部列(标志、长度、原始消息)。"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2524
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2530
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2528
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2534
msgid "View"
msgstr "视图"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:944
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:948
msgid "WAN drop/reject logging is off."
msgstr "WAN 丢弃/拒绝日志记录已关闭。"
@@ -774,7 +779,7 @@ msgstr ""
"WAN 丢弃/拒绝日志记录已开启。被阻止的 WAN 入站流量会显示在这里;正常的 LAN 浏"
"览不会。"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:908
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:910
msgid ""
"WAN drop/reject logging is on. Blocked inbound traffic should appear here as "
"it happens — not normal LAN browsing."
@@ -782,7 +787,7 @@ msgstr ""
"WAN 丢弃/拒绝日志记录已开启。被阻止的入站流量会实时显示在这里 — 正常的 LAN 浏"
"览不会。"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:910
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:912
msgid "WAN logging is already enabled."
msgstr "WAN 日志记录已处于启用状态。"
@@ -811,25 +816,25 @@ msgstr "WAN 日志不可用:无 WAN 区域"
msgid "Waiting for firewall events"
msgstr "正在等待防火墙事件"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1701
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2494
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1705
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2498
msgid "Watching"
msgstr "监视中"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2584
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2588
msgid "Wrap"
msgstr "自动换行"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/table.js:71
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2640
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2644
msgid "block"
msgstr "拦截"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1259
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1263
msgid "buffer filling"
msgstr "缓冲区填充中"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1263
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1267
msgid "buffer full"
msgstr "缓冲区已满"
@@ -846,7 +851,7 @@ msgid "does not contain"
msgstr "不包含"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/table.js:72
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2641
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2645
msgid "drop"
msgstr "丢弃"
@@ -854,16 +859,16 @@ msgstr "丢弃"
msgid "is"
msgstr "是"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1258
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1262
msgid "loading buffer"
msgstr "正在加载缓冲区"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1633
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1637
msgid "loading…"
msgstr "加载中…"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1487
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1496
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1491
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1500
msgid "none"
msgstr "无"
@@ -871,94 +876,94 @@ msgstr "无"
msgid "not"
msgstr "非"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1844
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1848
msgid "not AH"
msgstr "非 AH"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1843
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1847
msgid "not ESP"
msgstr "非 ESP"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1842
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1846
msgid "not GRE"
msgstr "非 GRE"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1839
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1843
msgid "not ICMP"
msgstr "非 ICMP"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1840
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1844
msgid "not ICMPv6"
msgstr "非 ICMPv6"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1841
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1845
msgid "not IGMP"
msgstr "非 IGMP"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1845
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1849
msgid "not SCTP"
msgstr "非 SCTP"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1837
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1841
msgid "not TCP"
msgstr "非 TCP"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1838
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1842
msgid "not UDP"
msgstr "非 UDP"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2646
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2650
msgid "not block"
msgstr "非阻止"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2645
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2649
msgid "not drop"
msgstr "非丢弃"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2644
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2648
msgid "not pass"
msgstr "非放行"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2647
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2651
msgid "not reject"
msgstr "非拒绝"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2648
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2652
msgid "not unknown"
msgstr "非未知"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2663
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2667
msgid "or type…"
msgstr "或输入…"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/table.js:70
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2639
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2643
msgid "pass"
msgstr "通过"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/table.js:73
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2642
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2646
msgid "reject"
msgstr "拒绝"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1265
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1269
msgid "render paused (high rate)"
msgstr "渲染已暂停(速率过高)"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1274
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1278
msgid "resolve paused (load)"
msgstr "解析已暂停(负载)"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1276
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1280
msgid "scroll frozen — scroll to top to follow live"
msgstr "滚动已冻结 — 滚动到顶部以恢复跟随"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1273
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1277
msgid "truncated"
msgstr "已截断"
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/table.js:62
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2643
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2647
msgid "unknown"
msgstr "未知"
@@ -36,18 +36,22 @@ NF_LOG_IPV6_READY=false
# firewall.<zone>.log bit, computes a target, then uci set + uci commit. Two
# concurrent callers could otherwise interleave and last-commit-wins.
#
# BusyBox flock constraint: it has NO -w timeout. The lock is opened with
# `exec 9>>` + `flock 9`. In ash/dash, `>>` clears close-on-exec, so a live
# child keeps fd 9 after this holder dies. A stuck lock therefore blocks any
# waiter until the last fd-9 inheritor exits or the device reboots (killing
# the holder alone may not release it). The critical section MUST stay SHORT
# (a few uci commands). Do NOT hold the lock across the /etc/init.d/firewall
# reload (can take seconds); the lock is released before reload.
# Reload-failure rollback re-acquires the lock so the read-compare-restore
# is atomic against concurrent writers; do not drop that re-acquire.
# Overridable for tests/containers (default is root-only /etc/fwlive).
# BusyBox flock has no -w timeout, so acquire with `flock -n 9` and poll for
# WAN_LOG_LOCK_WAIT_SEC one-second retry intervals. In ash/dash, `>>` clears
# close-on-exec, so a live child can keep fd 9 after this holder dies; callers
# fail closed when that stale lock outlives the wait budget. The critical
# section MUST stay SHORT (a few uci commands). Do NOT hold the lock across
# the /etc/init.d/firewall reload (can take seconds); the lock is released
# before reload. Reload-failure rollback re-acquires the lock and checks the
# volatile commit generation so an intervening off->on write is not mistaken
# for an unchanged value; do not drop that re-acquire or generation check.
# The lock path can be overridden for tests/containers (default is root-only
# /etc/fwlive); generation tests use the analogous FWLIVE_WAN_LOG_GENERATION_FILE.
WAN_LOG_LOCK_FILE="${FWLIVE_WAN_LOG_LOCK_FILE:-/etc/fwlive/logging.lock}"
WAN_LOG_BASELINE_FILE="${FWLIVE_WAN_LOG_BASELINE_FILE:-/etc/fwlive/wan-log-baseline}"
WAN_LOG_GENERATION_FILE="${FWLIVE_WAN_LOG_GENERATION_FILE:-/var/run/fwlive/wan-log-generation}"
WAN_LOG_LOCK_WAIT_SEC=5
WAN_LOG_COMMIT_GENERATION='unavailable'
WAN_ZONE_DIAGNOSTIC_JSON=''
WAN_ZONE_FOUND=''
@@ -155,8 +159,9 @@ wan_log_lock_dir_safe() {
[ -z "$_writable" ]
}
# Acquire the exclusive logging lock on fd 9. Blocks until free; fails closed
# (return 1) only if the lock file cannot be opened or flock is unavailable.
# Acquire the exclusive logging lock on fd 9. Polls non-blocking flock for a
# bounded retry budget; fails closed if the lock file cannot be opened, flock is
# unavailable, or the lock stays busy for WAN_LOG_LOCK_WAIT_SEC intervals.
# Create/tighten the lock to 0600 so unprivileged UIDs cannot take LOCK_EX on
# a world-readable fd (flock(2) allows exclusive locks on O_RDONLY).
acquire_wan_log_lock() {
@@ -182,10 +187,25 @@ acquire_wan_log_lock() {
# silence this process's stderr on the success path.
( exec 9>>"$WAN_LOG_LOCK_FILE" ) 2>/dev/null || return 1
exec 9>>"$WAN_LOG_LOCK_FILE"
flock 9 2>/dev/null || {
exec 9>&-
return 1
}
_waited=0
while :; do
if flock -n 9 2>/dev/null; then
return 0
else
_flock_rc=$?
fi
# BusyBox and util-linux both use status 1 for lock contention. Other
# failures (including a missing flock command) are setup errors.
if [ "$_flock_rc" -ne 1 ]; then
exec 9>&-
return 1
fi
[ "$_waited" -ge "$WAN_LOG_LOCK_WAIT_SEC" ] && break
sleep 1
_waited=$((_waited + 1))
done
exec 9>&-
return 1
}
# Release the logging lock (explicit unlock, then close fd 9).
@@ -194,6 +214,66 @@ release_wan_log_lock() {
exec 9>&-
}
# WAN_LOG_GENERATION_FILE is under a root-only volatile directory. Each
# fwlive writer increments it while holding fd 9, before staging/committing
# the UCI update. Atomic replacement keeps a killed writer from leaving a
# truncated token; a token write that succeeds but is followed by a failed
# commit is conservative (it can only suppress a later rollback).
wan_log_generation_read() {
_generation_dir="$(dirname "$WAN_LOG_GENERATION_FILE")"
[ -L "$_generation_dir" ] && return 1
wan_log_lock_dir_safe "$_generation_dir" || return 1
[ -L "$WAN_LOG_GENERATION_FILE" ] && return 1
if [ ! -e "$WAN_LOG_GENERATION_FILE" ]; then
printf '0'
return 0
fi
[ -f "$WAN_LOG_GENERATION_FILE" ] || return 1
_generation_value=$(cat "$WAN_LOG_GENERATION_FILE" 2>/dev/null) || return 1
case "$_generation_value" in
''|*[!0-9]*|0[0-9]*) return 1 ;;
esac
printf '%s' "$_generation_value"
}
wan_log_generation_bump() {
_generation_dir="$(dirname "$WAN_LOG_GENERATION_FILE")"
[ -L "$_generation_dir" ] && return 1
( umask 077; mkdir -p "$_generation_dir" ) 2>/dev/null || return 1
[ -L "$_generation_dir" ] && return 1
wan_log_lock_dir_safe "$_generation_dir" || return 1
[ -L "$WAN_LOG_GENERATION_FILE" ] && return 1
_generation_value=$(wan_log_generation_read) || return 1
# Keep below signed 32-bit ash arithmetic bounds. Exhaustion fails closed;
# it must never wrap and let an old rollback token compare equal again.
[ "$_generation_value" -lt 2147483646 ] || return 1
_generation_next=$((_generation_value + 1))
_generation_tmp=$(mktemp "${WAN_LOG_GENERATION_FILE}.XXXXXX") || return 1
if ! printf '%s\n' "$_generation_next" >"$_generation_tmp"; then
rm -f "$_generation_tmp"
return 1
fi
chmod 0600 "$_generation_tmp" 2>/dev/null || {
rm -f "$_generation_tmp"
return 1
}
[ -L "$WAN_LOG_GENERATION_FILE" ] && {
rm -f "$_generation_tmp"
return 1
}
mv -f "$_generation_tmp" "$WAN_LOG_GENERATION_FILE" 2>/dev/null || {
rm -f "$_generation_tmp"
return 1
}
printf '%s' "$_generation_next"
}
wan_log_tracking_failed_json() {
_zone_json="$1"
logger -t fwlive "WAN log toggle aborted: commit generation unavailable" 2>/dev/null || true
printf '{"ok":false,"changed":false,"wan_zone":%s,"error":"rollback_tracking_failed"}' "$_zone_json"
}
find_wan_zone_section_state() {
# Match the first zone whose name is 'wan' or whose effective network list
# contains 'wan'/'wan6'. UCI defaults an omitted network option to the
@@ -765,6 +845,12 @@ restore_wan_zone_log() {
logger -t fwlive "WAN log rollback skipped: firewall changes pending" 2>/dev/null || true
return 1
fi
# Record this write before touching UCI. If generation tracking is
# unavailable, do not commit an untracked rollback over another caller.
wan_log_generation_bump >/dev/null || {
logger -t fwlive "WAN log rollback skipped: commit generation unavailable" 2>/dev/null || true
return 1
}
# Capture the committed value before staging our rollback. If another
# writer stages a firewall delta after this point, the post-stage guard
# below must be able to undo only our rollback staging without reverting
@@ -848,6 +934,7 @@ commit_wan_log_change() {
zone="$1"
zone_json="$2"
target="$3"
WAN_LOG_COMMIT_GENERATION='unavailable'
# Last-moment guard: runs before OUR delta is staged, so a
# non-empty changes list here can only be a foreign writer's race.
@@ -857,6 +944,16 @@ commit_wan_log_change() {
return 1
fi
# Advance the shared volatile revision before the UCI write. A later
# reload-failure rollback may restore only if this exact revision is still
# current. Failing closed here avoids a commit that other in-flight callers
# cannot detect. A failed UCI operation can leave a harmless revision gap.
_generation=$(wan_log_generation_bump) || {
wan_log_tracking_failed_json "$zone_json"
return 1
}
WAN_LOG_COMMIT_GENERATION="$_generation"
# Staging is empty here — capture the committed value so a post-stage
# foreign race can undo our delta without `uci revert firewall`.
previous=$(wan_zone_log_value "$zone")
@@ -946,21 +1043,21 @@ report_wan_log_after_commit() {
return 0
fi
shift 2
reload_and_report_wan_log "$@"
reload_and_report_wan_log "$@" "$WAN_LOG_COMMIT_GENERATION"
}
# Firewall reload + best-effort UCI rollback on reload failure. The reload
# itself runs WITHOUT the logging lock (it can take seconds and a held lock
# would block a concurrent toggle until the holder exits — BusyBox flock has
# no -w timeout).
# makes concurrent toggles poll across WAN_LOG_LOCK_WAIT_SEC one-second
# intervals, plus command and scheduling overhead).
#
# The ROLLBACK re-acquires the lock: read->compare->restore is only
# atomic when no other writer can commit between the read and the restore.
# All writers hold the same flock, so re-acquiring it makes the
# decision-and-restore a serialized unit. The lock is held only for the few
# uci commands of the restore (short critical section), never across the
# reload. If the lock cannot be re-acquired, skip the rollback (report the
# reload failure; the next toggle self-corrects).
# The ROLLBACK re-acquires the lock and checks the generation captured at
# commit time. Value equality alone misses an off->on ABA while reload runs.
# All fwlive writers advance the same generation under the lock before UCI
# changes, so rollback proceeds only when no later writer has intervened.
# The lock is held only for the few uci commands of the restore (short
# critical section), never across the reload. If reacquisition or generation
# verification fails, skip rollback and report the reload failure.
reload_and_report_wan_log() {
zone="$1"
previous="$2"
@@ -968,26 +1065,29 @@ reload_and_report_wan_log() {
fail_msg="$4"
success_msg="$5"
zone_json="$6"
committed_generation="$7"
if ! reload_firewall; then
# Re-acquire the logging lock so the rollback decision is atomic
# against concurrent toggles (no check-then-restore race).
# against concurrent toggles and check for an intervening ABA.
if acquire_wan_log_lock; then
current_generation=$(wan_log_generation_read 2>/dev/null || true)
now="$(wan_zone_log_value "$zone")"
if [ "$now" = "$committed" ]; then
# Current value is still what THIS caller committed — restore
# the pre-commit value. (If a concurrent toggle committed the
# same value, the toggle is idempotent: the state intent is
# identical, so restoring previous is the correct rollback.)
if [ -n "$committed_generation" ] \
&& [ "$committed_generation" != unavailable ] \
&& [ "$current_generation" = "$committed_generation" ] \
&& [ "$now" = "$committed" ]; then
# No later fwlive commit attempt advanced the generation, and
# UCI still carries this caller's committed value.
if restore_wan_zone_log "$zone" "$previous"; then
logger -t fwlive "$fail_msg" 2>/dev/null || true
else
logger -t fwlive "Firewall reload failed; WAN log rollback skipped (pending changes or restore failed)" 2>/dev/null || true
fi
else
# A concurrent toggle changed the value after our commit; do
# not clobber it. Log the divergence and leave the newer value.
logger -t fwlive "Firewall reload failed; WAN log changed concurrently — rollback skipped" 2>/dev/null || true
# A later writer may have left the same value (ABA), or the
# revision may be unavailable. Do not clobber that intent.
logger -t fwlive "Firewall reload failed; WAN log changed concurrently or revision unavailable — rollback skipped" 2>/dev/null || true
fi
release_wan_log_lock
else
@@ -1045,6 +1145,11 @@ enable_wan_logging() {
# uninstall restore logging after disable. Skip-if-exists in
# the helper. Failure must not become baseline_snapshot_failed.
maybe_snapshot_wan_log_baseline "$zone" "" || true
if ! wan_log_generation_bump >/dev/null; then
release_wan_log_lock
wan_log_tracking_failed_json "$zone_json"
return 0
fi
release_wan_log_lock
printf '{"ok":true,"changed":false,"wan_zone":%s}' "$zone_json"
return 0
@@ -1098,6 +1203,11 @@ disable_wan_logging() {
current=$(wan_zone_log_value "$zone")
if [ -z "$current" ] || ! wan_filter_log_enabled "$current"; then
if ! wan_log_generation_bump >/dev/null; then
release_wan_log_lock
wan_log_tracking_failed_json "$zone_json"
return 0
fi
release_wan_log_lock
printf '{"ok":true,"changed":false,"wan_zone":%s}' "$zone_json"
return 0
+1 -1
View File
@@ -23,7 +23,7 @@ LUCI_DEPENDS:= \
PKG_NAME:=luci-app-homeproxy-pro
PKG_VERSION:=28.9.1.14
PKG_RELEASE:=7
PKG_RELEASE:=8
# Only files the package actually ships belong here. The previous list named
# four paths that exist nowhere in the tree - certs/ and ruleset/ are created at
+32
View File
@@ -74,6 +74,38 @@
**一句话总结**:pro 的核心价值是**把"单文件能跑"变成"orchestrator + table-driven adapter + 可独立测试的模块"**,并把约束、质量、回滚三件事从靠人盯变成靠代码执行(arch-guard 135 checks 静态锁住跨文件不变量)。
## 排障要点
两条会把人带偏的坑,都是实测踩出来的,写在这里省得下一个人重走一遍。
**1. 客户端的 `sing-box-c.log` 里会出现"不是本机做出的决定"**
代理节点侧的失败会用同样的措辞落进客户端日志。例如目的 IP 的 443 没有服务:
```
ERROR connection: open connection to 1.2.3.4:443 using outbound/direct[direct]:
dial tcp 1.2.3.4:443: connect: connection refused
```
这行**不代表**本机把该流量送去了直连——它可能是远端节点直连目标失败后的回传。曾据此误判"分流规则失效",实际是节点侧错误被记进了客户端日志。
判定真实走向要看**转发层**,不要看日志措辞:
```sh
# 被重定向到本机代理端口 = 流量确实进入了代理路径
grep -m1 'dst=<IP>.*dport=443' /proc/net/nf_conntrack # 回复方向出现 sport=5331
# 分流链各分支计数:return 的分支=直连,最后 goto=走代理
nft list chain inet fw4 homeproxy_redirect
# 大陆集合:防火墙据此判定"直连",与路由侧的 china-ip 规则集同源(china_ip4.txt)
nft list chain inet fw4 homeproxy_redirect | grep mainland_addr_v4
```
**2. `sing-box tools connect/fetch` 不能用来验证分流**
两者都通过 `instance.Outbound().Default()` 取默认出站(`cmd_tools_connect.go` / `cmd_tools_fetch.go`),**不经过 route 规则**。用它们测"某个域名/IP 会走代理还是直连",得到的结论一定是错的。
要验证实际走向,只能让**局域网真实客户端**发起连接,再看上面的 conntrack / nft 计数,或看 `sing-box-c.log` 里的 `outbound/xxx[tag]` 行(那才是本机的出站选择)。
## 已知限制
- **试验性**:不承诺 API/配置稳定,重大变更可能在 minor 版本里发生。
@@ -58,6 +58,20 @@ import { attachSchema, attachExperimental } from './common.uc';
export function generate(dm, env) {
const ctx = build_context(dm, env);
/* Everything the client prints lands in one file, and not all of it was
* decided here: when the proxy node cannot reach a destination, sing-box
* reports it through the same logger, so a line like
*
* open connection to 1.2.3.4:443 using outbound/direct[direct]:
* connect: connection refused
*
* can be the *node's* failure rather than this router sending the traffic
* direct. Reading it as a local routing decision already produced one
* wrong conclusion during review. What the router actually did is in the
* forwarding layer: `nft list chain inet fw4 homeproxy_redirect` (a
* `return` branch is direct, the final `goto` is the proxy port) and
* `/proc/net/nf_conntrack` (`sport=5331` on the reply tuple). See the
* README's troubleshooting section. */
const config = {
log: {
disabled: false,
@@ -130,9 +130,27 @@ export function build_context(dm, env) {
udp_mapping = (udp_mapping_raw === 'default') ? '' : udp_mapping_raw,
udp_filtering = (udp_filtering_raw === 'default') ? '' : udp_filtering_raw;
/* Where a destination the preset lists do not mention goes. This is the
* mode's whole default policy, so it is declared once here and read by
* both consumers - the route rule chain and the DNS rule chain:
*
* bypass_mainland_china mainland -> direct, everything else -> proxy
* global everything -> proxy
* gfwlist only the list -> proxy, everything else direct
* proxy_mainland_china mainland -> proxy, everything else -> direct
*
* The two chains have to agree. They used to say it independently (a
* literal `main-dns` in dns.uc, a literal `main-out` in route.uc) with
* nothing tying them together, and the route side applied "unknown goes
* to the proxy" to all four modes - so "Only proxy mainland China" sent
* every unlisted destination through the proxy, which is the opposite of
* what the mode is named. Both now derive from this one value. */
const proxy_fallback = (routing_mode !== 'proxy_mainland_china');
/* Routing-mode-dependent defaults */
const ctx = {
routing_mode, proxy_mode, ipv6_support, log_level,
proxy_fallback,
self_mark, ntp_server, dns_port, mixed_port,
redirect_port, tproxy_port,
tun_name, tun_addr4, tun_addr6, tun_mtu,
@@ -112,7 +112,11 @@ function append_proxy_dns(config, dm, ctx) {
detour: 'main-out',
...parse_dnsserver(ctx.dns_server, 'tcp')
});
config.dns.final = 'main-dns';
/* The DNS half of the mode's default policy. It has to name the same
* side the route chain falls through to (ctx.proxy_fallback), or a
* domain that ends up proxied would be resolved by the resolver of the
* other path. The pair is asserted per mode in the generator suite. */
config.dns.final = ctx.proxy_fallback ? 'main-dns' : 'default-dns';
if (length(ctx.direct_domain_list))
push(config.dns.rules, {
@@ -26,7 +26,7 @@
'use strict';
import { isEmpty, strToInt, strToTime, strToBool, parse_port } from '../homeproxy-pro.uc';
import { isEmpty, strToInt, strToTime, strToBool, parse_port, HP_DIR } from '../homeproxy-pro.uc';
import { get_outbound, get_resolver, get_ruleset, get_direct_override } from './common.uc';
@@ -92,15 +92,24 @@ function build_route_proxy(config, dm, ctx, direct_overrides) {
outbound: 'direct-out'
});
/* Bypass CN traffic: resolve the destination first, then route by IP.
sing-box does not match an IP-based rule set (geoip-cn) against a domain
destination unless it is resolved first, so add an explicit resolve
action; geoip-cn then sends China IPs to direct and everything else falls
through to main-out (proxy). This avoids relying on the geosite-* domain
lists, which can mis-classify foreign domains (e.g. Google's gvt2.com
beacons) as "cn" and send them direct to time out. Keep the direct-domain
fast-path above for known direct domains. */
if (ctx.routing_mode === 'bypass_mainland_china') {
/* Split by destination IP, in both directions.
*
* sing-box does not match an IP-based rule set (geoip-cn) against a
* domain destination unless it is resolved first, so an explicit resolve
* action comes first. Which way the split points is the mode's default
* policy (ctx.proxy_fallback):
*
* bypass/global/gfwlist mainland -> direct, the rest falls through to
* main-out (proxy)
* proxy_mainland_china mainland -> proxy, the rest falls through to
* direct-out
*
* Using geoip-cn rather than the geosite-* domain lists is deliberate:
* those mis-classify foreign domains (Google's gvt2.com beacons) as "cn"
* and would send them direct to time out. Keep the direct-domain
* fast-path above for known direct domains. */
if (ctx.routing_mode === 'bypass_mainland_china'
|| ctx.routing_mode === 'proxy_mainland_china') {
push(config.route.rules, {
action: 'resolve',
strategy: (ctx.ipv6_support !== '1') ? 'prefer_ipv4' : null
@@ -108,6 +117,23 @@ function build_route_proxy(config, dm, ctx, direct_overrides) {
push(config.route.rules, {
rule_set: 'geoip-cn',
action: 'route',
outbound: ctx.proxy_fallback ? 'direct-out' : 'main-out'
});
}
if (ctx.routing_mode === 'bypass_mainland_china') {
/* geoip-cn.srs is upstream's list; the firewall's mainland set is
* built from our own china_ip4.txt (firewall_post.ut reads it
* directly). The two disagree - the bundled list has 8.152.0.0/13
* (Alibaba Cloud) and geoip-cn.srs does not - so a connection into
* that range passes the firewall as "not mainland" and would then be
* sent direct by the rule above instead of to the proxy. This local
* rule-set is generated from the same china_ip4.txt, which is what
* makes both sides decide from one list. In
* proxy_mainland_china it is not needed twice: the geoip-cn rule
* already selects the mainland side for the proxy. */
push(config.route.rules, {
rule_set: 'china-ip',
action: 'route',
outbound: 'direct-out'
});
}
@@ -134,7 +160,10 @@ function build_route_proxy(config, dm, ctx, direct_overrides) {
override_port: main_override.override_port
});
config.route.final = 'main-out';
/* The route half of the mode's default policy - the other consumer of
* ctx.proxy_fallback, and the reason dns.final and this line are
* asserted together per mode. */
config.route.final = ctx.proxy_fallback ? 'main-out' : 'direct-out';
/* --- proxy-mode rule_set block ----------------------------------- */
@@ -162,7 +191,13 @@ function build_route_proxy(config, dm, ctx, direct_overrides) {
]
});
if (ctx.routing_mode === 'bypass_mainland_china') {
/* Both mainland modes split on geoip-cn, so both need it declared - it is
* what the route rule added above matches. `proxy_mainland_china` used
* to reach this point without it and sing-box refused the whole config
* with "initialize rule[3]: rule-set not found: geoip-cn", which the
* health gate turned into a rollback and an unproxied network. */
if (ctx.routing_mode === 'bypass_mainland_china'
|| ctx.routing_mode === 'proxy_mainland_china') {
/*
* Fetched straight from the upstream SagerNet repositories and
* downloaded through the selected node. A direct fetch depends on
@@ -170,18 +205,22 @@ function build_route_proxy(config, dm, ctx, direct_overrides) {
* makes it fail intermittently; the files total ~250 KB per
* day, so proxying the download costs almost nothing.
*
* Both geosite-cn and geoip-cn are still emitted, but they are
* consumed by different blocks:
* Both rule_sets are emitted, but they are consumed by different
* blocks:
*
* geoip-cn - referenced by the route rule above (line 105) to
* split CN destinations to direct-out.
* geoip-cn - referenced by the route rule above to split
* destinations by IP: mainland -> direct-out in
* bypass_mainland_china, mainland -> main-out in
* proxy_mainland_china.
* geosite-cn - referenced only by the DNS rule in
* generator/dns.uc line 135-139 (rule_set: geosite-cn
* -> server: china-dns); the route layer never
* matches it. Sing-box still loads and keeps the
* rule-set in memory even when only one block
* references it, so removing it would also break the
* DNS split.
* generator/dns.uc (rule_set: geosite-cn ->
* server: china-dns); the route layer never matches
* it. Sing-box still loads and keeps the rule-set in
* memory even when only one block references it, so
* removing it would also break the DNS split. It is
* only declared for bypass_mainland_china because that
* is the only mode whose DNS chain routes through
* china-dns.
*
* History: a third geosite-noncn used to be declared here as well,
* but no rule referenced it and sing-box still loaded and updated
@@ -205,6 +244,21 @@ function build_route_proxy(config, dm, ctx, direct_overrides) {
update_interval: '24h',
download_detour: 'main-out'
});
/* Local, and not downloaded: it is generated from the same
* china_ip4.txt the firewall renders homeproxy_mainland_addr_v4
* from, so the two cannot drift apart. `type: local` is watched by
* sing-box with fswatch, so when the resource updater replaces the
* file the running instance reloads it in place - no restart and,
* more importantly, no second copy of the list to keep in sync.
* China IPv6 is deliberately absent: the firewall only fills the v6
* set when ipv6_support is on, and the client refuses to resolve
* AAAA when it is off. */
push(config.route.rule_set, {
type: 'local',
tag: 'china-ip',
path: HP_DIR + '/resources/china_ip4.json'
});
}
if (isEmpty(config.route.rule_set))
@@ -0,0 +1,147 @@
#!/usr/bin/ucode
/*
* SPDX-License-Identifier: GPL-2.0-only
*
* Turn a plain CIDR list into a sing-box local rule-set, so the route side and
* the firewall decide "is this destination mainland China" from the same bytes.
*
* The firewall's homeproxy_mainland_addr_v4 set is filled by firewall_post.ut
* straight from /etc/homeproxy-pro/resources/china_ip4.txt, while the route rule
* used the upstream geoip-cn.srs. Those two disagree: the bundled list carries
* 8.152.0.0/13 (Alibaba Cloud) which geoip-cn.srs does not, so a connection to
* segmentfault.com or ctrip.com resolved into that range is redirected by the
* firewall (not in the nft set) and then sent *direct* by the route rule (not
* in geoip-cn.srs) - the opposite of what the mode promises.
*
* Reading the same text file on both sides removes the disagreement, and it
* has to survive the resource updater replacing the list: the generated
* rule-set is written with a local `type: local` entry in the client config,
* which sing-box watches with fswatch, so replacing the file is enough - no
* service restart, no second copy of the data.
*
* Usage: ucode -S china_ip_ruleset.uc <source .txt> <destination .json>
* Exit 0 when the file was written, 1 (with a message on stderr) otherwise.
* An unreadable or empty source is *not* an error: the caller may legitimately
* have no list yet, and a missing rule-set file is handled by the generator
* (it emits the local rule only when the file exists).
*/
'use strict';
import { readfile, writefile, rename, unlink } from 'fs';
if (length(ARGV) < 2 || ARGV[0] == null || ARGV[1] == null) {
warn('usage: china_ip_ruleset.uc <source .txt> <destination .json>\n');
exit(1);
}
const src = ARGV[0];
const dst = ARGV[1];
const data = readfile(src);
if (type(data) !== 'string') {
warn(sprintf('cannot read %s\n', src));
exit(1);
}
/* One `ip_cidr` array is one netipx.IPSet on the sing-box side, which is a
* prefix-compressed structure with a binary search per query; splitting the
* list into several rules would only add rules to walk per packet.
*
* The entries are validated here rather than trusted. netip.ParsePrefix() is
* what sing-box uses, it is strict (no leading zeros, no out-of-range octets),
* and one bad entry makes it reject the *whole* rule-set - which would take
* the client's start with it. A malformed line is therefore skipped and
* reported, exactly like the blank lines and comments a list may carry. */
function is_v4(addr) {
const parts = split(addr, '.');
if (length(parts) != 4)
return false;
for (let part in parts) {
if (!match(part, /^[0-9]{1,3}$/))
return false;
/* Go's parser rejects "01"; match it so the file it accepts and
* the file we emit cannot disagree. */
if (length(part) > 1 && substr(part, 0, 1) == '0')
return false;
if (int(part) > 255)
return false;
}
return true;
}
function is_v6(addr) {
/* Deliberately structural, not exhaustive: the point is to keep a
* malformed line out, and the strict parser on the other side is the
* authority for what a valid address is. */
if (!match(addr, /^[0-9a-fA-F:]+$/))
return false;
if (!match(addr, /:/))
return false;
if (match(addr, /:::/))
return false;
return true;
}
function valid_cidr(entry) {
const slash = index(entry, '/');
if (slash <= 0)
return false;
const addr = substr(entry, 0, slash);
const bits = substr(entry, slash + 1);
if (!match(bits, /^[0-9]{1,3}$/))
return false;
if (is_v4(addr))
return int(bits) <= 32;
if (is_v6(addr))
return int(bits) <= 128;
return false;
}
const prefixes = [];
let skipped = 0;
for (let line in split(data, /\n/)) {
const entry = trim(line);
if (entry == '')
continue;
/* The updater's lists are bare CIDRs, but a stray comment or a host
* address must not make sing-box reject the whole rule-set at start. */
if (match(entry, /^[#;]/))
continue;
if (!valid_cidr(entry)) {
skipped++;
continue;
}
push(prefixes, entry);
}
if (skipped > 0)
warn(sprintf('skipped %d malformed entries in %s\n', skipped, src));
if (length(prefixes) == 0) {
warn(sprintf('no usable CIDR entries in %s\n', src));
exit(1);
}
/* The temporary name is a sibling so the rename stays on one filesystem: the
* watcher must never read a half-written file. `%.J` is the same serializer
* the config generator uses, so the output is the canonical sing-box form. */
const rendered = sprintf('%.J\n', {
version: 3,
rules: [
{ ip_cidr: prefixes }
]
});
const tmp = dst + '.tmp';
if (!writefile(tmp, rendered)) {
warn(sprintf('cannot write %s\n', tmp));
exit(1);
}
if (!rename(tmp, dst)) {
unlink(tmp);
warn(sprintf('cannot install %s\n', dst));
exit(1);
}
printf('%d prefixes\n', length(prefixes));
@@ -127,6 +127,24 @@ hp_prepare_runtime_files() {
local client_enabled="$4"
local server_enabled="$5"
# The route rule that keeps mainland destinations direct matches a local
# rule-set generated from china_ip4.txt, the same list the firewall
# renders its nft set from, so both sides decide from one source. It is
# regenerated here as well as by the resource updater: an install that
# predates this file (or a list replaced while the service was stopped)
# must still get one, or the client config would reference a missing
# file and sing-box would refuse to start. A missing source list is not
# an error - the generator then emits no route-side rule at all.
if [ "$client_enabled" = "1" ] && [ -f "$hp_dir/resources/china_ip4.txt" ]; then
if ucode -S "$hp_dir/scripts/runtime/china_ip_ruleset.uc" \
"$hp_dir/resources/china_ip4.txt" "$hp_dir/resources/china_ip4.json" >>"$LOG_PATH" 2>&1; then
chown sing-box:sing-box "$hp_dir/resources/china_ip4.json" 2>"/dev/null" \
|| log "Warning: failed to hand ${hp_dir}/resources/china_ip4.json to sing-box."
else
log "Warning: could not generate ${hp_dir}/resources/china_ip4.json; the route side keeps the previous list."
fi
fi
# cache_file is enabled for bypass_mainland_china and custom alike
# (generator/common.uc:attachExperimental). The file has to exist before
# the client jail binds it, or sing-box cannot create it in there.
@@ -219,13 +237,16 @@ hp_procd_client_instance() {
# guessed subset. procd orders the mounts by path, so this parent is
# bound before the read-write cache.db below and cannot shadow it.
procd_add_jail_mount "$hp_dir/"
# cache_file is enabled for bypass_mainland_china and custom alike
# (generator/common.uc:attachExperimental), so both need the file
# writable inside the jail.
case "$routing_mode" in
"bypass_mainland_china"|"custom")
procd_add_jail_mount_rw "$hp_dir/cache.db" ;;
esac
# cache_file is emitted unconditionally (generator/common.uc:
# attachExperimental takes the routing mode and never reads it), so
# every mode's sing-box opens this file and dies with
# initialize cache-file: open /etc/homeproxy-pro/cache.db: read-only file system
# if it is bound read-only - which is what the mode-gated mount here
# did for global / gfwlist / proxy_mainland_china. The instance then
# failed its health gate and the intercept layer was reverted, so
# switching to one of those modes took the whole network off the
# proxy.
procd_add_jail_mount_rw "$hp_dir/cache.db"
procd_add_jail_mount "$hp_dir/certs/"
# The certificate path gate accepts /etc/acme as well (a client TLS
# certificate managed by acme.sh), so the client jail has to carry it
@@ -193,6 +193,23 @@ check_list_update() {
# can read it.
date -u +"%Y-%m-%dT%H:%M:%SZ" > "$RESOURCES_DIR/$listtype.updated_at"
log "[$(to_upper "$listtype")] Successfully updated via $mirror (blob $local_blob)."
# The route side reads the China list through a generated sing-box
# rule-set rather than geoip-cn.srs, so the list and what the kernel
# decides from cannot drift apart. sing-box watches that file with
# fswatch and reloads it in place, so regenerating it here is the
# whole update path - no service restart. A failure is logged and
# not fatal: the previous rule-set is still valid and the firewall
# has already switched to the new list, which is exactly the state
# the next run (or the next service start) repairs.
if [ "$listtype" = "china_ip4" ]; then
if ucode -S "$SCRIPT_DIR/runtime/china_ip_ruleset.uc" \
"$RESOURCES_DIR/china_ip4.txt" "$RESOURCES_DIR/china_ip4.json" >>"$LOG_PATH" 2>&1; then
log "[CHINA_IP4] Route-side rule-set regenerated."
chown sing-box:sing-box "$RESOURCES_DIR/china_ip4.json" 2>"/dev/null"
else
log "[CHINA_IP4] Warning: could not regenerate china_ip4.json; the route side keeps the previous list."
fi
fi
else
rm -f "$RUN_DIR/$listname"
log "[$(to_upper "$listtype")] Failed to install update (mv failed)."
+1
View File
@@ -269,6 +269,7 @@ Two further host differences are bridged so the remaining checks still run:
| `tests/i18n-coverage.py` | Reports how many `po/templates/homeproxy-pro.pot` strings have a non-fuzzy, non-empty `po/zh_Hans/homeproxy-pro.po` translation. Technical tokens that stay as-is are listed in `tests/i18n-ignore.txt`; anything else missing fails the run (`--fail-below 100`) and adds a job-summary entry. The release workflow (`build.yml`) deliberately warns instead of failing, so a translation gap cannot block packaging - the PR gates already catch it. |
| `tests/luci-form-snapshot.js` | Dumps every option (name, kind, title, description, depends, values, datatype/default/…) of the node, client and server views. Diffs against `tests/snapshots/{node,client,server}.json`, so a refactor that changes a field or its visibility fails. |
| `tests/ucode/test_homeproxy_utils.uc` | `executeCommand()` return shape, stderr/exit-code capture, binary detection, and a descriptor-leak check (200 calls). |
| `tests/ucode/test_china_ip_ruleset.sh` | `runtime/china_ip_ruleset.uc`, the generator that turns `china_ip4.txt` into the `type: local` rule-set the route side matches mainland destinations with — the same list `firewall_post.ut` renders `homeproxy_mainland_addr_v4` from, so both sides decide from one source. The cases are the ones that break a router silently: a malformed line must be skipped (sing-box rejects the *whole* rule-set over one bad entry, and the client then will not start), the file must be replaced by rename (the running instance watches it with fswatch), and an empty or unreadable source must fail instead of installing a rule-set that matches nothing. Pins the bundled list's `8.152.0.0/13` — the range `geoip-cn.srs` does not carry, and the reason this exists. |
| `tests/ucode/test_resource_blob_sha.sh` | `resource_blob_sha.uc`, the digest the resource updater verifies downloads with. The expected values are git's own (`git hash-object` on the same bytes), covering the two cases an implementation gets wrong quietly: the empty file (a real blob id, not "no digest") and content whose byte length differs from its character count. Unreadable input must produce *no* digest and exit non-zero - the updater treats an empty answer as "cannot verify" and refuses, so a made-up digest would turn an unreadable file into a passing check. Re-checks the repository's real resource files against git when the host has it. |
| `tests/ucode/test_homeproxy_utils_inject.uc` | The failure path of `executeCommand()`: the script stages a copy of `homeproxy-pro.uc` whose `system()` call is replaced by `die()`, then checks that the exception still propagates and that neither descriptor leaks. |
| `tests/ucode/test_tls_transport.uc` | Direct calls into `buildTLSObject()` / `buildTransportObject()`: client-vs-server-only fields (no `insecure` on a server inbound, no `utls` on a server, `public_key` only on the client and `private_key` only on the server for reality), the ECH client/server split, and the `validateHomeProxyPath()` gate on `cert_path` / `key_path`. This is the boundary the generator fixtures only exercised end-to-end, so it is where the "server emitted insecure=true" and "reality key on the wrong side" classes of bug get named. |
@@ -174,6 +174,7 @@ procd_add_jail sing-box-c log procfs
procd_add_jail_mount var/run/homeproxy-pro/sing-box-c.json
procd_add_jail_mount_rw var/run/homeproxy-pro/sing-box-c.log
procd_add_jail_mount etc/homeproxy-pro/
procd_add_jail_mount_rw etc/homeproxy-pro/cache.db
procd_add_jail_mount etc/homeproxy-pro/certs/
procd_add_jail_mount /etc/acme/
procd_add_jail_mount /etc/ssl/
@@ -354,6 +354,14 @@ echo "== resource blob digest helper (B2) =="
# repository's real resource files against git when the host has it.
sh "$ROOT/tests/ucode/test_resource_blob_sha.sh" "$ROOT" "$WORK/resource_blob_sha" || FAILED=1
echo "== china ip rule-set generator =="
# The route side's mainland split is decided from a local rule-set generated out
# of china_ip4.txt - the same list the firewall renders its nft set from. One
# malformed entry makes sing-box reject the whole rule-set and the client
# refuses to start, and the list is replaced unattended, so the generator's
# validation and its atomic write are both load-bearing.
sh "$ROOT/tests/ucode/test_china_ip_ruleset.sh" "$ROOT" "$WORK/china_ip_ruleset" || FAILED=1
echo "== test doubles still match production =="
# The mocks copy isEmpty/decodeBase64Str/parseURL/redactUrl from production and
# their headers say to keep them in sync; nothing enforced it, and the fetcher's
@@ -0,0 +1,154 @@
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0-only
#
# runtime/china_ip_ruleset.uc: the text -> sing-box local rule-set generator
# that keeps the route side and the firewall deciding mainland China from one
# list.
#
# Why this has its own test: sing-box parses the generated file with
# netip.ParsePrefix(), which is strict, and one bad entry makes it reject the
# *whole* rule-set - taking the client's start with it ("initialize router:
# parse rule-set[0]: ... ParseAddr: IPv4 field has value >255", reproduced on
# the target). The source list is downloaded and replaced unattended, so the
# failure would arrive on a router nobody is watching. The cases below are the
# ones that decide whether that can happen: a malformed line must be skipped,
# the file must be replaced atomically (the running instance watches it), and
# an empty or unreadable source must fail rather than install an empty rule-set
# that silently matches nothing.
#
# Usage: sh tests/ucode/test_china_ip_ruleset.sh <repo-root> [workdir]
set -u
ROOT="$(cd "${1:-$(dirname "$0")/../..}" && pwd)"
WORK="${2:-/tmp/hp-china-ip-test}"
if ! command -v ucode > "/dev/null" 2>&1; then
echo "NOT RUN: ucode is not on PATH."
exit 2
fi
HELPER="$ROOT/root/etc/homeproxy-pro/scripts/runtime/china_ip_ruleset.uc"
rm -rf "$WORK"
mkdir -p "$WORK"
FAILED=0
CHECKS=0
FAILURES=0
expect() {
# expect <name> <actual> <expected>
CHECKS=$((CHECKS + 1))
if [ "$2" = "$3" ]; then
echo "PASS: $1"
else
echo "FAIL: $1 (expected '$3', got '$2')"
FAILED=1
FAILURES=$((FAILURES + 1))
fi
}
generate() {
# generate <source> <destination>; stdout is "N prefixes", status 0/1
ucode -S "$HELPER" "$1" "$2" 2>"/dev/null"
}
# --- fixtures ---------------------------------------------------------------
# A list shaped like the real one: comments, blank lines, valid IPv4 and IPv6,
# and five ways a line can be malformed.
cat > "$WORK/mixed.txt" <<'EOF'
# comment
; also a comment
1.1.8.0/24
2.2.2.2/32
10.0.0.0/8
2001:db8::/32
fe80::/10
999.1.1.1/24
01.2.3.4/24
1.1.8.0/33
fe80::/129
garbage
1.2.3.4
EOF
# Five survive: 1.1.8.0/24, 2.2.2.2/32, 10.0.0.0/8, 2001:db8::/32, fe80::/10.
expect "mixed list: only the valid entries survive" \
"$(generate "$WORK/mixed.txt" "$WORK/mixed.json")" "5 prefixes"
expect "mixed list: the out-of-range octet is gone" \
"$(grep -c '999\.1\.1\.1' "$WORK/mixed.json")" "0"
expect "mixed list: the leading-zero address is gone" \
"$(grep -c '01\.2\.3\.4' "$WORK/mixed.json")" "0"
expect "mixed list: the /33 prefix is gone" \
"$(grep -c '1\.1\.8\.0/33' "$WORK/mixed.json")" "0"
expect "mixed list: the /129 prefix is gone" \
"$(grep -c 'fe80::/129' "$WORK/mixed.json")" "0"
expect "mixed list: a bare address is gone" \
"$(grep -c '"1\.2\.3\.4"' "$WORK/mixed.json")" "0"
expect "mixed list: the valid IPv4 entries stay" \
"$(grep -c '1\.1\.8\.0/24\|2\.2\.2\.2/32\|10\.0\.0\.0/8' "$WORK/mixed.json")" "3"
expect "mixed list: the valid IPv6 entry stays" \
"$(grep -c '2001:db8::/32' "$WORK/mixed.json")" "1"
# The rule-set has to be one `ip_cidr` array: one netipx.IPSet on the sing-box
# side, not N rules to walk per packet.
expect "output is a single ip_cidr rule" \
"$(grep -c '"ip_cidr"' "$WORK/mixed.json")" "1"
expect "output carries a version" \
"$(grep -c '"version": 3' "$WORK/mixed.json")" "1"
# --- the real bundled list --------------------------------------------------
# The list this ships with is what the firewall renders its nft set from; a
# generator that mangles it would break both sides at once.
expect "the bundled china_ip4.txt generates" \
"$(generate "$ROOT/root/etc/homeproxy-pro/resources/china_ip4.txt" "$WORK/real.json")" "4068 prefixes"
# 8.152.0.0/13 is the range geoip-cn.srs does *not* carry, and the one that
# made segmentfault.com and ctrip.com resolve into the proxy path.
expect "the bundled list carries 8.152.0.0/13" \
"$(grep -c '8\.152\.0\.0/13' "$WORK/real.json")" "1"
# --- failure paths ----------------------------------------------------------
# An empty source must not install an empty rule-set: the caller would then be
# running a split that matches nothing while looking like it worked.
: > "$WORK/empty.txt"
expect "empty source produces no file" "$(generate "$WORK/empty.txt" "$WORK/empty.json")" ""
ucode -S "$HELPER" "$WORK/empty.txt" "$WORK/empty.json" >/dev/null 2>&1
expect "empty source exits non-zero" "$?" "1"
expect "empty source wrote nothing" "$(test -e "$WORK/empty.json" && echo yes || echo no)" "no"
ucode -S "$HELPER" "$WORK/does-not-exist.txt" "$WORK/missing.json" >/dev/null 2>&1
expect "unreadable source exits non-zero" "$?" "1"
expect "unreadable source wrote nothing" "$(test -e "$WORK/missing.json" && echo yes || echo no)" "no"
ucode -S "$HELPER" >/dev/null 2>&1
expect "no arguments exits non-zero" "$?" "1"
# --- atomicity --------------------------------------------------------------
# The running instance watches this path with fswatch, so a partial write would
# be read as a broken rule-set. The generator writes a sibling temp file and
# renames it, which is why no `*.tmp` may survive a successful run.
rm -f "$WORK/real.json" "$WORK/real.json.tmp"
generate "$ROOT/root/etc/homeproxy-pro/resources/china_ip4.txt" "$WORK/real.json" >"/dev/null"
expect "a successful run leaves no temp file" \
"$(ls "$WORK"/real.json.tmp 2>/dev/null | wc -l)" "0"
expect "a successful run leaves the rule-set" \
"$(test -s "$WORK/real.json" && echo yes || echo no)" "yes"
# A failed run must not damage an existing rule-set either: the destination is
# only touched by the rename.
cp "$WORK/real.json" "$WORK/keep.json"
ucode -S "$HELPER" "$WORK/empty.txt" "$WORK/keep.json" >/dev/null 2>&1
expect "a failed run leaves the previous rule-set in place" \
"$(test -s "$WORK/keep.json" && echo yes || echo no)" "yes"
expect "a failed run leaves no temp file behind" \
"$(ls "$WORK"/keep.json.tmp 2>/dev/null | wc -l)" "0"
echo
echo "$CHECKS checks, $FAILURES failures"
[ "$FAILED" -eq 0 ] && echo "CHINA IP RULESET TESTS PASSED"
exit "$FAILED"
@@ -68,6 +68,15 @@ stage_case() {
printf '%s\n' "$DIRECT_DOMAIN" > "$dir/resources/direct_list.txt"
printf '%s\n' "$PROXY_DOMAIN" > "$dir/resources/proxy_list.txt"
# The client config carries a `type: local` rule-set generated from
# china_ip4.txt; sing-box opens that path during `check`, and on a router
# hp_prepare_runtime_files generates it before the config is used.
if ! ucode -S "$ROOT/root/etc/homeproxy-pro/scripts/runtime/china_ip_ruleset.uc" \
"$ROOT/root/etc/homeproxy-pro/resources/china_ip4.txt" "$dir/resources/china_ip4.json" \
>>"$dir/resources/china_ip4.log" 2>&1; then
printf '{"version":3,"rules":[{"ip_cidr":["192.0.2.0/24"]}]}\n' > "$dir/resources/china_ip4.json"
fi
VALIDATE_DATA="${HP_VALIDATE_DATA:-/sbin/validate_data}"
sed -e "s#^export const HP_DIR = '/etc/homeproxy-pro';#export const HP_DIR = '$dir';#" \
-e "s#^export const RUN_DIR = '/var/run/homeproxy-pro';#export const RUN_DIR = '$dir/run';#" \
@@ -113,6 +122,68 @@ stage_case() {
return 0
}
# The mode's default policy, asserted as a *pair*. The DNS chain and the
# route chain each end in a fallback (`dns.final`, `route.final`), and they
# have to name the same side: a domain that ends up proxied must be resolved
# by the proxy-path resolver, and one that ends up direct must not be
# resolved through the proxy. The two were literals in different modules with
# nothing tying them together, and the route side applied "unknown goes to the
# proxy" to all four modes - so "Only proxy mainland China" proxied everything
# it was not told to proxy.
check_fallback_pair() {
label="$1"
json="$2"
case "$label" in
proxy_mainland_china) want_route="direct-out"; want_dns="default-dns" ;;
*) want_route="main-out"; want_dns="main-dns" ;;
esac
# The two finals sit in different blocks and their relative order is not
# fixed (route.final is emitted before dns.final in the modes whose dns
# block grows, after it in the others), so each is taken from its own
# block: dns.final is the only '"final"' indented by two tabs inside the
# top-level dns object, route.final the only one inside route.
have_dns="$(awk '/^\t"dns": \{/{d=1} d && /^\t\t"final":/{gsub(/.*: "|".*/,""); print; exit}' "$json")"
have_route="$(awk '/^\t"route": \{/{r=1} r && /^\t\t"final":/{gsub(/.*: "|".*/,""); print; exit}' "$json")"
if [ "$have_route" != "$want_route" ]; then
echo "FAIL: $label: route.final is '$have_route', the mode's policy is '$want_route'"
return 1
fi
if [ "$have_dns" != "$want_dns" ]; then
echo "FAIL: $label: dns.final is '$have_dns', the mode's policy is '$want_dns'"
return 1
fi
# Stated as its own property, because the failure it catches is silent:
# if one of the two ever gains a new value, the pair is what breaks.
proxy_route=no; proxy_dns=no
[ "$have_route" = "main-out" ] && proxy_route=yes
[ "$have_dns" = "main-dns" ] && proxy_dns=yes
if [ "$proxy_route" != "$proxy_dns" ]; then
echo "FAIL: $label: route.final ($have_route) and dns.final ($have_dns) disagree on the mode's default"
return 1
fi
echo "PASS: $label: route.final=$have_route and dns.final=$have_dns agree"
# Both mainland modes match destinations against geoip-cn, so both have to
# declare it. proxy_mainland_china reached this point without the
# declaration once and sing-box refused the whole config with
# "initialize rule[3]: rule-set not found: geoip-cn"; the health gate then
# rolled the intercept layer back and the network went unproxied.
case "$label" in
bypass_mainland_china|proxy_mainland_china)
if ! grep -qF '"rule_set": "geoip-cn"' "$json" || ! grep -qF '"tag": "geoip-cn"' "$json"; then
echo "FAIL: $label: the route rule matches geoip-cn but the rule-set is not declared"
return 1
fi
;;
esac
return 0
}
# The rule_set assertions, shared by every case: the list reaches the config,
# the DNS half resolves through main-dns ahead of the SVCB/HTTPS reject, and
# direct-domain keeps its precedence.
@@ -232,6 +303,7 @@ for mode in bypass_mainland_china global gfwlist proxy_mainland_china; do
else
FAILED=1
fi
check_fallback_pair "$mode" "$dir/run/sing-box-c.json"
else
FAILED=1
fi
@@ -49,6 +49,19 @@ run_case() {
: > "$dir/resources/direct_list.txt"
: > "$dir/resources/proxy_list.txt"
# The client config now carries a `type: local` rule-set generated from
# china_ip4.txt (the same list the firewall renders its nft set from, so
# both sides decide mainland-China from one source). sing-box opens that
# path during `check`, so the staged tree has to have it; on a router
# hp_prepare_runtime_files generates it before the config is used.
if ! ucode -S "$ROOT/root/etc/homeproxy-pro/scripts/runtime/china_ip_ruleset.uc" \
"$ROOT/root/etc/homeproxy-pro/resources/china_ip4.txt" "$dir/resources/china_ip4.json" \
>>"$dir/resources/china_ip4.log" 2>&1; then
# Most fixtures do not exercise the route side; an empty rule-set
# keeps `sing-box check` happy without inventing data.
printf '{"version":3,"rules":[{"ip_cidr":["192.0.2.0/24"]}]}\n' > "$dir/resources/china_ip4.json"
fi
if grep -q "__RULESET_DIR__" "$fixture"; then
# The fixture needs a real local rule-set on disk. The path must
# live under /tmp/homeproxy_* (validateHomeProxyPath() in
@@ -277,6 +290,34 @@ if ! grep -q '"detour": "main-out"' "$WORK/client/run/sing-box-c.json"; then
FAILED=1
fi
# The mainland split must be decided from the same list the firewall renders
# its nft set from. geoip-cn.srs (upstream) and china_ip4.txt (ours) disagree
# on real ranges - the bundled list carries 8.152.0.0/13, geoip-cn.srs does
# not - so a destination in that range passes the firewall as "not mainland"
# and must still be sent direct by the route rule. The local rule-set is
# generated from china_ip4.txt by runtime/china_ip_ruleset.uc and watched with
# fswatch, so a resource update needs no restart.
if ! grep -q '"path": "'"$WORK"'/client/resources/china_ip4.json"' "$WORK/client/run/sing-box-c.json"; then
echo "FAIL: client: the mainland route rule does not read the generated china_ip4 rule-set"
FAILED=1
fi
if ! grep -q '"tag": "china-ip"' "$WORK/client/run/sing-box-c.json"; then
echo "FAIL: client: the local china-ip rule-set is missing from the generated config"
FAILED=1
fi
# ... and it has to be a rule the route layer actually consults, not a
# declaration sing-box loads and never matches.
if ! grep -A2 '"rule_set": "china-ip"' "$WORK/client/run/sing-box-c.json" | grep -q '"outbound": "direct-out"'; then
echo "FAIL: client: no route rule sends the china-ip rule-set to direct-out"
FAILED=1
fi
# The generated file itself must hold the list, or the node would run with a
# split that matches nothing.
if ! grep -q '8\.152\.0\.0/13' "$WORK/client/resources/china_ip4.json"; then
echo "FAIL: client: the generated china_ip4 rule-set does not carry the bundled list"
FAILED=1
fi
# --- the product default proxy mode --------------------------------------
#
# client.uci runs `proxy_mode 'tun'`, where self_mark is empty and no outbound
File diff suppressed because it is too large. Load diff
+8 -8
View File
@@ -3,11 +3,11 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Language: de\n"
"Content-Transfer-Encoding: 8bit\n"
"Last-Translator: Pavel Borecki <pavel.borecki@gmail.com>\n"
"Last-Translator: Ettore Atalan <atalanttore@googlemail.com>\n"
"Language-Team: German <https://hosted.weblate.org/projects/openwrt/"
"luciapplicationsqosify/de/>\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"PO-Revision-Date: 2026-09-24 06:06+0000\n"
"PO-Revision-Date: 2026-09-28 18:23+0000\n"
"X-Generator: Weblate 2026.10.dev0\n"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:882
@@ -19,8 +19,8 @@ msgstr[1] ""
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:943
msgid "%d rule"
msgid_plural "%d rules"
msgstr[0] ""
msgstr[1] ""
msgstr[0] "%d Regel"
msgstr[1] "%d Regeln"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:942
msgid "%d section"
@@ -30,7 +30,7 @@ msgstr[1] ""
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:682
msgid "%s (current)"
msgstr ""
msgstr "%s (aktuell)"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:700
msgid "%s (from bandwidth)"
@@ -1369,15 +1369,15 @@ msgstr ""
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:1541
msgid "total"
msgstr ""
msgstr "gesamt"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:1160
msgid "value"
msgstr ""
msgstr "Wert"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:902
msgid "yes"
msgstr ""
msgstr "ja"
#~ msgid ""
#~ "DSCP codepoints: CS0–CS7, AF11–AF43, EF, VA, NQB, LE, DF. Any dscp_* "
File diff suppressed because it is too large. Load diff
+34 -34
View File
@@ -7,8 +7,8 @@ msgstr ""
"Language-Team: Korean <https://hosted.weblate.org/projects/openwrt/"
"luciapplicationsqosify/ko/>\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"PO-Revision-Date: 2026-08-17 17:59+0000\n"
"X-Generator: Weblate 2026.9.dev0\n"
"PO-Revision-Date: 2026-09-29 17:59+0000\n"
"X-Generator: Weblate 2026.10.dev0\n"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:882
msgid "%d interface"
@@ -208,7 +208,7 @@ msgstr "사용 가능"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:1196
msgid "Backup"
msgstr ""
msgstr "백업"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:76
msgid ""
@@ -231,7 +231,7 @@ msgstr ""
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:89
msgid "CAKE egress options"
msgstr ""
msgstr "CAKE 이그레스 옵션"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:739
msgid "CAKE egress options, space separated."
@@ -239,7 +239,7 @@ msgstr ""
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:88
msgid "CAKE ingress options"
msgstr ""
msgstr "CAKE 인그레스 옵션"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:738
msgid "CAKE ingress options, space separated."
@@ -247,7 +247,7 @@ msgstr ""
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:90
msgid "CAKE options for ingress + egress"
msgstr ""
msgstr "인그레스 및 이그레스 CAKE 옵션"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:83
msgid ""
@@ -261,7 +261,7 @@ msgstr ""
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:564
msgid "Cancel"
msgstr ""
msgstr "취소"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:1214
msgid "Check Devices"
@@ -269,7 +269,7 @@ msgstr ""
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:1193
msgid "Choose file…"
msgstr ""
msgstr "파일 선택…"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:1114
msgid "Classes"
@@ -304,7 +304,7 @@ msgstr ""
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:740
msgid "Common CAKE options"
msgstr ""
msgstr "공통 CAKE 옵션"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:604
msgid "Config"
@@ -329,7 +329,7 @@ msgstr "설정: %s"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:566
msgid "Continue"
msgstr ""
msgstr "계속"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:1245
msgid "Could not read %s: %s"
@@ -416,7 +416,7 @@ msgstr ""
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:718
msgid "Device"
msgstr ""
msgstr "장치"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:1711
msgid "Device check done."
@@ -447,7 +447,7 @@ msgstr "다운로드"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:720
msgid "Download bandwidth"
msgstr ""
msgstr "다운로드 대역폭"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:724
msgid "Download shaping"
@@ -466,7 +466,7 @@ msgstr ""
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:739
msgid "Egress CAKE options"
msgstr ""
msgstr "이그레스 CAKE 옵션"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:2020
msgid "Empty file"
@@ -492,7 +492,7 @@ msgstr ""
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:81
msgid "Enable CAKE host isolation"
msgstr ""
msgstr "CAKE 호스트 격리 활성화"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:78
msgid "Enable egress shaping"
@@ -575,7 +575,7 @@ msgstr "오류:"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:1198
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:1205
msgid "File"
msgstr ""
msgstr "파일"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:1832
msgid "File changed on disk"
@@ -599,7 +599,7 @@ msgstr "LuCI 앱 qosify에 접근 권한 부여"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:728
msgid "Host isolation"
msgstr ""
msgstr "호스트 격리"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:1156
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:1169
@@ -613,11 +613,11 @@ msgstr ""
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:738
msgid "Ingress CAKE options"
msgstr ""
msgstr "인그레스 CAKE 옵션"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:718
msgid "Interface"
msgstr ""
msgstr "인터페이스"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:741
msgid "Invalid CAKE options can stop qosify starting."
@@ -808,7 +808,7 @@ msgstr ""
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:721
msgid "Queueing mode"
msgstr ""
msgstr "대기열 관리 모드"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:1163
msgid "Quick Add"
@@ -917,7 +917,7 @@ msgstr "기본값 복원 중..."
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:605
msgid "Rules"
msgstr ""
msgstr "규칙"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:1976
msgid "Rules saved"
@@ -994,7 +994,7 @@ msgstr ""
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:663
msgid "Service"
msgstr ""
msgstr "서비스"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:1745
msgid "Service action failed: %s"
@@ -1012,7 +1012,7 @@ msgstr ""
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:941
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:1198
msgid "Size"
msgstr ""
msgstr "크기"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:73
msgid "Skip this section"
@@ -1085,7 +1085,7 @@ msgstr ""
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:866
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:925
msgid "Unknown"
msgstr ""
msgstr "알 수 없음"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:435
msgid "Unknown class/DSCP target: %s"
@@ -1105,19 +1105,19 @@ msgstr ""
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:1205
msgid "Upload"
msgstr ""
msgstr "업로드"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:1210
msgid "Upload & Apply"
msgstr ""
msgstr "업로드 및 적용"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:2009
msgid "Upload and apply"
msgstr ""
msgstr "업로드 및 적용"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:719
msgid "Upload bandwidth"
msgstr ""
msgstr "업로드 대역폭"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:2061
msgid "Upload error: %s"
@@ -1177,7 +1177,7 @@ msgstr ""
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:901
msgid "active"
msgstr ""
msgstr "활성"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:1767
msgid ""
@@ -1199,11 +1199,11 @@ msgstr ""
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:900
msgid "device %s"
msgstr ""
msgstr "장치 %s"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:902
msgid "device: %s, ingress: %s, egress: %s"
msgstr ""
msgstr "장치: %s, 인그레스: %s, 이그레스: %s"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:289
msgid ""
@@ -1231,7 +1231,7 @@ msgstr ""
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:901
msgid "inactive"
msgstr ""
msgstr "비활성"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:1109
msgid "ingress %s, egress %s"
@@ -1243,7 +1243,7 @@ msgstr ""
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:900
msgid "interface %s"
msgstr ""
msgstr "인터페이스 %s"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:1155
msgid "match"
@@ -1347,11 +1347,11 @@ msgstr ""
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:955
msgid "section name"
msgstr ""
msgstr "섹션 이름"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:953
msgid "section type"
msgstr ""
msgstr "섹션 유형"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:1529
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:1533
File diff suppressed because it is too large. Load diff
+12 -9
View File
@@ -3,9 +3,12 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Language: pt\n"
"Content-Transfer-Encoding: 8bit\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Last-Translator: Pavel Borecki <pavel.borecki@gmail.com>\n"
"Language-Team: Portuguese <https://hosted.weblate.org/projects/openwrt/"
"luciapplicationsqosify/pt/>\n"
"Plural-Forms: nplurals=2; plural=n > 1;\n"
"PO-Revision-Date: 2026-09-29 12:10+0000\n"
"X-Generator: Weblate 2026.10.dev0\n"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:882
msgid "%d interface"
@@ -204,7 +207,7 @@ msgstr ""
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:883
msgid "Available"
msgstr ""
msgstr "Disponível"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:1196
msgid "Backup"
@@ -261,7 +264,7 @@ msgstr ""
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:564
msgid "Cancel"
msgstr ""
msgstr "Cancelar"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:1214
msgid "Check Devices"
@@ -416,7 +419,7 @@ msgstr ""
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:718
msgid "Device"
msgstr ""
msgstr "Dispositivo"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:1711
msgid "Device check done."
@@ -430,7 +433,7 @@ msgstr ""
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:881
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:930
msgid "Disabled"
msgstr ""
msgstr "Desativado"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:75
msgid "Downlink bandwidth (same format as tc)"
@@ -505,7 +508,7 @@ msgstr ""
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:881
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:930
msgid "Enabled"
msgstr ""
msgstr "Ativado"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:858
msgid "Enabled — Not Running"
@@ -1012,7 +1015,7 @@ msgstr ""
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:941
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:1198
msgid "Size"
msgstr ""
msgstr "Tamanho"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:73
msgid "Skip this section"
@@ -1324,7 +1327,7 @@ msgstr ""
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:1253
msgid "qosify-status"
msgstr ""
msgstr "qosify-status"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:1682
msgid "qosify-status returned no output."
+8 -8
View File
@@ -3,12 +3,12 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Language: pt_BR\n"
"Content-Transfer-Encoding: 8bit\n"
"Last-Translator: Martin Olmer <m_iv@seznam.cz>\n"
"Last-Translator: Pavel Borecki <pavel.borecki@gmail.com>\n"
"Language-Team: Portuguese (Brazil) <https://hosted.weblate.org/projects/"
"openwrt/luciapplicationsqosify/pt_BR/>\n"
"Plural-Forms: nplurals=2; plural=n > 1;\n"
"PO-Revision-Date: 2026-09-02 07:51+0000\n"
"X-Generator: Weblate 2026.9.dev0\n"
"PO-Revision-Date: 2026-09-29 17:59+0000\n"
"X-Generator: Weblate 2026.10.dev0\n"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:882
msgid "%d interface"
@@ -207,7 +207,7 @@ msgstr ""
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:883
msgid "Available"
msgstr ""
msgstr "Disponível"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:1196
msgid "Backup"
@@ -419,7 +419,7 @@ msgstr ""
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:718
msgid "Device"
msgstr ""
msgstr "Dispositivo"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:1711
msgid "Device check done."
@@ -1015,7 +1015,7 @@ msgstr ""
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:941
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:1198
msgid "Size"
msgstr ""
msgstr "Tamanho"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:73
msgid "Skip this section"
@@ -1303,7 +1303,7 @@ msgstr ""
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:595
#: applications/luci-app-qosify/root/usr/share/luci/menu.d/luci-app-qosify.json:3
msgid "qosify"
msgstr ""
msgstr "qosify"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:847
msgid ""
@@ -1327,7 +1327,7 @@ msgstr ""
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:1253
msgid "qosify-status"
msgstr ""
msgstr "qosify-status"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:1682
msgid "qosify-status returned no output."
+8 -8
View File
@@ -9,22 +9,22 @@ msgstr ""
"luciapplicationsqosify/ru/>\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
"PO-Revision-Date: 2026-08-28 22:28+0000\n"
"X-Generator: Weblate 2026.9.dev0\n"
"PO-Revision-Date: 2026-09-28 18:23+0000\n"
"X-Generator: Weblate 2026.10.dev0\n"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:882
msgid "%d interface"
msgid_plural "%d interfaces"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
msgstr[0] "%d интерфейс"
msgstr[1] "%d интерфейса"
msgstr[2] "%d интерфейсов"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:943
msgid "%d rule"
msgid_plural "%d rules"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
msgstr[0] "%d правило"
msgstr[1] "%d правила"
msgstr[2] "%d правил"
#: applications/luci-app-qosify/htdocs/luci-static/resources/view/qosify/main.js:942
msgid "%d section"
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -9,7 +9,7 @@ LUCI_PKGARCH:=all
PKG_LICENSE:=Apache-2.0
PKG_VERSION:=1.0
PKG_RELEASE:=1
PKG_RELEASE:=2
PKG_MAINTAINER:=Meng Zhuo <mengzhuo@iscas.ac.cn>
include $(TOPDIR)/feeds/luci/luci.mk
+6 -3
View File
@@ -3,9 +3,12 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Language: pt\n"
"Content-Transfer-Encoding: 8bit\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Last-Translator: Pavel Borecki <pavel.borecki@gmail.com>\n"
"Language-Team: Portuguese <https://hosted.weblate.org/projects/openwrt/"
"luciapplicationssfp-info/pt/>\n"
"Plural-Forms: nplurals=2; plural=n > 1;\n"
"PO-Revision-Date: 2026-09-29 12:10+0000\n"
"X-Generator: Weblate 2026.10.dev0\n"
#: applications/luci-app-sfp-info/htdocs/luci-static/resources/view/sfp-info/main.js:68
msgid "Bias Current"
@@ -97,7 +100,7 @@ msgstr ""
#: applications/luci-app-sfp-info/htdocs/luci-static/resources/view/sfp-info/main.js:63
msgid "Type"
msgstr ""
msgstr "Tipo"
#: applications/luci-app-sfp-info/htdocs/luci-static/resources/view/sfp-info/main.js:62
msgid "Vendor"
+93
View File
@@ -2,6 +2,99 @@
All notable changes to this project will be documented in this file.
## [2.6.0-r1] - 协议覆盖补全与导入/输出保真修复
本轮起因:用户报告「添加本地订阅 → 表单导入」的「类型」下拉框协议不全,
与 README 描述不符。审计后确认问题存在,且**「文本导入」存在更严重的同类缺口**,
并顺带查出若干静默数据丢失 / 非法输出问题。所有修改均经代码或测试确认,
未做任何推测性改动。
### 协议覆盖(本次报告的主问题)
- **表单导入缺 `hysteria`(v1) 与 `socks`**:`nodeform.js` 的下拉框只有 8 个协议,
而 `node.lua` 的 `M.PROTOS`、节点页协议筛选、规则 `proto_filter` 都认 10 个。
用户因此无法用表单录入这两类节点。
- **文本导入(URI)缺 `hysteria`(v1) 与 `socks`/`socks5`**,且这是**导出→导入回环断裂**:
`output_uri.to_share_uri` 会生成 `hysteria://` 与 `socks5://` 链接,但 `parser.parse_uri`
对二者一律返回 `unsupported proto`。含这两类节点的订阅文本会被**整行静默丢弃**。
已新增 `parse_hysteria` / `parse_socks` 并补齐 `SUPPORTED` 分发表。
- **简易 YAML 兜底解析的协议表漂移**:该处自维护一份映射,缺
`hysteria2` / `hysteria` / `tuic` / `wireguard`,且 `socks5` 未归一,更严重的是
对认不出的 `type` 用 `or "vmess"` 兜底 —— 一份 sing-box YAML 里的
hysteria2/tuic/wireguard 出站会变成数个**字段全错的假 vmess 节点**(静默数据损坏)。
现改为复用 `parser_clash_yaml.TYPE_MAP`(单一事实来源),未知协议直接丢弃。
- **简易 YAML 兜底解析的 `outbounds:` 分支从不生效**:该分支同时接受 Clash 的
`proxies:` 与 sing-box 的 `outbounds:` 段名,却只读 Clash 的 `port` / `name`,
而 sing-box 用 `server_port` / `tag`。结果是 sing-box YAML 出站**全部被静默丢弃**
(识别了段名却解析出 0 个节点)。现按 `parser_json_config.parse_singbox_json`
读取的键名逐字补齐别名:`server_port` / `tag` / `auth_str` / `auth` /
`tls.server_name` / `tls.insecure` / `security`(vmess 加密方式) / `network`。
### 归一化
- **`socks5` → `socks` 归一**:两者是同一协议的不同写法(Clash 写 `socks5`、
sing-box 写 `socks`、分享链接写 `socks5://`)。输出模块本来就同时认两种写法,
但 `node.filter` 与 `rules.proto_filter` 是精确比较,两种写法互相看不见 ——
节点页按协议筛选与规则过滤都会漏。现统一为规范名 `socks`。
### 输出保真
- **clashmeta:hysteria(v1) 的 SNI 丢失**。此前统一输出 `servername`,而 mihomo 的
hysteria 系列没有该字段(写 `servername` 会被忽略),SNI 丢失会导致客户端
以 IP 校验证书、握手直接失败。现按协议区分:hysteria / hysteria2 输出 `sni`,
vmess / vless / trojan 仍输出 `servername`。同时补上 v1 的 `obfs`(普通字符串)。
- **clashmeta:socks5 的 `password` 被输出两次**(通用字段段 + 协议专属段),
在 YAML 里形成重复键,属非法/歧义配置。现协议专属段只补 `username`。
- **clashmeta / singbox / surge:`http` 的用户名丢失**,只输出密码。现按上游文档
补齐 `username`(mihomo 的 http / socks5 用 `username` + `password`;
Surge 家族用 `username=` / `password=` 具名参数)。
- **singbox:hysteria(v1) 字段名错误**。此前输出 `password` 与
`obfs = { type, password }`,但 sing-box 的 hysteria 出站认证字段是 `auth_str`,
`obfs` 是**普通字符串**(对象形式是 hysteria2 的 salamander 专属)。
该修正与本项目 `parser_json_config` 自身的回读逻辑一致
(其按 `outbound.password or outbound.auth_str or outbound.auth` 读取)。
- **`output_uri` / `output_clash_meta`:hysteria(v1) 不应出现 `obfs-password`**。
该字段是 hysteria2 的 salamander 专属,写到 v1 上是非法参数/非法键。
### 表单
- **`core.FORM_KEYS` 缺 `username`**:`merge_form_node` 先按 `FORM_KEYS` 清空原节点
再套用提交值,不在表里的字段会保留旧值 —— 用户在表单里**清空用户名也删不掉**
(`collectNodes` 会略过空值)。已补入。
### 已知限制(本轮不修,均有明确原因)
- **混合格式文本导入不支持**。README 称可粘贴「YAML / URI / JSON / wg-quick `.conf` 混合」
文本,但 `parse_local → parse → detect` 只识别**一种**格式,其余部分被静默丢弃
(已实测:「URI + WG conf」只剩 WG 节点;「URI + JSON」只剩 URI 节点)。
这是**功能缺失**而非小缺陷,需要重新设计 parser 的分段架构(规格 §22 明确警告
不要直接采用未经验证的逐行算法),故留待专门版本处理。
- **sing-box 的 hysteria(v1) 出站还要求 `up` / `down`(带宽)**,本项目的节点模型
不承载该字段。凭空填默认值属于猜测,故不输出;用户需在自己的配置里补上。
- **hysteria(v1) 的上游 URI 规范未能核实**(上游文档站点持续 404,无法取得权威定义)。
因此 `parse_hysteria` 只保证解析本项目 `output_uri` 自身生成的链接形态
(回环已验证),认不出的查询参数一律忽略而不报错,不臆造参数语义。
- **`http` 不加入任何 UI 协议列表**:规格 §25 的权威协议表恰好是 10 个协议、不含 `http`。
它可由 Clash YAML / JSON 配置导入并正常导出(凭据已修),但不作为表单可选项。
- **`parser_yaml.lua` 为死代码**(`parser.lua` 顶部 require 后从未调用),
其中留有同样未归一的 `socks5` 映射,仅记录,不影响运行。
- **`parse_tuic` / `parse_hysteria2` 按第一个 `@` 切分 userinfo**,与 `parse_socks`
修复前同类(密码含裸 `@` 会被切坏)。本轮只修了 socks,其余留作观察项。
### 测试
- 新增 `tests/protocol_coverage_test.lua`(110 项断言),覆盖上述全部修复:
hysteria / socks 的 URI 导入与导出→导入回环、混合文本不再丢行、
兜底解析不再造假日 vmess、sing-box YAML 出站别名、`socks5` 归一后筛选/规则命中、
clashmeta / singbox / surge 的输出字段、`FORM_KEYS` 的 `username`。
- 全量测试:`tests/*_test.lua` 逐文件运行,**0 个失败文件**;
`tests/cron_result_test.sh` rc=0。
### 版本
- 版本号 2.5.1-r1 → 2.6.0-r1(协议覆盖为功能增强,走 minor);
`README.md` / `README.en.md` / `docs/INSTALL.md` 同步。
## [2.5.1-r1] - 安全加固与数据保真修复
本轮为缺陷修复版本,重点是**命令注入 / SSRF 绕过 / 静默数据丢失**三类问题。
+2 -2
View File
@@ -9,8 +9,8 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-substore
# 版本约定:每次提交 PKG_RELEASE +1(2.1.2-r1 ~ r9);
# 达到 r10 时 PKG_VERSION 末位 +1(2.1.2 -> 2.1.3),PKG_RELEASE 重置为 1。
PKG_VERSION:=2.5.1
PKG_RELEASE:=10
PKG_VERSION:=2.6.0
PKG_RELEASE:=11
LUCI_DEPENDS:=+luci-lua-runtime +luci-compat
+11 -8
View File
@@ -23,17 +23,20 @@ group them, then re-emit them in a format your client can consume.
(optionally with keyword include / exclude and dedup rules) into one combination that
has its own name, token and subscription link; combinations are recomputed automatically
when a source updates
- **Local subscription**: no URL needed — paste node text (mixed YAML / URI / JSON /
- **Local subscription**: no URL needed — paste node text (YAML / URI / JSON /
wg-quick `.conf`) or enter nodes one by one via a form whose fields adapt to the
selected protocol
(vmess / vless / ss / ssr / trojan / hysteria2 / tuic / wireguard / socks)
selected protocol. A paste is detected as **one** format (YAML / URI / JSON /
`.conf`); mixing several formats in one paste is not supported
(vmess / vless / ss / ssr / trojan / hysteria / hysteria2 / tuic / wireguard / socks)
**Input parsing**
- Subscription formats: URI lists, Base64, JSON, Clash YAML, sing-box JSON,
V2Ray / Xray JSON, Surge / Surfboard / Loon / Quantumult X configs, LAN
subscription links, and wg-quick / AmneziaWG `.conf`
- Node protocols: `vmess` / `vless` / `trojan` / `shadowsocks` / `ssr` / `hysteria2` /
`tuic` / `socks` (and more)
- Node protocols: `vmess` / `vless` / `trojan` / `shadowsocks` / `ssr` / `hysteria` /
`hysteria2` / `tuic` / `wireguard` / `socks` (plus `http`, which can be imported from
Clash YAML / JSON configs and exported, but is not offered in the form importer —
it is not served as a proxy node)
- **WireGuard / AmneziaWG**: full field import and export (`private-key` / `public-key` /
`pre-shared-key` / `ip` / `ipv6` / `allowed-ips` / `reserved` / `persistent-keepalive` /
`listen-port` / `mtu` / `dns`) plus the `amnezia-wg-option` sub-block
@@ -93,18 +96,18 @@ group them, then re-emit them in a format your client can consume.
## Installation
> The version in the package name must match `PKG_VERSION` / `PKG_RELEASE` in the
> [Makefile](Makefile) (currently `2.5.1-r1`).
> [Makefile](Makefile) (currently `2.6.0-r1`).
opkg (OpenWrt / ImmortalWrt 24.10 and earlier):
```bash
opkg install luci-app-substore-2.5.1-r1.ipk
opkg install luci-app-substore-2.6.0-r1.ipk
```
apk (OpenWrt / ImmortalWrt 25.12+):
```bash
apk add --allow-untrusted luci-app-substore-2.5.1-r1.apk
apk add --allow-untrusted luci-app-substore-2.6.0-r1.apk
```
Then open LuCI: **Services → Subscriptions**.
+9 -6
View File
@@ -21,14 +21,17 @@
用于订阅源直连失败时
- **组合订阅**:勾选任意子集的现有订阅(可叠加关键词包含 / 排除、去重规则)合并成一个组合,
拥有独立名称、token 与订阅链接;源订阅更新后组合自动重算
- **本地订阅**:不填 URL,直接粘贴节点文本(YAML / URI / JSON / wg-quick `.conf` 混合)导入,
- **本地订阅**:不填 URL,直接粘贴节点文本(YAML / URI / JSON / wg-quick `.conf`)导入,
一次粘贴按**单一格式**识别(自动判定 YAML / URI / JSON / `.conf`,不支持多格式混排),
或用「表单导入」按协议动态字段逐条录入节点
(vmess / vless / ss / ssr / trojan / hysteria2 / tuic / wireguard / socks)
(vmess / vless / ss / ssr / trojan / hysteria / hysteria2 / tuic / wireguard / socks)
**输入解析**
- 订阅格式:URI 列表、Base64、JSON、Clash YAML、sing-box JSON、V2Ray / Xray JSON、
Surge / Surfboard / Loon / Quantumult X 配置、局域网订阅链接、wg-quick / AmneziaWG `.conf`
- 节点协议:`vmess` / `vless` / `trojan` / `shadowsocks` / `ssr` / `hysteria2` / `tuic` / `socks`(及更多)
- 节点协议:`vmess` / `vless` / `trojan` / `shadowsocks` / `ssr` / `hysteria` / `hysteria2` /
`tuic` / `wireguard` / `socks`(及 `http`,可由 Clash YAML / JSON 配置导入并导出,
但不在表单导入的可选协议内 —— 它不作为代理节点对外提供订阅)
- **WireGuard / AmneziaWG**:导入并导出完整字段(`private-key` / `public-key` / `pre-shared-key` /
`ip` / `ipv6` / `allowed-ips` / `reserved` / `persistent-keepalive` / `listen-port` / `mtu` / `dns`),
以及 `amnezia-wg-option` 子块(Jc / Jmin / Jmax / S1–S4 / H1–H4 / I1–I5 / J1–J3 / Itime);
@@ -80,18 +83,18 @@
## 安装
> 包名中的版本号必须与 [Makefile](Makefile) 的 `PKG_VERSION` / `PKG_RELEASE` 保持一致
> (当前 `2.5.1-r1`)。
> (当前 `2.6.0-r1`)。
opkg(OpenWrt / ImmortalWrt 24.10 及更早):
```bash
opkg install luci-app-substore-2.5.1-r1.ipk
opkg install luci-app-substore-2.6.0-r1.ipk
```
apk(OpenWrt / ImmortalWrt 25.12+):
```bash
apk add --allow-untrusted luci-app-substore-2.5.1-r1.apk
apk add --allow-untrusted luci-app-substore-2.6.0-r1.apk
```
然后在 LuCI 菜单打开:**服务 → 订阅**。
+3 -3
View File
@@ -3,18 +3,18 @@
> NOTE: stage-0 skeleton has not yet been installed/verified on a target device.
> This document will be updated once installation is validated.
>
> 包名中的版本号需与 Makefile 的 `PKG_VERSION` / `PKG_RELEASE` 保持同步(当前 2.5.1-r1)。
> 包名中的版本号需与 Makefile 的 `PKG_VERSION` / `PKG_RELEASE` 保持同步(当前 2.6.0-r1)。
## Install (opkg — OpenWrt / ImmortalWrt 24.10 及更早)
```bash
opkg install luci-app-substore-2.5.1-r1.ipk
opkg install luci-app-substore-2.6.0-r1.ipk
```
## Install (apk — OpenWrt / ImmortalWrt 25.12+)
```bash
apk add --allow-untrusted luci-app-substore-2.5.1-r1.apk
apk add --allow-untrusted luci-app-substore-2.6.0-r1.apk
```
Refresh LuCI:
@@ -133,7 +133,8 @@ var FIELD_LABELS = {
"listen-port": "Listen Port",
"mtu": "MTU",
"dns": "DNS",
"amnezia-wg-option": "Amnezia WG Option"
"amnezia-wg-option": "Amnezia WG Option",
"username": "Username"
};
var NODEFORM_I18N = {
"node": "<%:Node%>",
@@ -76,7 +76,8 @@ var FIELD_LABELS = {
"listen-port": "Listen Port",
"mtu": "MTU",
"dns": "DNS",
"amnezia-wg-option": "Amnezia WG Option"
"amnezia-wg-option": "Amnezia WG Option",
"username": "Username"
};
var NODEFORM_I18N = {
"node": "<%:Node%>",
@@ -7,7 +7,7 @@ local parser = require("substore.parser")
local M = {}
M.version = "2.5.1"
M.version = "2.6.0"
M.DATA_DIR = "/etc/substore"
M.LIST_FILE = M.DATA_DIR .. "/subscriptions.json"
M.NODES_DIR = M.DATA_DIR .. "/nodes"
@@ -25,12 +25,21 @@ function M.ensure_dirs()
util.ensure_dir(M.NODES_DIR)
end
-- 读取订阅列表,返回 seq, items, err。
--
-- 必须区分「文件不存在 / 为空」(合法的空列表)与「文件存在但解析失败」
-- (内容损坏、被截断、磁盘错误)。后者若按空列表处理,后果是灾难性的:
-- M.add / M.add_local / M.add_combo 会在这个空表上追加一条然后整表写回,
-- 把用户已有的全部订阅抹掉,并且 _seq 归零后重新发出 s00000001 这种
-- 已经用过的 ID。因此损坏时必须显式报错,由调用方拒绝写入。
local function load()
M.ensure_dirs()
local raw = util.read_file(M.LIST_FILE)
if not raw or raw == "" then return 0, {} end
local data = util.json_decode(raw)
if type(data) ~= "table" then return 0, {} end
if type(data) ~= "table" then
return 0, {}, "订阅列表文件已损坏,无法解析:" .. M.LIST_FILE
end
local seq = tonumber(data._seq) or 0
local items = type(data.items) == "table" and data.items or {}
return seq, items
@@ -41,8 +50,11 @@ local function save(seq, items)
return util.atomic_write(M.LIST_FILE, util.json_encode({ _seq = seq, items = items }))
end
-- 返回 arr, err。err 非空表示列表文件已损坏(此时 arr 为空)。
-- 追加第二个返回值是向后兼容的:调用方普遍写成 ipairs(core.list()) 或
-- local items = core.list(),都只取第一个值。
function M.list()
local _, items = load()
local _, items, lerr = load()
local arr = {}
for id, meta in pairs(items) do
local m = {}
@@ -51,7 +63,7 @@ function M.list()
arr[#arr + 1] = m
end
table.sort(arr, function(a, b) return (a.name or "") < (b.name or "") end)
return arr
return arr, lerr
end
function M.get(id)
@@ -70,7 +82,8 @@ function M.add(name, url, opts)
url = util.trim(url or "")
opts = opts or {}
if name == "" or url == "" then return nil, "名称/URL 不能为空" end
local seq, items = load()
local seq, items, lerr = load()
if lerr then return nil, lerr end
seq = seq + 1
local id = string.format("s%08x", seq)
local cron_time = util.trim(opts.cron_time or "")
@@ -103,7 +116,8 @@ function M.add_local(name, raw_content, local_mode, opts)
local_mode = local_mode or "text"
opts = opts or {}
if name == "" or raw_content == "" then return nil, "名称/内容 不能为空" end
local seq, items = load()
local seq, items, lerr = load()
if lerr then return nil, lerr end
seq = seq + 1
local id = string.format("s%08x", seq)
items[id] = {
@@ -131,7 +145,8 @@ end
-- 获取订阅的下载 token;若不存在则生成并持久化
function M.ensure_token(id)
if not id_is_valid(id) then return nil end
local seq, items = load()
local seq, items, lerr = load()
if lerr then return nil end
local meta = items[id]
if not meta then return nil end
if not meta.token or meta.token == "" then
@@ -178,7 +193,8 @@ M.CLEAR = setmetatable({}, { __tostring = function() return "substore.CLEAR" end
function M.save_meta(id, patch)
if not id_is_valid(id) then return false, "非法 ID" end
local seq, items = load()
local seq, items, lerr = load()
if lerr then return false, lerr end
local meta = items[id]
if not meta then return false, "订阅不存在" end
for k, v in pairs(patch or {}) do
@@ -189,7 +205,8 @@ end
function M.remove(id)
if not id_is_valid(id) then return false end
local seq, items = load()
local seq, items, lerr = load()
if lerr then return false, lerr end
if not items[id] then return false end
items[id] = nil
save(seq, items)
@@ -363,6 +380,10 @@ local FORM_KEYS = {
reserved = true, ["persistent-keepalive"] = true, persistent_keepalive = true,
["listen-port"] = true, listen_port = true,
mtu = true, dns = true, ["amnezia-wg-option"] = true,
-- socks / http 的用户名。必须在表里:下面的 merge_form_node 先按 FORM_KEYS
-- 清空原节点再套用提交值,不在表里的字段会保留旧值——用户在表单里清空用户名
-- 也删不掉(collectNodes 会略过空值),等于改不动。
username = true,
}
-- 合并表单节点到原节点:表单字段整体替换(可清空),非表单字段保留
@@ -441,7 +462,8 @@ function M.add_combo(name, sources, opts)
end
end
if #srcs == 0 then return nil, "请选择至少一个订阅" end
local seq, items = load()
local seq, items, lerr = load()
if lerr then return nil, lerr end
seq = seq + 1
local id = string.format("s%08x", seq)
items[id] = {
@@ -37,6 +37,11 @@ end
function M.normalize(node)
if type(node) ~= "table" then return node end
if node.proto == "ss" then node.proto = "shadowsocks" end
-- socks5 与 socks 是同一个协议,只是各客户端写法不同(Clash 写 socks5、
-- sing-box 写 socks、分享链接写 socks5://)。统一成 socks:
-- 各输出模块本来就同时认这两种写法,但 node.filter / rules.proto_filter
-- 是精确比较,两种写法会互相看不见——节点页按协议筛选和规则过滤都会漏。
if node.proto == "socks5" then node.proto = "socks" end
local d = DEFAULTS[node.proto] or {}
for k, v in pairs(d) do
if node[k] == nil then node[k] = v end
@@ -5,9 +5,15 @@ local M = {}
local function esc_yaml(s)
s = tostring(s or "")
s = s:gsub("\\", "\\\\"):gsub("\"", "\\\""):gsub("\n", "\\n")
-- 注意:这里必须用 Lua 模式(不能传 plain=true),否则整串被当作字面量、永不匹配
if s:find("[ :#{}%[%],&*?|>'\"%@`]") or s:match("^[-?]*:") then
-- 是否需要双引号必须在转义之前判定:转义之后的 \t / \r 只剩「反斜杠 + 字母」,
-- 落在 plain scalar 里会被 YAML 当成两个字面字符而不是制表符/回车。含任何控制
-- 字符(\n \r \t 等)时必须加引号 —— 未加引号的换行/回车会直接破坏文档结构。
local need_quote = s:find("%c") ~= nil
-- 注意:这里必须用 Lua 模式(不能传 plain=true),否则整串被当作字面量、永不匹配
or s:find("[ :#{}%[%],&*?|>'\"%@`]") ~= nil
or s:match("^[-?]*:") ~= nil
s = s:gsub("\\", "\\\\"):gsub("\"", "\\\""):gsub("\n", "\\n"):gsub("\r", "\\r"):gsub("\t", "\\t")
if need_quote then
return '"' .. s .. '"'
end
return s
@@ -106,11 +112,18 @@ local function format_node(node)
end
-- servername / sni
if node.sni then
lines[#lines + 1] = " servername: " .. esc_yaml(node.sni)
end
if node.servername then
lines[#lines + 1] = " servername: " .. esc_yaml(node.servername)
-- vmess / vless / trojan 在 mihomo 里用 servername;hysteria / hysteria2 用 sni
-- (已对照上游文档确认:hysteria 系列没有 servername 字段,写了会被忽略,
-- 结果 SNI 丢失 → 客户端拿 IP 校验证书直接握手失败)。hysteria 系列的 sni
-- 由下面的协议专属分支输出。
-- sni 与 servername 是同一个字段的两种写法,必须只输出一个键:Clash YAML 导入
-- 会同时填上两者(先 sni = p.sni or p.servername,随后的字段保留循环又原样复制了
-- servername),各写一行会让 YAML 出现重复键 —— 严格解析器直接报错,宽松解析器
-- 则取最后一个,行为不确定。
local is_hysteria = (ctype == "hysteria" or ctype == "hysteria2")
local tls_name = node.sni or node.servername
if tls_name and not is_hysteria then
lines[#lines + 1] = " servername: " .. esc_yaml(tls_name)
end
-- 特殊字段
@@ -152,7 +165,7 @@ local function format_node(node)
if node.sni then
lines[#lines + 1] = " sni: " .. esc_yaml(node.sni)
end
-- 混淆(salamander)
-- 混淆(salamander):hysteria2 的 obfs 是 { type, password } 两段
if node.obfs and node.obfs ~= "" and node.obfs ~= "plain" then
lines[#lines + 1] = " obfs: " .. esc_yaml(node.obfs)
local opw = node["obfs-password"] or node.obfs_password
@@ -162,6 +175,17 @@ local function format_node(node)
end
end
if ctype == "hysteria" then
-- hysteria(v1):obfs 只是普通字符串,**没有** obfs-password
-- (obfs-password 是 hysteria2 的 salamander 专属字段,写到 v1 上是非法键)
if node.sni then
lines[#lines + 1] = " sni: " .. esc_yaml(node.sni)
end
if node.obfs and node.obfs ~= "" and node.obfs ~= "plain" then
lines[#lines + 1] = " obfs: " .. esc_yaml(node.obfs)
end
end
if ctype == "tuic" then
if node["udp-relay-mode"] then
lines[#lines + 1] = " udp-relay-mode: " .. esc_yaml(node["udp-relay-mode"])
@@ -214,13 +238,13 @@ local function format_node(node)
end
end
if ctype == "socks5" then
-- socks5 / http 的认证字段都是 username + password(已对照上游 mihomo 文档确认)。
-- password 已由上面的「协议通用字段」输出,这里只补 username——重复输出会让
-- YAML 里出现两个 password 键,属于非法/歧义配置。
if ctype == "socks5" or ctype == "http" then
if node.username then
lines[#lines + 1] = " username: " .. esc_yaml(node.username)
end
if node.password then
lines[#lines + 1] = " password: " .. esc_yaml(node.password)
end
end
if ctype == "ssr" then
@@ -57,8 +57,17 @@ function M.surge_line(n)
e[#e + 1] = "username=" .. (n.uuid or "")
if n.password then e[#e + 1] = "password=" .. n.password end
if n.sni then e[#e + 1] = "sni=" .. n.sni end
if n.alpn then e[#e + 1] = "alpn=" .. n.alpn end
elseif proto == "socks5" or proto == "socks" then
if n.alpn then
-- alpn 可能是数组:sing-box JSON 与 Clash YAML 的 alpn 列表导入后就是 table,
-- 直接拼接会 "attempt to concatenate a table value" 让整次导出失败
local alpn = n.alpn
if type(alpn) == "table" then alpn = table.concat(alpn, ",") end
if alpn ~= "" then e[#e + 1] = "alpn=" .. alpn end
end
elseif proto == "socks5" or proto == "socks" or proto == "http" then
-- socks5 / http 在 Surge 家族里都用 username= / password= 具名参数
-- (Surge 手册:Name = http, <host>, <port>[, <username>, <password>]
-- "may be given positionally after the port, or as named parameters")
if n.username then
e[#e + 1] = "username=" .. n.username
if n.password then e[#e + 1] = "password=" .. n.password end
@@ -70,7 +79,8 @@ function M.surge_line(n)
local line = (n.name or "") .. " = " .. head
if #e > 0 then line = line .. ", " .. table.concat(e, ", ") end
return line
-- 节点名与各参数值都来自订阅(不可信):含换行会截断本行并伪造出一条新的代理行
return util.one_line(line)
end
-- 收集节点名列表
@@ -112,7 +122,7 @@ local function surge_config(nodes, group_name, supports_ssr)
local select = group_name .. " = select"
for _, nm in ipairs(names) do select = select .. ", " .. nm end
select = select .. ", DIRECT"
out[#out + 1] = select
out[#out + 1] = util.one_line(select)
return table.concat(out, "\n")
end
@@ -172,6 +182,7 @@ function M.to_qx(nodes, options)
local proto = props(n)
local host = (n.server or "") .. ":" .. tostring(n.port or 0)
local tag = n.name or host
local before = #out
if proto == "shadowsocks" then
out[#out + 1] = string.format(
"shadowsocks=%s, method=%s, password=%s, tag=%s",
@@ -199,13 +210,16 @@ function M.to_qx(nodes, options)
host, n.password or "", tag)
if n.sni then out[#out] = out[#out] .. ", tls-host=" .. n.sni end
end
-- 节点名 / sni / path / host 全部来自订阅(不可信),含换行会截断本行
-- 并伪造出一条新的 server_local 行
for i = before + 1, #out do out[i] = util.one_line(out[i]) end
end
out[#out + 1] = ""
out[#out + 1] = "[policy]"
local names = names_of(nodes)
local policy = "static=" .. (options.name or "PROXY") .. ", DIRECT"
for _, nm in ipairs(names) do policy = policy .. ", " .. nm end
out[#out + 1] = policy
out[#out + 1] = util.one_line(policy)
out[#out + 1] = ""
out[#out + 1] = "[server_remote]"
return table.concat(out, "\n")
@@ -33,31 +33,32 @@ local function supported(proto)
end
-- 构建 TLS 字段
--
-- enabled 必须显式写 true:sing-box 的 OutboundTLSOptions.Enabled 是
-- `bool` + `json:"enabled,omitempty"`(option/tls.go),缺省值即 false。
-- 上游 common/tls/client.go 里 `if !options.Enabled { return dialer, nil }`
-- 会直接按明文拨号,hysteria2 / tuic 更会因为 `options.TLS == nil ||
-- !options.TLS.Enabled` 返回 C.ErrTLSRequired 而拒绝启动。只输出
-- server_name / insecure 而不带 enabled 等于没配 TLS。
local function build_tls(n)
local tls
if n.security and n.security ~= "none" then
tls = {}
if n.sni or n.servername then tls.server_name = n.sni or n.servername end
if n.alpn then
if type(n.alpn) == "string" then
local list = {}
for p in n.alpn:gmatch("[^,]+") do list[#list + 1] = p:match("^%s*(.-)%s*$") end
tls.alpn = list
else
tls.alpn = n.alpn
end
if not (n.security and n.security ~= "none") then return nil end
local tls = { enabled = true }
if n.sni or n.servername then tls.server_name = n.sni or n.servername end
if n.alpn then
if type(n.alpn) == "string" then
local list = {}
for p in n.alpn:gmatch("[^,]+") do list[#list + 1] = p:match("^%s*(.-)%s*$") end
tls.alpn = list
else
tls.alpn = n.alpn
end
if n["skip-cert-verify"] ~= nil then
tls.insecure = bool(n["skip-cert-verify"])
elseif n.skip_cert_verify ~= nil then
tls.insecure = bool(n.skip_cert_verify)
end
-- 空表会被 json_encode 编码成 [],而 tls 必须是对象;
-- security=tls 但无 sni/alpn/insecure 时就会走到这里
if next(tls) == nil then return util.JSON_EMPTY_OBJECT end
return tls
end
return nil
if n["skip-cert-verify"] ~= nil then
tls.insecure = bool(n["skip-cert-verify"])
elseif n.skip_cert_verify ~= nil then
tls.insecure = bool(n.skip_cert_verify)
end
return tls
end
-- 构建传输(transport)字段
@@ -112,12 +113,23 @@ function M.to_outbound(n, tag)
elseif stype == "shadowsocks" then
o.method = n.method or n.cipher or "aes-256-gcm"
o.password = n.password or ""
elseif stype == "hysteria2" or stype == "hysteria" then
elseif stype == "hysteria2" then
o.password = n.password or ""
-- 混淆(salamander)
-- 混淆(salamander):hysteria2 的 obfs 是 { type, password } 对象
if n.obfs and n.obfs ~= "" and n.obfs ~= "plain" then
o.obfs = { type = n.obfs, password = n["obfs-password"] or n.obfs_password or "" }
end
elseif stype == "hysteria" then
-- hysteria(v1) 与 hysteria2 在 sing-box 里的字段名并不相同(已对照上游文档确认):
-- * 认证字段是 auth_str,不是 password(parser_json_config 也是按
-- outbound.password or outbound.auth_str or outbound.auth 回读的)
-- * obfs 是普通字符串,不是 { type, password } 对象
-- 注意:sing-box 的 hysteria 出站还要求 up/down(带宽),本项目的节点模型
-- 不承载该字段,需用户在自己的配置里补上(见 CHANGELOG 已知限制)。
o.auth_str = n.password or ""
if n.obfs and n.obfs ~= "" and n.obfs ~= "plain" then
o.obfs = n.obfs
end
elseif stype == "tuic" then
o.uuid = n.uuid or ""
o.password = n.password or ""
@@ -142,7 +154,8 @@ function M.to_outbound(n, tag)
if n["listen-port"] then o.listen_port = tonumber(n["listen-port"]) end
if n.mtu then o.mtu = tonumber(n.mtu) end
if n.dns then o.dns = n.dns end
elseif stype == "socks" then
elseif stype == "socks" or stype == "http" then
-- socks 与 http 出站的认证字段相同(username / password)
if n.username then o.username = n.username end
if n.password then o.password = n.password end
end
@@ -84,11 +84,15 @@ function M.to_share_uri(n)
local q = {}
if n.sni then q[#q + 1] = "sni=" .. url_encode(n.sni) end
if n.insecure ~= nil then q[#q + 1] = "insecure=" .. tostring(n.insecure) end
-- 混淆(salamander):hy2 URI 标准参数 obfs / obfs-password
-- 混淆:hysteria2 是 salamander,URI 参数为 obfs / obfs-password;
-- hysteria(v1) 的 obfs 只是普通字符串,**没有** obfs-password
-- (写到 v1 链接上是非法参数,且会误导下游客户端)
if n.obfs and n.obfs ~= "" and n.obfs ~= "plain" then
q[#q + 1] = "obfs=" .. url_encode(n.obfs)
local opw = n["obfs-password"] or n.obfs_password
if opw and opw ~= "" then q[#q + 1] = "obfs-password=" .. url_encode(opw) end
if proto == "hysteria2" then
local opw = n["obfs-password"] or n.obfs_password
if opw and opw ~= "" then q[#q + 1] = "obfs-password=" .. url_encode(opw) end
end
end
local suffix = #q > 0 and ("?" .. table.concat(q, "&")) or ""
return (proto == "hysteria2" and "hysteria2://" or "hysteria://")
@@ -2,6 +2,8 @@
-- luci-app-substore
-- 与 parser.parse_wireguard_conf 互为逆操作:导入解析 [Interface] / [Peer],此处按同格式写回。
local util = require("substore.util")
local M = {}
-- 仅 wireguard 节点可用 .conf 表达;其余协议无法落到单行/单段,直接丢弃
@@ -156,7 +158,9 @@ function M.generate(nodes, options)
return nil, "该 WireGuard 节点没有私钥 (private-key),无法导出 .conf"
end
local lines = { "# " .. (n.name or ((n.server or "") .. ":" .. tostring(n.port or ""))) }
-- 注释行也必须压成单行:节点名来自订阅(不可信),含换行时会注入出真正的
-- 配置行(例如 "x\n[Interface]"),伪造出一段用户没写过的隧道配置
local lines = { "# " .. util.one_line(n.name or ((n.server or "") .. ":" .. tostring(n.port or ""))) }
for _, line in ipairs(build_section(n)) do lines[#lines + 1] = line end
return table.concat(lines, "\n") .. "\n"
end
@@ -10,9 +10,15 @@ local parser_surge = require("substore.parser_surge")
local M = {}
-- URI 文本导入支持的 scheme。
-- 必须覆盖 output_uri.to_share_uri 能生成的全部 scheme,否则「导出→导入」回环会
-- 丢节点:之前 hysteria / socks 只出不进(导出为 hysteria:// / socks5://,
-- 再导入却报 "unsupported proto"),含这两类节点的订阅文本也会被整行丢弃。
-- socks5 与 socks 都接受,统一归一为 proto="socks"(见 node.normalize)。
local SUPPORTED = {
vmess = true, vless = true, trojan = true, ss = true, ssr = true,
hysteria2 = true, tuic = true, wireguard = true,
hysteria = true, hysteria2 = true, tuic = true, wireguard = true,
socks = true, socks5 = true,
}
local function split_lines(content)
@@ -363,6 +369,88 @@ local function parse_hysteria2(uri, body)
return out
end
-- hysteria://password@host:port/?sni=..&insecure=..&obfs=..#name(v1)
-- 与 hysteria2 的区别(均已对照上游确认):
-- * v1 的 obfs 是普通字符串,没有 obfs-password(obfs-password 是 hysteria2 的 salamander)
-- * 两者在 mihomo / sing-box 里都是 TLS-only,因此同样补 security="tls"
-- 认不出的查询参数一律忽略(不报错),保证第三方生成的链接仍能解析出 server/port/password。
local function parse_hysteria(uri, body)
local name, rest = "", body
local hash = rest:find("#", 1, true)
if hash then
name = util.url_decode(rest:sub(hash + 1))
rest = rest:sub(1, hash - 1)
end
local query = {}
local qpos = rest:find("?", 1, true)
local hp = rest
if qpos then
hp = rest:sub(1, qpos - 1)
for k, v in rest:sub(qpos + 1):gmatch("([^&=]+)=([^&]*)") do
query[k] = util.url_decode(v)
end
end
local at = hp:find("@", 1, true)
if not at then return nil, "bad hysteria" end
local password = util.url_decode(hp:sub(1, at - 1))
local host, port = util.split_hostport(hp:sub(at + 1))
if not host or host == "" then return nil, "bad hysteria" end
local out = node.normalize({
proto = "hysteria", name = name, server = host, port = tonumber(port),
password = password, raw = uri,
})
if query.sni then out.sni = query.sni end
out.security = "tls"
if query.insecure ~= nil then
out.insecure = query.insecure
out["skip-cert-verify"] = not (query.insecure == "0" or query.insecure == "false")
end
if query.obfs and query.obfs ~= "" then out.obfs = query.obfs end
return out
end
-- socks5://user:pass@host:port#name(socks:// 同义)
-- 认证信息可选:没有 userinfo 时是无认证 socks。统一归一为 proto="socks"。
local function parse_socks(uri, body)
local name, rest = "", body
local hash = rest:find("#", 1, true)
if hash then
name = util.url_decode(rest:sub(hash + 1))
rest = rest:sub(1, hash - 1)
end
-- socks 没有标准查询参数,但第三方链接可能带 ? 尾巴,截掉以免污染 host:port
local qpos = rest:find("?", 1, true)
local hp = qpos and rest:sub(1, qpos - 1) or rest
local username, password
-- 取**最后**一个 @ 作为 userinfo 与 authority 的分界:未编码的 @ 只可能出现在
-- 密码里(userinfo 不允许裸 @),按第一个 @ 切会把 "p@ss" 这类密码切坏,
-- 而且失败是静默的(解析出一个错的密码)。
local userinfo, hostport = hp:match("^(.*)@([^@]*)$")
if not userinfo then
hostport = hp
else
userinfo = util.url_decode(userinfo)
local cpos = userinfo:find(":", 1, true)
if cpos then
username = userinfo:sub(1, cpos - 1)
password = userinfo:sub(cpos + 1)
else
username = userinfo
end
if username == "" then username = nil end
end
local host, port = util.split_hostport(hostport)
if not host or host == "" then return nil, "bad socks" end
local out = node.normalize({
proto = "socks", name = name, server = host, port = tonumber(port), raw = uri,
})
if username then out.username = username end
if password then out.password = password end
return out
end
-- tuic://uuid:password@host:port/?congestion_control=..&alpn=..&sni=..#name
local function parse_tuic(uri, body)
local name, rest = "", body
@@ -617,9 +705,11 @@ function M.parse_uri(uri)
if proto == "vless" then return parse_vless(uri, body) end
if proto == "trojan" then return parse_trojan(uri, body) end
if proto == "vmess" then return parse_vmess(uri, body) end
if proto == "hysteria" then return parse_hysteria(uri, body) end
if proto == "hysteria2" then return parse_hysteria2(uri, body) end
if proto == "tuic" then return parse_tuic(uri, body) end
if proto == "wireguard" then return parse_wireguard(uri, body) end
if proto == "socks" or proto == "socks5" then return parse_socks(uri, body) end
return nil, "unsupported"
end
@@ -730,38 +820,72 @@ local function parse_yaml_content(content)
raw_nodes[#raw_nodes + 1] = current_node
end
-- 协议映射:Clash type -> proto
local proto_map = {
vmess = "vmess",
vless = "vless",
trojan = "trojan",
ss = "shadowsocks",
ssr = "ssr",
http = "http",
socks5 = "socks5",
}
-- 协议映射:Clash type -> proto。
-- 复用 parser_clash_yaml 的权威表(单一事实来源),本处曾自维护一份,缺
-- hysteria2/hysteria/tuic/wireguard 且 socks5 未归一,后果是:
-- * 认不出的 type 被 `or "vmess"` 兜底成 vmess —— 一份 sing-box YAML 里的
-- hysteria2/tuic/wireguard 出站会变成 4 个没有 uuid 的假 vmess 节点(静默数据损坏)
-- * socks5 原样保留,而节点页筛选/规则 proto_filter 用的是规范名 socks,节点查不到
local proto_map = parser_clash_yaml.TYPE_MAP
-- 转换字段名并归一化
local result = {}
for _, n in ipairs(raw_nodes) do
if not n or not n.server or not n.port then
-- 跳过无效节点
local proto = proto_map[n.type or n.proto]
-- 上面的 in_proxies 分支同时接受 `proxies:`(Clash)与 `outbounds:`(sing-box)
-- 两种段名,但两者字段名并不相同:Clash 用 port / name,sing-box 用
-- server_port / tag。此前只读 port / name,导致 sing-box YAML 的 outbounds
-- 全部卡在下面的 `not port` 判断上被静默丢弃(识别了段名却一个节点都拿不到)。
-- 下列别名逐字对照 parser_json_config.parse_singbox_json 读取的键名,
-- 那是本项目读取 sing-box 出站的权威实现,不另立一套。
local port = n.port or n.server_port
if not n or not n.server or not port or not proto then
-- 跳过无效节点 / 未知协议。
-- 未知协议不能兜底成 vmess:那是凭空造出一个字段全错的节点,
-- 比丢弃更糟(用户看到"导入成功",实际拿到一批不可用的假节点)。
else
local proto = proto_map[n.type or n.proto] or "vmess"
-- vmess 的加密方式:Clash 写 cipher,sing-box 写 security。
-- security 在 node.normalize 里是 TLS 层,两者语义冲突,因此仅当取值
-- 落在 vmess 加密方式白名单内时才当作 cipher 消费掉,否则留给 TLS 层。
local vmess_cipher
if proto == "vmess" then
vmess_cipher = n.cipher
if vmess_cipher == nil and n.security ~= nil
and node.VMESS_CIPHERS[n.security] then
vmess_cipher = n.security
end
end
-- 同上的字符串真值问题:只认明确的真值写法
local scv = n["skip-cert-verify"] or n.insecure
local skip_cert_verify = nil
if scv == true or scv == "true" or scv == "1" then skip_cert_verify = true
elseif scv == false or scv == "false" or scv == "0" then skip_cert_verify = false end
-- 注意不能用 `(cond) and nil or x` 写法:Lua 的 and/or 在 cond 为真时
-- 结果是 nil,会被后面的 or 继续兜底,等于没生效
local tls_security = n.security
if vmess_cipher ~= nil then tls_security = nil end
local node_data = {
proto = proto,
name = n.name or n.Name or (n.server .. ":" .. tostring(n.port or "")),
name = n.name or n.Name or n.tag or (n.server .. ":" .. tostring(port)),
server = n.server,
port = tonumber(n.port),
port = tonumber(port),
uuid = n.uuid or n.id,
password = n.password,
-- sing-box 的 hysteria/hysteria2 认证字段是 password / auth_str / auth
password = n.password or n.auth_str or n.auth,
method = n.cipher or n.method,
-- Clash 的 cipher 是 vmess 加密方式;tls 才是 TLS 层,
-- 交由 node.normalize 归一到 security
cipher = (proto == "vmess") and n.cipher or nil,
cipher = vmess_cipher,
tls = n.tls,
security = n.security,
sni = n.sni or n.servername,
-- sing-box 的 vmess.security 已被上面消费为 cipher,不再当 TLS 层
security = tls_security,
-- Clash 写 sni / servername,sing-box 的 tls 子块写 server_name
sni = n.sni or n.servername or n.server_name,
-- sing-box 的 tls.insecure 对应 skip-cert-verify。
-- 行解析器读出来的是字符串,而 "false" / "0" 在 Lua 里也是真值,
-- 直接透传会让 skip-cert-verify=false 变成「跳过证书校验」,故显式判假。
["skip-cert-verify"] = skip_cert_verify,
net = n.net or n.network,
alterId = tonumber(n.alterId),
}
result[#result + 1] = node.normalize(node_data)
@@ -888,6 +1012,17 @@ function M.parse(content)
-- 兼容标准 base64 与 base64url(- _ 无 padding):base64_url_decode 两者皆可
local decoded = util.base64_url_decode(content)
if decoded == "" then return nil, "Base64 解码失败" end
-- 解出来的内容本身可能是 YAML / JSON:机场把整份 Clash 配置或 sing-box
-- 配置 base64 后直接下发是很常见的做法。原先一律按 URI 列表解析,这类
-- 订阅会得到 0 个节点且不报错(用户只看到「订阅为空」)。
-- 递归走一遍 detect/parse 复用既有分支;inner == "base64" 时不再递归,
-- 避免 base64 套 base64 时无限递归。外层容器格式仍是 base64。
local inner = M.detect(decoded)
if inner ~= "base64" and inner ~= "empty" and inner ~= "unknown" then
local res, err = M.parse(decoded)
if res then res.format = "base64" end
return res, err
end
return { nodes = parse_lines(split_lines(decoded)), format = "base64" }
elseif format == "json" then
local nodes, err = parse_json_content(content)
@@ -7,7 +7,10 @@ local node = require("substore.node")
local M = {}
-- Clash type → 统一 proto
-- Clash type → 统一 proto。
-- 这是**权威映射表**:parser_json_config 与 parser.lua 的简易 YAML 兜底解析都复用同一份,
-- 避免各自维护一份而漂移(曾出现兜底那份缺 hysteria2/hysteria/tuic/wireguard,
-- 且把 socks5 原样留下,导致这些节点被误当成 vmess 或被筛选器静默丢弃)。
local TYPE_MAP = {
vmess = "vmess",
vless = "vless",
@@ -24,6 +27,9 @@ local TYPE_MAP = {
http = "http",
}
-- 导出供 parser.lua 的简易 YAML 兜底解析复用(单一事实来源)
M.TYPE_MAP = TYPE_MAP
-- 标量值转换:布尔 / 数字 / 去引号 / 保留字符串
local function scalar(raw)
raw = raw or ""
@@ -47,6 +53,74 @@ local function scalar(raw)
return raw
end
-- 去掉行尾注释。YAML 规定内联注释的 # 前必须有空白,引号内的 # 不算注释。
-- 不处理的话 `proxies: # 说明` 会把注释文本当成值,proxies 变成字符串,
-- 整份配置被判定为「没有 proxies」→ 0 个节点且不报错。
local function strip_comment(v)
local in_s, in_d = false, false
for i = 1, #v do
local c = v:sub(i, i)
if in_s then
if c == "'" then in_s = false end
elseif in_d then
if c == '"' then in_d = false end
elseif c == "'" then in_s = true
elseif c == '"' then in_d = true
elseif c == "#" and (i == 1 or v:sub(i - 1, i - 1):match("%s")) then
return (v:sub(1, i - 1):gsub("%s*$", ""))
end
end
return v
end
-- 按顶层分隔符切分,忽略引号内以及 {} / [] 内部的同级分隔符
local function split_top(s, sep)
local parts, buf = {}, {}
local depth, in_s, in_d = 0, false, false
for i = 1, #s do
local c = s:sub(i, i)
if in_s then
if c == "'" then in_s = false end
buf[#buf + 1] = c
elseif in_d then
if c == '"' then in_d = false end
buf[#buf + 1] = c
elseif c == "'" then
in_s = true; buf[#buf + 1] = c
elseif c == '"' then
in_d = true; buf[#buf + 1] = c
elseif c == "{" or c == "[" then
depth = depth + 1; buf[#buf + 1] = c
elseif c == "}" or c == "]" then
depth = depth - 1; buf[#buf + 1] = c
elseif c == sep and depth == 0 then
parts[#parts + 1] = table.concat(buf); buf = {}
else
buf[#buf + 1] = c
end
end
parts[#parts + 1] = table.concat(buf)
return parts
end
-- 解析 YAML 流式映射 {...}。Clash 机场配置常用
-- `- {name: A, type: vmess, server: 1.1.1.1, port: 443}` 这种写法:它是合法 YAML,
-- 但不是合法 JSON(键没有加引号),util.json_decode 必然失败,节点被整条静默
-- 丢弃(0 个节点且无报错)。
local function parse_flow_map(s)
local body = s:match("^%s*{(.*)}%s*$")
if not body then return nil end
local map = {}
for _, part in ipairs(split_top(body, ",")) do
local k, v = part:match("^%s*([^:]+):%s*(.*)$")
if k then
map[k:gsub("%s*$", "")] = scalar(strip_comment(v))
end
end
if next(map) == nil then return nil end
return map
end
-- 预处理内容为 (indent, rest) 列表,跳过空行、注释、文档分隔符
local function preprocess(content)
local items = {}
@@ -79,6 +153,9 @@ local function parse_yaml(content)
pos = pos + 1
else
k = k:gsub("%s*$", "")
-- 行尾注释必须先去掉,否则 `proxies: # 说明` 会把注释文本当成值,
-- proxies 变成字符串,整份配置被判定为「没有 proxies」
v = strip_comment(v)
pos = pos + 1
if v == "" or v == "|" or v == ">" then
if pos <= n_items and items[pos].ind > it.ind then
@@ -108,12 +185,17 @@ local function parse_yaml(content)
if it.rest == "-" then dash = "" else dash = it.rest:match("^-%s*(.*)$") end
if dash == nil then break end
pos = pos + 1
-- 列表项也要去掉行尾注释:`- {a: 1} # 说明`、`- "::/0" # 说明`
-- 都会让注释文本混进值里(流式映射那条甚至会整项解析失败)
dash = strip_comment(dash)
local m = nil
-- 流式 JSON 对象:- {"name":"...","type":"vmess",...}(机场 clash 配置常见写法)
if dash:sub(1, 1) == "{" then
-- 先按 JSON 解(键带引号的流式写法),失败再按 YAML 流式映射解
local obj = util.json_decode(dash)
if type(obj) == "table" then m = obj end
if not m then m = parse_flow_map(dash) end
else
local k, v = dash:match("^([^:]+):%s*(.*)$")
-- 引号开头的列表项是标量而非映射:避免把 "- \"::/0\"" 里的冒号
@@ -152,7 +234,15 @@ local function map_clash_node(p)
if type(p) ~= "table" then return nil end
if not p.name or not p.server or not p.port then return nil end
local proto = TYPE_MAP[p.type] or p.type or "vmess"
-- 未知协议必须丢弃,不能原样透传、也不能兜底成 vmess:
-- * 原样透传:统一模型承载不了 snell / shadowtls / mieru / ssh 等类型(认证
-- 方式与字段都不同),透传出去会在输出端变成 sing-box 的 type: "snell"、
-- Xray 的 protocol: "snell" 这类非法取值,客户端会拒绝加载整份配置。
-- * 兜底成 vmess:那是凭空造出一个字段全错的节点,比丢弃更糟(用户看到
-- "导入成功",实际拿到一批不可用的假节点)。
-- parser.lua 的简易 YAML 兜底解析(复用同一张 TYPE_MAP)早已按此处理,此处对齐。
local proto = TYPE_MAP[p.type]
if not proto then return nil end
local n = {
proto = proto,
name = p.name,
@@ -102,7 +102,10 @@ function M.parse_singbox_json(content)
node_data.password = outbound.password
end
-- sing-box 用 tls 对象表达 TLS 层(enabled 缺省即 true)
-- sing-box 用 tls 对象表达 TLS 层。注意 enabled 的缺省值是 false
-- (option/tls.go: `Enabled bool` + omitempty),本输出模块因此总是
-- 显式写 enabled=true。读取侧这里放宽为「只要不是显式 false 就当作
-- 启用」,以便导入那些省略 enabled 的第三方配置时仍能保留 TLS 层。
if outbound.tls and type(outbound.tls) == "table" then
if outbound.tls.server_name then
node_data.sni = outbound.tls.server_name
@@ -77,12 +77,46 @@ function M.shq(s)
return "'" .. tostring(s or ""):gsub("'", "'\\''") .. "'"
end
-- 把值压成单行。Surge / Loon / Quantumult X / wg-quick .conf 都是行式格式:
-- 值里一旦含换行,就会截断当前行并伪造出额外的一行(节点名、sni、path 等
-- 全部来自订阅内容,属于不可信输入)。换行/回车压成空格,其余控制字符丢弃。
function M.one_line(s)
if s == nil then return nil end
return (tostring(s):gsub("[\r\n]+", " "):gsub("%c", ""))
end
-- 生成随机十六进制 token(用于下载链接访问控制)
--
-- 不能直接用 math.random 产出:Lua 5.1 的 math.random 是 31 位 LCG,而原实现
-- 每次调用都重新 randomseed,同一时钟刻度内的多次调用会给出完全相同的序列
-- (实测 20 万次调用只有约 18 万个不同值,约 9.7% 的 token 重复)。订阅下载
-- token 是访问控制的唯一凭据,重复即可被猜测。优先直接读内核熵源,
-- 只有在 /dev/urandom 不可用时才退回 PRNG(且只播种一次)。
local prng_seeded = false
local function seed_prng()
if prng_seeded then return end
prng_seeded = true
math.randomseed(os.time() + math.floor((os.clock() * 1000000) % 1000000))
end
function M.rnd_hex(len)
len = len or 16
local f = io.open("/dev/urandom", "rb")
if f then
local need = math.ceil(len / 2)
local bytes = f:read(need)
f:close()
if bytes and #bytes == need then
local out = {}
for i = 1, #bytes do
out[#out + 1] = string.format("%02x", bytes:byte(i))
end
return (table.concat(out):sub(1, len))
end
end
seed_prng()
local hex = "0123456789abcdef"
local out = {}
math.randomseed(os.time() + (os.clock() * 1000000 % 1000000) + math.random(0, 65535))
for _ = 1, len do
local idx = math.random(1, 16)
out[#out + 1] = hex:sub(idx, idx)
@@ -345,17 +379,33 @@ function M.file_size(path)
return #data
end
-- 原子写:先写临时文件再重命名,避免写一半损坏
-- 原子写:先写临时文件再重命名,避免写一半损坏。
--
-- 两处必须做对,否则「原子」只是名义上的:
-- 1) 临时文件名不能固定成 "<path>.tmp":两个进程同时写同一路径时会交错写进
-- 同一个临时文件,rename 上去的是两者内容的混合体,各自的原子性都失效。
-- 2) write / close 的返回值必须检查:磁盘写满或写入被截断时 f:write 会失败,
-- 但 os.rename 依然成功 —— 于是原子地换上一个残缺文件,调用方却以为成功,
-- 下一次读取才发现数据没了。
function M.atomic_write(path, content)
local tmp = path .. ".tmp"
local tmp = string.format("%s.tmp.%s", path, M.rnd_hex(8))
local f, e = io.open(tmp, "wb")
if not f then return false, e end
f:write(content)
f:close()
local ok, e2 = os.rename(tmp, path)
if not ok then
local wok, werr = f:write(content)
if not wok then
f:close()
os.remove(tmp)
return false, e2
return false, werr
end
local cok, cerr = f:close()
if not cok then
os.remove(tmp)
return false, cerr
end
local rok, rerr = os.rename(tmp, path)
if not rok then
os.remove(tmp)
return false, rerr
end
return true
end
@@ -5,7 +5,10 @@
* 提供:SUBSTORE_PROTOS / PROTO_FIELDS / renderFields / collectNodes / addNodeRow
*/
var SUBSTORE_PROTOS = ["vmess","vless","trojan","shadowsocks","ssr","hysteria2","tuic","wireguard"];
// 必须与 substore/node.lua 的 M.PROTOS 一致(规格 §25「本地表单协议能力」的协议集合)。
// 之前这里少了 hysteria / socks:node.lua、节点页筛选、规则 proto_filter 都认这两个协议,
// 唯独表单下拉框选不到,用户无法用「表单导入」录入它们。
var SUBSTORE_PROTOS = ["vmess","vless","trojan","shadowsocks","ssr","hysteria2","tuic","hysteria","wireguard","socks"];
var PROTO_FIELDS = {
ssr: ["server","port","password","cipher","protocol","obfs","obfs-param","protocol-param","udp"],
@@ -14,8 +17,12 @@ var PROTO_FIELDS = {
trojan: ["server","port","password","sni","net","headerType","path","host","udp","skip-cert-verify"],
shadowsocks: ["server","port","password","method","headerType","udp"],
hysteria2: ["server","port","password","sni","obfs","obfs-password","skip-cert-verify"],
// hysteria(v1):obfs 是普通字符串(不是 hysteria2 的 salamander),因此没有 obfs-password。
// 字段集合以各输出模块实际消费的键为准(output_clash_meta / output_singbox / output_uri)。
hysteria: ["server","port","password","sni","obfs","skip-cert-verify"],
tuic: ["server","port","uuid","password","sni","udp","skip-cert-verify"],
wireguard: ["server","port","private-key","public-key","pre-shared-key","ip","ipv6","allowed-ips","reserved","persistent-keepalive","listen-port","mtu","amnezia-wg-option"]
wireguard: ["server","port","private-key","public-key","pre-shared-key","ip","ipv6","allowed-ips","reserved","persistent-keepalive","listen-port","mtu","amnezia-wg-option"],
socks: ["server","port","username","password","udp"]
};
// 枚举字段用下拉框;键为 "协议.字段" 优先,退化为通用 "字段"
@@ -0,0 +1,132 @@
-- data_integrity_test.lua — 数据完整性与 token 熵回归测试
-- 用法:lua5.1 tests/data_integrity_test.lua
--
-- 覆盖:
-- H8 订阅列表文件损坏时必须报错并拒绝写入,而不是按空列表处理
-- (否则下一次 add 会把用户所有订阅覆盖掉)
-- H9 atomic_write 必须检查 write / close 的返回值
-- H10 临时文件名必须唯一,并发写同一路径不得互相踩踏、不得残留
-- H11 下载 token 必须来自内核熵源,不得出现可预测的重复
package.path = "./root/usr/share/?.lua;" .. package.path
local core = require("substore.core")
local util = require("substore.util")
local passed, failed = 0, 0
local function check(name, cond)
if cond then
passed = passed + 1
print("PASS " .. name)
else
failed = failed + 1
print("FAIL " .. name)
end
end
-- 重定向数据目录到临时目录,避免污染系统
local tmp = os.tmpname() .. "_substore"
os.execute("mkdir -p " .. string.format("%q", tmp .. "/nodes"))
core.DATA_DIR = tmp
core.LIST_FILE = tmp .. "/subscriptions.json"
core.NODES_DIR = tmp .. "/nodes"
-- ---------- H8:损坏的列表文件 ----------
local id1 = core.add("订阅一", "http://example.com/a")
check("add first subscription", id1 ~= nil)
check("list count after add", #core.list() == 1)
local good = util.read_file(core.LIST_FILE)
check("list file written", good ~= nil and good ~= "")
-- 把文件写成无法解析的内容,模拟截断 / 磁盘损坏
util.atomic_write(core.LIST_FILE, "{ 这不是合法 JSON")
local arr, lerr = core.list()
check("list reports corruption", lerr ~= nil and type(lerr) == "string")
check("list returns empty array on corruption", type(arr) == "table" and #arr == 0)
-- 关键:写入路径必须拒绝,而不是在空表上追加后整表写回
local id2, aerr = core.add("订阅二", "http://example.com/b")
check("add refuses on corruption", id2 == nil)
check("add returns corruption error", aerr ~= nil and aerr:find("损坏", 1, true) ~= nil)
local id3, lerr2 = core.add_local("本地订阅", "vmess://x")
check("add_local refuses on corruption", id3 == nil and lerr2 ~= nil)
local id4, cerr = core.add_combo("组合", { id1 })
check("add_combo refuses on corruption", id4 == nil and cerr ~= nil)
check("corrupt file not overwritten", util.read_file(core.LIST_FILE) == "{ 这不是合法 JSON")
check("save_meta refuses on corruption", (core.save_meta(id1, { name = "x" })) == false)
-- 恢复后必须能继续正常写入,且 _seq 不得回退(否则会重复发放已用过的 ID)
util.atomic_write(core.LIST_FILE, good)
check("list recovers", #core.list() == 1)
local id5 = core.add("订阅三", "http://example.com/c")
check("add works after recovery", id5 ~= nil)
check("id not reused after recovery", id5 ~= nil and id5 ~= id1)
-- ---------- H9:atomic_write 的返回值 ----------
local ok_bad, err_bad = util.atomic_write("/proc/substore-nonexistent/x.json", "data")
check("atomic_write fails on bad path", ok_bad == false)
check("atomic_write returns error", err_bad ~= nil)
local target = tmp .. "/atomic.txt"
check("atomic_write succeeds", util.atomic_write(target, "hello") == true)
check("atomic_write content", util.read_file(target) == "hello")
check("atomic_write overwrites", util.atomic_write(target, "world") == true
and util.read_file(target) == "world")
-- ---------- H10:临时文件名唯一、无残留 ----------
local base = tmp .. "/concurrent.txt"
local names = {}
for _ = 1, 20 do
local n = string.format("%s.tmp.%s", base, util.rnd_hex(8))
names[n] = true
end
local distinct = 0
for _ in pairs(names) do distinct = distinct + 1 end
check("tmp names are unique", distinct == 20)
-- 写完后不得在目标目录留下 .tmp 文件
os.execute("rm -f " .. string.format("%q", tmp) .. "/*.tmp.*")
util.atomic_write(base, "x")
local listing = tmp .. "/ls.txt"
os.execute("ls -a " .. string.format("%q", tmp) .. " > " .. string.format("%q", listing))
local ls = util.read_file(listing) or ""
local leftover = 0
for name in ls:gmatch("[^\n]+") do
if name:find("%.tmp%.") then leftover = leftover + 1 end
end
check("no leftover tmp files", leftover == 0)
-- ---------- H11:token 熵 ----------
local seen, dups, N = {}, 0, 20000
for _ = 1, N do
local t = util.rnd_hex(16)
if seen[t] then dups = dups + 1 else seen[t] = true end
end
check("no duplicate tokens in " .. N .. " draws", dups == 0)
local tok = util.rnd_hex(16)
check("token length 16", #tok == 16)
check("token is lowercase hex", tok:match("^[0-9a-f]+$") ~= nil)
check("token length honoured for odd len", #util.rnd_hex(7) == 7)
check("token length honoured for 32", #util.rnd_hex(32) == 32)
-- 订阅 token 走同一条路径
local t1 = core.ensure_token(id1)
check("subscription token length", type(t1) == "string" and #t1 == 16)
check("subscription token stable", core.ensure_token(id1) == t1)
os.execute("rm -rf " .. string.format("%q", tmp))
-- ---------- 结果 ----------
print(string.format("\n%d passed, %d failed", passed, failed))
os.exit(failed == 0 and 0 or 1)
@@ -148,5 +148,29 @@ local empty_output = output_clash_meta.generate({}, {})
check("empty nodes handled", type(empty_output) == "string")
-- ---------- 结果 ----------
-- H12:sni 与 servername 是同一字段的两种写法,同时存在时只能输出一个 servername
-- 键。Clash YAML 导入会同时填上两者,各写一行会让 YAML 出现重复键。
local dup_out = output_clash_meta.generate({ { proto = "vmess", name = "D", server = "1.1.1.1",
port = 443, uuid = "u", sni = "s.example.com", servername = "s.example.com" } })
local dup_count = 0
for _ in dup_out:gmatch("servername:") do dup_count = dup_count + 1 end
check("no duplicate servername key", dup_count == 1)
check("servername value kept", dup_out:find("servername: s%.example%.com") ~= nil)
check("servername only still emitted",
output_clash_meta.generate({ { proto = "vmess", name = "S", server = "1.1.1.1", port = 443,
uuid = "u", servername = "only.example.com" } }):find("servername: only%.example%.com") ~= nil)
-- H13:含控制字符的值必须转义并加双引号。未加引号的换行/回车会破坏文档结构,
-- 而转义后的 "\t" 落在 plain scalar 里会被 YAML 当成两个普通字符。
local esc_out = output_clash_meta.generate({ { proto = "vmess", name = "tab\there", server = "1.1.1.1",
port = 443, uuid = "u", password = "p\rw" } })
check("tab escaped and quoted", esc_out:find('"tab\\there"') ~= nil)
check("CR escaped and quoted", esc_out:find('"p\\rw"') ~= nil)
check("no raw CR in output", esc_out:find("\r") == nil)
local nl_out = output_clash_meta.generate({ { proto = "vmess", name = "a\nb", server = "1.1.1.1",
port = 443, uuid = "u" } })
check("newline name quoted and escaped", nl_out:find('name: "a\\nb"') ~= nil)
check("newline name does not add a line", nl_out:find('name: a\nb') == nil)
print(string.format("\n%d passed, %d failed", passed, failed))
os.exit(failed == 0 and 0 or 1)
@@ -4,6 +4,7 @@
package.path = "./root/usr/share/?.lua;" .. package.path
local output = require("substore.output")
local fmts = require("substore.output_formats")
local util = require("substore.util")
local passed, failed = 0, 0
@@ -100,5 +101,52 @@ check("unknown format has err", err ~= nil)
-- 空节点
check("empty nodes ok", type(output.generate({}, "surge")) == "string")
-- H14:tuic 的 alpn 可能是数组(sing-box JSON / Clash YAML 的 alpn 列表导入后即为
-- table),直接 "alpn=" .. n.alpn 会 attempt to concatenate a table value,
-- 让整次导出直接报错。
local ok_alpn, line_alpn = pcall(fmts.surge_line, { proto = "tuic", name = "T", server = "1.2.3.4",
port = 443, uuid = "u", password = "p", security = "tls", sni = "s.example.com",
alpn = { "h3", "h2" } })
check("tuic alpn array does not raise", ok_alpn)
check("tuic alpn array joined", ok_alpn and line_alpn:find("alpn=h3,h2", 1, true) ~= nil)
check("tuic alpn string still works",
fmts.surge_line({ proto = "tuic", name = "T", server = "1.2.3.4", port = 443, uuid = "u",
alpn = "h3" }):find("alpn=h3", 1, true) ~= nil)
-- H7:节点名 / 参数值来自订阅内容(不可信输入),含换行会截断当前行并伪造出
-- 一条新的代理行。
local nl_line = fmts.surge_line({ proto = "trojan", name = "A\nB = trojan, 6.6.6.6, 443, password=x",
server = "1.1.1.1", port = 443, password = "p", security = "tls" })
check("surge line has no newline", nl_line:find("\n") == nil)
check("surge line has no CR", nl_line:find("\r") == nil)
check("surge line keeps name prefix", nl_line:sub(1, 1) == "A")
local function count_lines(s)
local c = 0
for _ in s:gmatch("\n") do c = c + 1 end
return c
end
-- 代理组行同样不能被节点名注入:压平后的名字不得让输出多出一行,
-- 也不得产生一条以注入内容开头的新行
local grp = fmts.to_surge({ { proto = "trojan", name = "G\nINJECT = trojan, 9.9.9.9, 443, password=q",
server = "1.1.1.1", port = 443, password = "p", security = "tls" } })
local grp_ok = fmts.to_surge({ { proto = "trojan", name = "GINJECT = trojan, 9.9.9.9, 443, password=q",
server = "1.1.1.1", port = 443, password = "p", security = "tls" } })
check("surge group newline name adds no line", count_lines(grp) == count_lines(grp_ok))
local starts_inject = false
for l in grp:gmatch("[^\n]+") do
if l:sub(1, 6) == "INJECT" then starts_inject = true end
end
check("surge group line not injected", starts_inject == false)
-- Quantumult X:节点名含换行不得让输出多出一行
local qx_nl = fmts.to_qx({ { proto = "trojan", name = "X\nY", server = "1.1.1.1", port = 443,
password = "p" } })
local qx_ok = fmts.to_qx({ { proto = "trojan", name = "XY", server = "1.1.1.1", port = 443,
password = "p" } })
check("qx newline name does not add lines", count_lines(qx_nl) == count_lines(qx_ok))
check("qx newline name flattened", qx_nl:find("X Y", 1, true) ~= nil)
print(string.format("\n%d passed, %d failed", passed, failed))
os.exit(failed == 0 and 0 or 1)
@@ -10,6 +10,7 @@ local util = require("substore.util")
local output = require("substore.output")
local singbox = require("substore.output_singbox")
local v2ray = require("substore.output_v2ray")
local sbjson = require("substore.parser_json_config")
local passed, failed = 0, 0
@@ -96,13 +97,44 @@ check("sb route private -> direct", sb.route.rules[1].ip_is_private == true and
-- action 自 1.11.0 起才存在且默认即 "route";省略它才能同时兼容 1.10 与 1.11+
check("sb route rule omits action (version compat)", sb.route.rules[1].action == nil)
-- tls 必须是对象:security=tls 但无 sni 时曾产出非法的 "tls":[]
-- tls 必须是对象,且必须显式 enabled=true:sing-box 的 OutboundTLSOptions.Enabled
-- 是 `bool` + `json:"enabled,omitempty"`(option/tls.go),缺省值即 false。少了
-- enabled 整个 tls 块会被上游忽略 —— vmess/vless/trojan 退化成明文拨号,
-- hysteria2/tuic 更会以 C.ErrTLSRequired 拒绝启动。此处原本断言 "tls":{},
-- 等于把这个缺陷固化进了测试。
local tls_node = { { proto = "vmess", name = "T", server = "1.1.1.1", port = 443, uuid = "u", security = "tls" } }
local tls_raw = singbox.generate(tls_node)
check("sb tls is object not array", tls_raw:find('"tls":{}') ~= nil and tls_raw:find('"tls":%[%]') == nil)
check("sb tls with sni keeps server_name",
singbox.generate({ { proto = "vmess", name = "T", server = "1.1.1.1", port = 443, uuid = "u",
security = "tls", sni = "example.com" } }):find('"server_name":"example.com"') ~= nil)
check("sb tls is object not array", tls_raw:find('"tls":%[%]') == nil)
local tls_out = util.json_decode(tls_raw).outbounds[1].tls
check("sb tls enabled explicit true", type(tls_out) == "table" and tls_out.enabled == true)
local tls_sni = util.json_decode(singbox.generate({ { proto = "vmess", name = "T", server = "1.1.1.1", port = 443,
uuid = "u", security = "tls", sni = "example.com" } })).outbounds[1].tls
check("sb tls with sni keeps server_name", tls_sni.server_name == "example.com")
check("sb tls with sni keeps enabled", tls_sni.enabled == true)
-- 无 TLS 的节点不得凭空得到 tls 块(enabled 不能无条件写)
local no_tls = util.json_decode(singbox.generate({ { proto = "vmess", name = "N", server = "1.1.1.1",
port = 443, uuid = "u" } })).outbounds[1]
check("sb no tls block without security", no_tls.tls == nil)
check("sb security none yields no tls",
util.json_decode(singbox.generate({ { proto = "vmess", name = "N", server = "1.1.1.1", port = 443,
uuid = "u", security = "none" } })).outbounds[1].tls == nil)
-- hysteria2 / tuic 在 sing-box 里是 TLS-only,security=tls 时必须带 enabled=true
local hy2 = util.json_decode(singbox.generate({ { proto = "hysteria2", name = "H", server = "1.1.1.1",
port = 443, password = "pw", security = "tls", sni = "h.example.com" } })).outbounds[1]
check("sb hysteria2 tls enabled", hy2.tls and hy2.tls.enabled == true and hy2.tls.server_name == "h.example.com")
local tuic = util.json_decode(singbox.generate({ { proto = "tuic", name = "U", server = "1.1.1.1",
port = 443, uuid = "u", password = "pw", security = "tls" } })).outbounds[1]
check("sb tuic tls enabled", tuic.tls and tuic.tls.enabled == true)
-- 往返:本模块输出的 tls.enabled 必须能被 sing-box 解析器读回为 security=tls
local rt_sb = util.json_decode(singbox.generate({ { proto = "vmess", name = "RT", server = "1.1.1.1",
port = 443, uuid = "u", security = "tls", sni = "rt.example.com" } }))
local rt_node = sbjson.parse_singbox_json(util.json_encode(rt_sb))[1]
check("sb tls round-trip keeps security", rt_node and rt_node.security == "tls")
check("sb tls round-trip keeps sni", rt_node and rt_node.sni == "rt.example.com")
-- 节点名与保留 tag 重名时不得冲突
local clash_name = singbox.generate({ { proto = "vmess", name = "direct", server = "1.1.1.1", port = 443, uuid = "u" } })
@@ -222,5 +222,81 @@ if fnodes and #fnodes >= 2 then
end
-- ---------- 结果 ----------
-- H6:未知 Clash 类型必须丢弃。透传出去会在输出端变成 sing-box 的 type: "snell"、
-- Xray 的 protocol: "snell" 这类非法取值,客户端会拒绝加载整份配置;
-- 兜底成 vmess 则是凭空造出一个字段全错的假节点,比丢弃更糟。
local unknown = parser.parse([[
proxies:
- name: SN
type: snell
server: 1.1.1.1
port: 443
psk: secret
- name: OK
type: vmess
server: 2.2.2.2
port: 443
uuid: u
]])
check("unknown clash type dropped", #unknown == 1)
check("known node survives", unknown[1] ~= nil and unknown[1].proto == "vmess")
check("unknown proto not leaked", unknown[1] ~= nil and unknown[1].proto ~= "snell")
-- 缺失 type 同样丢弃(不再兜底成 vmess)
check("missing type dropped", #parser.parse([[
proxies:
- name: X
server: 1.1.1.1
port: 443
]]) == 0)
-- H4a:YAML 流式风格(键未加引号)是合法 YAML 但不是合法 JSON,
-- 原先 json_decode 失败后整项被静默丢弃
local flow = parser.parse([[
proxies:
- {name: F, type: vmess, server: 1.1.1.1, port: 443, uuid: u}
]])
check("flow style node count", #flow == 1)
check("flow style proto", flow[1] ~= nil and flow[1].proto == "vmess")
check("flow style server", flow[1] ~= nil and flow[1].server == "1.1.1.1")
check("flow style port is number", flow[1] ~= nil and flow[1].port == 443)
-- 流式风格的数组值仍要拆成 table
local flow_arr = parser.parse([[
proxies:
- {name: FA, type: vmess, server: 1.1.1.1, port: 443, uuid: u, alpn: [h2, http/1.1]}
]])
check("flow style array value", flow_arr[1] ~= nil and type(flow_arr[1].alpn) == "table")
-- 键带引号的流式写法仍走 JSON 分支
check("json flow style still works", #parser.parse([[
proxies:
- {"name":"J","type":"vmess","server":"1.1.1.1","port":443,"uuid":"u"}
]]) == 1)
-- H4b:行尾注释不得被当成值(`proxies: # 说明` 曾让 proxies 变成字符串)
local commented = parser.parse([[
proxies: # 我的节点
- name: C
type: vmess
server: 1.1.1.1
port: 443
uuid: u
]])
check("proxies with trailing comment", #commented == 1)
check("comment not used as value", commented[1] ~= nil and commented[1].server == "1.1.1.1")
-- 列表项的行尾注释同样要去掉
check("list item trailing comment", #parser.parse([[
proxies:
- {name: T, type: vmess, server: 1.1.1.1, port: 443, uuid: u} # 行尾注释
]]) == 1)
-- # 前无空白、或位于引号内时都不是注释,必须原样保留
local hash_plain = parser.parse("proxies:\n - name: A\n type: ss\n server: 1.1.1.1\n port: 443\n cipher: aes-128-gcm\n password: p#ss\n")
check("hash without space kept", hash_plain[1] ~= nil and hash_plain[1].password == "p#ss")
local hash_quoted = parser.parse('proxies:\n - name: A\n type: ss\n server: 1.1.1.1\n port: 443\n cipher: aes-128-gcm\n password: "a # b"\n')
check("hash inside quotes kept", hash_quoted[1] ~= nil and hash_quoted[1].password == "a # b")
print(string.format("\n%d passed, %d failed", passed, failed))
os.exit(failed == 0 and 0 or 1)
@@ -0,0 +1,332 @@
-- protocol_coverage_test.lua — 协议覆盖一致性回归测试(2.6.0)
-- 覆盖本轮修复:
-- 1. hysteria(v1) / socks 的 URI 文本导入(此前只出不进,导出→导入回环丢节点)
-- 2. 简易 YAML 兜底解析不再把未知协议兜底成 vmess(静默数据损坏)
-- 3. socks5 -> socks 规范化,使节点页筛选 / 规则 proto_filter 能命中
-- 4. clashmeta hysteria(v1) 用 sni 而非 servername;不写 v1 非法的 obfs-password
-- 5. clashmeta socks5 不再重复输出 password;http 补 username
-- 6. singbox hysteria(v1) 用 auth_str(不是 password)、obfs 为字符串
-- 7. singbox / surge 的 socks、http 凭据不再丢
-- 8. core.merge_form_node 能清空 username
-- 用法:lua5.1 tests/protocol_coverage_test.lua
package.path = "./root/usr/share/?.lua;" .. package.path
local parser = require("substore.parser")
local output = require("substore.output")
local output_uri = require("substore.output_uri")
local node = require("substore.node")
local core = require("substore.core")
local util = require("substore.util")
local passed, failed = 0, 0
local function check(name, cond)
if cond then
passed = passed + 1
print("PASS " .. name)
else
failed = failed + 1
print("FAIL " .. name)
end
end
-- ============================================================
-- 1. URI 文本导入:hysteria(v1)
-- ============================================================
local hy = parser.parse_uri("hysteria://pw@1.2.3.4:443/?sni=a.com&insecure=1&obfs=xxx#HY")
check("hysteria URI 可解析", hy ~= nil)
check("hysteria proto", hy and hy.proto == "hysteria")
check("hysteria server", hy and hy.server == "1.2.3.4")
check("hysteria port", hy and tonumber(hy.port) == 443)
check("hysteria password", hy and hy.password == "pw")
check("hysteria sni", hy and hy.sni == "a.com")
check("hysteria obfs", hy and hy.obfs == "xxx")
-- hysteria 系列在 mihomo / sing-box 里都是 TLS-only
check("hysteria security=tls", hy and hy.security == "tls")
-- obfs-password 是 hysteria2(salamander) 专属,v1 不应有
check("hysteria 无 obfs-password", hy and hy["obfs-password"] == nil)
check("hysteria insecure 透传", hy and hy.insecure == "1")
-- 不带查询参数的极简链接也要能解析
local hy_min = parser.parse_uri("hysteria://secret@9.9.9.9:8443#MIN")
check("hysteria 无参数可解析", hy_min ~= nil and hy_min.password == "secret")
-- 无法识别的查询参数忽略而不是报错(第三方链接常带 upmbps/downmbps 等)
local hy_extra = parser.parse_uri("hysteria://pw@1.2.3.4:443/?upmbps=100&downmbps=200&peer=x#E")
check("hysteria 未知参数忽略", hy_extra ~= nil and hy_extra.server == "1.2.3.4")
-- ============================================================
-- 2. URI 文本导入:socks / socks5
-- ============================================================
local sk = parser.parse_uri("socks5://user:pass@5.6.7.8:1080#SK")
check("socks5 URI 可解析", sk ~= nil)
-- socks5 与 socks 是同一协议,统一归一为规范名 socks
check("socks5 归一为 socks", sk and sk.proto == "socks")
check("socks server", sk and sk.server == "5.6.7.8")
check("socks port", sk and tonumber(sk.port) == 1080)
check("socks username", sk and sk.username == "user")
check("socks password", sk and sk.password == "pass")
local sk2 = parser.parse_uri("socks://5.6.7.8:1080#SK2")
check("socks:// 同义可解析", sk2 ~= nil and sk2.proto == "socks")
check("无认证 socks 无 username", sk2 and sk2.username == nil)
check("无认证 socks 无 password", sk2 and sk2.password == nil)
-- 密码含未编码的 @:必须取最后一个 @ 作分界(按第一个切会静默解析出错误密码)
local sk3 = parser.parse_uri("socks5://u:p@ss@5.6.7.8:1080#S3")
check("socks 密码含 @ 正确切分", sk3 ~= nil and sk3.username == "u"
and sk3.password == "p@ss" and sk3.server == "5.6.7.8")
-- 第三方链接可能带 ? 尾巴,不能污染 host:port
local sk4 = parser.parse_uri("socks5://5.6.7.8:1080/?foo=bar#S4")
check("socks 忽略查询尾巴", sk4 ~= nil and sk4.server == "5.6.7.8" and tonumber(sk4.port) == 1080)
-- ============================================================
-- 3. 导出 → 导入回环(修复前 hysteria / socks 只出不进)
-- ============================================================
local rt_cases = {
{ "hysteria", "hysteria://pw@1.2.3.4:443/?sni=a.com&insecure=1&obfs=xxx#HY" },
{ "hysteria2", "hysteria2://pw@1.2.3.4:443/?sni=a.com&obfs=salamander&obfs-password=op#H2" },
{ "socks5", "socks5://user:pass@5.6.7.8:1080#SK" },
{ "socks", "socks://5.6.7.8:1080#SK2" },
{ "tuic", "tuic://uuid-x:pw@1.2.3.4:443?sni=a.com#T" },
{ "vmess", "vmess://" .. util.base64_encode(util.json_encode({
v = "2", ps = "V", add = "1.2.3.4", port = "443", id = "u-1", aid = "0", net = "tcp", tls = "tls",
})) },
}
for _, c in ipairs(rt_cases) do
local n = parser.parse_uri(c[2])
check("回环 " .. c[1] .. " 首次解析", n ~= nil)
if n then
local back = output_uri.to_share_uri(n)
check("回环 " .. c[1] .. " 可再导出", back ~= nil)
local n2 = back and parser.parse_uri(back)
check("回环 " .. c[1] .. " 再导入成功", n2 ~= nil)
check("回环 " .. c[1] .. " proto 不变", n2 and n2.proto == n.proto)
check("回环 " .. c[1] .. " server 不变", n2 and n2.server == n.server)
check("回环 " .. c[1] .. " port 不变", n2 and tonumber(n2.port) == tonumber(n.port))
end
end
-- ============================================================
-- 4. 订阅文本中混有 hysteria / socks 行不再整行丢弃
-- ============================================================
local mixed = table.concat({
"hysteria://pw@1.1.1.1:443#A",
"socks5://u:p@2.2.2.2:1080#B",
"vmess://" .. util.base64_encode(util.json_encode({
v = "2", ps = "C", add = "3.3.3.3", port = "443", id = "u-2",
})),
}, "\n")
local mixed_res = parser.parse(mixed)
check("混合 URI 文本解析出 3 个节点", mixed_res and mixed_res.nodes and #mixed_res.nodes == 3)
if mixed_res and mixed_res.nodes then
local protos = {}
for _, n in ipairs(mixed_res.nodes) do protos[n.proto] = true end
check("混合文本含 hysteria", protos.hysteria == true)
check("混合文本含 socks", protos.socks == true)
check("混合文本含 vmess", protos.vmess == true)
end
-- ============================================================
-- 5. 简易 YAML 兜底解析:未知协议不得兜底成 vmess
-- ============================================================
local fb = parser.parse_yaml_content and parser.parse_yaml_content or nil
-- 走公开入口,确保兜底路径可达
local yaml_outbounds = [[
outbounds:
- type: hysteria2
server: 1.1.1.1
server_port: 443
password: p1
- type: hysteria
server: 1.1.1.2
server_port: 443
auth_str: p2
- type: tuic
server: 1.1.1.3
server_port: 443
uuid: u3
- type: wireguard
server: 1.1.1.4
server_port: 51820
- type: socks
server: 1.1.1.5
server_port: 1080
]]
local yres = parser.parse_yaml(yaml_outbounds)
if yres and #yres >0 then
local protos = {}
for _, n in ipairs(yres) do protos[n.proto] = (protos[n.proto] or 0) + 1 end
check("兜底解析 hysteria2 不被兜底成 vmess", protos.hysteria2 == 1)
check("兜底解析 hysteria 不被兜底成 vmess", protos.hysteria == 1)
check("兜底解析 tuic 不被兜底成 vmess", protos.tuic == 1)
check("兜底解析 wireguard 不被兜底成 vmess", protos.wireguard == 1)
check("兜底解析 socks 规范化", protos.socks == 1)
check("兜底解析无 vmess 假节点", protos.vmess == nil)
else
check("兜底解析 outbounds YAML", false)
end
-- ============================================================
-- 5b. sing-box YAML outbounds 的字段名别名(server_port / tag / auth_str / server_name / insecure)
-- 修复前:段名 `outbounds:` 被识别,但只读 Clash 的 port / name,
-- 所有 sing-box YAML 出站都卡在 `not port` 上被静默丢弃(解析出 0 个节点)。
-- ============================================================
local sb_yaml = [[
outbounds:
- type: hysteria2
tag: HY2
server: 1.1.1.1
server_port: 443
password: p1
tls:
server_name: sni.example.com
insecure: true
- type: hysteria
tag: HY1
server: 1.1.1.2
server_port: 8443
auth_str: p2
- type: vmess
tag: VM
server: 1.1.1.3
server_port: 443
uuid: u-3
security: aes-128-gcm
]]
local sb_res = parser.parse_yaml(sb_yaml)
check("sing-box YAML 出站不再被丢弃", sb_res and #sb_res == 3)
if sb_res and #sb_res == 3 then
check("sing-box YAML server_port 生效", tonumber(sb_res[1].port) == 443)
check("sing-box YAML tag 作为 name", sb_res[1].name == "HY2")
check("sing-box YAML tls.server_name -> sni", sb_res[1].sni == "sni.example.com")
check("sing-box YAML tls.insecure -> skip-cert-verify", sb_res[1]["skip-cert-verify"] == true)
check("sing-box YAML auth_str -> password", sb_res[2].password == "p2")
check("sing-box YAML hysteria 端口", tonumber(sb_res[2].port) == 8443)
-- sing-box 的 vmess.security 是加密方式,不是 TLS 层
check("sing-box YAML vmess.security -> cipher", sb_res[3].cipher == "aes-128-gcm")
check("sing-box YAML vmess security 不当作 TLS 层", sb_res[3].security ~= "aes-128-gcm")
end
-- insecure: false 不能被当成真值("false" 在 Lua 里是真值)
local sb_false = parser.parse_yaml([[
outbounds:
- type: hysteria2
server: 1.1.1.9
server_port: 443
password: p
tls:
insecure: false
]])
check("sing-box YAML insecure:false 不置 skip-cert-verify",
sb_false and sb_false[1] and sb_false[1]["skip-cert-verify"] ~= true)
-- ============================================================
-- 6. socks5 -> socks 规范化后,筛选 / 规则过滤能命中
-- ============================================================
local clash_socks = [[
proxies:
- name: SK
type: socks5
server: 1.1.1.1
port: 1080
username: u
password: p
]]
local cns = parser.parse_yaml(clash_socks)
check("clash socks5 导入成功", cns and #cns == 1)
check("clash socks5 归一为 socks", cns and cns[1] and cns[1].proto == "socks")
check("node.filter proto=socks 命中", #node.filter(cns, { proto = "socks" }) == 1)
check("apply_rules proto_filter=socks 命中", #node.apply_rules(cns, { proto_filter = "socks" }) == 1)
-- 直接对未归一节点调用 normalize 也应归一(所有真实节点都会经过 normalize)
local raw_norm = node.normalize({ proto = "socks5", server = "1.1.1.1", port = 1080 })
check("node.normalize 归一 socks5", raw_norm.proto == "socks")
-- ============================================================
-- 7. clashmeta 输出:hysteria(v1) 用 sni,不写 servername / obfs-password
-- ============================================================
local hy_node = { proto = "hysteria", name = "HY", server = "1.1.1.1", port = 443,
password = "pw", obfs = "obs", sni = "s.com" }
local cm_hy = output.generate({ hy_node }, "clashmeta")
check("clashmeta hysteria 有 sni", cm_hy and cm_hy:find(" sni: s.com", 1, true) ~= nil)
check("clashmeta hysteria 无 servername", cm_hy and cm_hy:find("servername", 1, true) == nil)
check("clashmeta hysteria 无 obfs-password", cm_hy and cm_hy:find("obfs-password", 1, true) == nil)
check("clashmeta hysteria 有 obfs", cm_hy and cm_hy:find(" obfs: obs", 1, true) ~= nil)
-- hysteria2 仍然用 sni + obfs-password
local h2_node = { proto = "hysteria2", name = "H2", server = "1.1.1.2", port = 443,
password = "p", obfs = "salamander", ["obfs-password"] = "op", sni = "s2.com" }
local cm_h2 = output.generate({ h2_node }, "clashmeta")
check("clashmeta hysteria2 有 sni", cm_h2 and cm_h2:find(" sni: s2.com", 1, true) ~= nil)
check("clashmeta hysteria2 有 obfs-password", cm_h2 and cm_h2:find(" obfs-password: op", 1, true) ~= nil)
-- vmess 仍然用 servername(不能被 hysteria 的改动误伤)
local vm_node = { proto = "vmess", name = "V", server = "1.1.1.3", port = 443,
uuid = "u", sni = "s3.com", security = "tls" }
local cm_vm = output.generate({ vm_node }, "clashmeta")
check("clashmeta vmess 仍用 servername", cm_vm and cm_vm:find(" servername: s3.com", 1, true) ~= nil)
-- ============================================================
-- 8. clashmeta 输出:socks password 不重复;http 补 username
-- ============================================================
local sk_node = { proto = "socks", name = "SK", server = "1.1.1.4", port = 1080,
username = "u", password = "p" }
local cm_sk = output.generate({ sk_node }, "clashmeta")
local _, pw_cnt = cm_sk:gsub(" password:", "")
check("clashmeta socks password 只出现一次", pw_cnt == 1)
check("clashmeta socks username 输出", cm_sk:find(" username: u", 1, true) ~= nil)
local http_node = { proto = "http", name = "HT", server = "1.1.1.5", port = 8080,
username = "hu", password = "hp" }
local cm_ht = output.generate({ http_node }, "clashmeta")
check("clashmeta http username 输出", cm_ht and cm_ht:find(" username: hu", 1, true) ~= nil)
local _, htpw = cm_ht:gsub(" password:", "")
check("clashmeta http password 只出现一次", htpw == 1)
-- ============================================================
-- 9. singbox 输出:hysteria(v1) 用 auth_str、obfs 为字符串
-- ============================================================
local sb_hy = util.json_decode(output.generate({ hy_node }, "singbox"))
check("singbox hysteria auth_str", sb_hy and sb_hy.outbounds[1] and sb_hy.outbounds[1].auth_str == "pw")
check("singbox hysteria 无 password 键", sb_hy and sb_hy.outbounds[1] and sb_hy.outbounds[1].password == nil)
check("singbox hysteria obfs 为字符串", sb_hy and sb_hy.outbounds[1] and sb_hy.outbounds[1].obfs == "obs")
local sb_h2 = util.json_decode(output.generate({ h2_node }, "singbox"))
check("singbox hysteria2 仍用 password", sb_h2 and sb_h2.outbounds[1] and sb_h2.outbounds[1].password == "p")
check("singbox hysteria2 obfs 为对象", sb_h2 and sb_h2.outbounds[1] and type(sb_h2.outbounds[1].obfs) == "table"
and sb_h2.outbounds[1].obfs.password == "op")
-- singbox socks / http 凭据
local sb_sk = util.json_decode(output.generate({ sk_node }, "singbox"))
check("singbox socks username", sb_sk and sb_sk.outbounds[1] and sb_sk.outbounds[1].username == "u")
check("singbox socks password", sb_sk and sb_sk.outbounds[1] and sb_sk.outbounds[1].password == "p")
local sb_ht = util.json_decode(output.generate({ http_node }, "singbox"))
check("singbox http username", sb_ht and sb_ht.outbounds[1] and sb_ht.outbounds[1].username == "hu")
check("singbox http password", sb_ht and sb_ht.outbounds[1] and sb_ht.outbounds[1].password == "hp")
-- ============================================================
-- 10. Surge 家族:socks / http 凭据
-- ============================================================
local sg_sk = output.generate({ sk_node }, "surge")
check("surge socks username", sg_sk and sg_sk:find("username=u", 1, true) ~= nil)
check("surge socks password", sg_sk and sg_sk:find("password=p", 1, true) ~= nil)
local sg_ht = output.generate({ http_node }, "surge")
check("surge http username", sg_ht and sg_ht:find("username=hu", 1, true) ~= nil)
check("surge http password", sg_ht and sg_ht:find("password=hp", 1, true) ~= nil)
-- ============================================================
-- 11. core.merge_form_node 能清空 username(FORM_KEYS 必须含 username)
-- ============================================================
local old_node = { proto = "socks", name = "N", server = "1.1.1.1", port = 1080,
username = "olduser", password = "oldpass" }
local merged = core.merge_form_node(old_node, { proto = "socks", name = "N", server = "1.1.1.1", port = 1080,
password = "newpass" })
check("merge_form_node 清空 username", merged and merged.username == nil)
check("merge_form_node 更新 password", merged and merged.password == "newpass")
-- ---------- 结果 ----------
print(string.format("\n%d passed, %d failed", passed, failed))
os.exit(failed == 0 and 0 or 1)
+24
View File
@@ -120,6 +120,30 @@ check("detect BOM prefix", parser.detect(bom_sub) == "base64")
local bom_res = parser.parse(bom_sub)
check("parse BOM count", bom_res and #bom_res.nodes == 1)
-- H5:base64 里包着 YAML / JSON(机场把整份配置 base64 后直接下发)
-- 原先一律按 URI 列表解析,这类订阅会得到 0 个节点且不报错
local b64_yaml = parser.parse(util.base64_encode([[
proxies:
- name: BY
type: vmess
server: 6.6.6.6
port: 443
uuid: u
]]))
check("base64 yaml node count", b64_yaml and #b64_yaml.nodes == 1)
check("base64 yaml node server", b64_yaml and b64_yaml.nodes[1] and b64_yaml.nodes[1].server == "6.6.6.6")
check("base64 yaml keeps outer format", b64_yaml and b64_yaml.format == "base64")
local b64_json = parser.parse(util.base64_encode(
'{"outbounds":[{"type":"vmess","tag":"BJ","server":"7.7.7.7","server_port":443,"uuid":"u"}]}'))
check("base64 json node count", b64_json and #b64_json.nodes == 1)
check("base64 json node server", b64_json and b64_json.nodes[1] and b64_json.nodes[1].server == "7.7.7.7")
check("base64 json keeps outer format", b64_json and b64_json.format == "base64")
-- base64 套 base64:不得无限递归
local nested = parser.parse(util.base64_encode(util.base64_encode(plain)))
check("nested base64 does not hang", nested ~= nil)
-- ---------- 结果 ----------
print(string.format("\n%d passed, %d failed", passed, failed))
os.exit(failed == 0 and 0 or 1)
@@ -482,5 +482,22 @@ do
end
-- ---------- 结果 ----------
-- H7:.conf 的注释行同样要压成单行。节点名来自订阅(不可信输入),含换行时
-- 会从注释里注入出真正的配置行,伪造出一段用户没写过的隧道配置。
local wgconf_out = require("substore.output_wireguard_conf")
local wg_evil = wgconf_out.generate({ { proto = "wireguard", name = "x\n[Interface]\nPrivateKey = injected",
server = "1.2.3.4", port = 51820, ["private-key"] = "PRIV", ["public-key"] = "PUB" } })
-- 注入文本只能留在注释行里:既不能出现真正的 [Interface] 段头,也不能出现
-- 以 "PrivateKey = injected" 开头的新行
check("wgconf comment not injected", wg_evil:find("\n[Interface]\nPrivateKey = injected", 1, true) == nil)
check("wgconf injected text stays in comment", wg_evil:find("\nPrivateKey = injected", 1, true) == nil)
local real_iface = 0
for l in wg_evil:gmatch("[^\n]+") do
if l == "[Interface]" then real_iface = real_iface + 1 end
end
check("wgconf single real Interface section", real_iface == 1)
check("wgconf real private key kept", wg_evil:find("\nPrivateKey = PRIV", 1, true) ~= nil)
check("wgconf comment is one line", wg_evil:match("^# [^\n]*\n") ~= nil)
print(string.format("\n%d passed, %d failed", passed, failed))
os.exit(failed == 0 and 0 or 1)
+2 -2
View File
@@ -1,8 +1,8 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-trafficctl
PKG_VERSION:=1.18.1
PKG_RELEASE:=4
PKG_VERSION:=1.18.2
PKG_RELEASE:=5
PKG_LICENSE:=Apache-2.0
PKG_MAINTAINER:=Denis Iusupov <yusdyr@gmail.com>
@@ -3200,7 +3200,13 @@ return view.extend({
inetBtn.disabled = true;
var action = inetBtn._action;
var fn = action === 'block' ? callBlock : callUnblock;
fn(ip, '').then(function() {
fn(ip, '').then(function(res) {
// A block without a known MAC covers IPv4 only, and the backend
// says so in msg. Swallowing it here would put the UI back to
// reporting a block that does not block (issue #67).
if (res && res.ipv6 === false) {
setStatus(statusDiv, 'error', (res && res.msg) || '?');
}
runQuery();
}).catch(function(e) {
setStatus(statusDiv, 'error', e.message);
@@ -35,15 +35,33 @@ if tctl_is_blocked "$IP"; then
exit 0
fi
TCTL_BLOCK_IPV6=0
if tctl_block_add "$IP" "$COMMENT"; then
conntrack -D -s "$IP" >/dev/null 2>&1
conntrack -D -d "$IP" >/dev/null 2>&1
# The v4 flush above leaves every established IPv6 flow running, and with
# flow offload those are never re-evaluated against the new rule — the
# device would stay online over v6 indefinitely while the UI reads blocked.
MAC=$(tctl_target_mac "$IP" 2>/dev/null) && tctl_conntrack_flush_v6 "$MAC"
tctl_persist_enabled && tctl_persist_save "block" "$IP" "$LABEL"
tctl_log "block" "$IP" "$LABEL" "${TCTL_VIA:-cli}" "${TCTL_SRC:-local}"
if [ "$SELF_BLOCK" = "true" ]; then
echo "{\"ok\":true,\"msg\":\"internet blocked for $IP (your device — LuCI access preserved)\"}"
# Say which families are actually covered. Reporting plain success while
# half the traffic walks past the rule is the bug this fixes, so the
# no-MAC case is stated rather than left to be discovered (same reasoning
# as the half-applied-limit messages in trafficctl-ratelimit.sh).
if [ "$TCTL_BLOCK_IPV6" = "1" ]; then
NOTE=""
elif tctl_ip_is_nexthop "$IP"; then
NOTE=" — IPv4 only: $IP is a routed next hop, and a MAC-keyed IPv6 rule would also block everything behind it"
else
echo "{\"ok\":true,\"msg\":\"internet blocked for $IP\"}"
NOTE=" — IPv4 only: no MAC known for $IP (no DHCP lease, no neighbour entry), so IPv6 is not covered"
fi
if [ "$SELF_BLOCK" = "true" ]; then
echo "{\"ok\":true,\"ipv6\":$([ "$TCTL_BLOCK_IPV6" = "1" ] && echo true || echo false),\"msg\":\"internet blocked for $IP (your device — LuCI access preserved)$NOTE\"}"
else
echo "{\"ok\":true,\"ipv6\":$([ "$TCTL_BLOCK_IPV6" = "1" ] && echo true || echo false),\"msg\":\"internet blocked for $IP$NOTE\"}"
fi
else
echo "{\"ok\":false,\"msg\":\"failed to block $IP\"}"
@@ -127,9 +127,13 @@ BLOCK_BYTES=0
if tctl_is_blocked "$IP"; then
BLOCKED=true
if [ "$TCTL_FW" = "nft" ]; then
block_line=$(nft list chain inet fw4 forward 2>/dev/null | grep "ip saddr $IP.*drop")
BLOCK_PACKETS=$(echo "$block_line" | grep -oE 'packets [0-9]+' | awk '{print $2}')
BLOCK_BYTES=$(echo "$block_line" | grep -oE 'bytes [0-9]+' | awk '{print $2}')
# A block is two rules — the v4 one keyed on the address and the v6 one
# keyed on the MAC (see tctl_block_add) — so the counters are summed
# across both. The v6 rule carries no address, only its "_mac" comment.
block_line=$(nft list chain inet fw4 forward 2>/dev/null \
| grep -e "ip saddr $IP.*drop" -e "comment \"$(tctl_block_comment "$IP")_mac\"")
BLOCK_PACKETS=$(echo "$block_line" | grep -oE 'packets [0-9]+' | awk '{ t += $2 } END { printf "%.0f", t }')
BLOCK_BYTES=$(echo "$block_line" | grep -oE 'bytes [0-9]+' | awk '{ t += $2 } END { printf "%.0f", t }')
else
block_line=$(iptables -L FORWARD -nvx 2>/dev/null | grep "DROP" | grep "$IP")
BLOCK_PACKETS=$(echo "$block_line" | awk '{print $1}')
@@ -10,6 +10,102 @@ else
TCTL_FW="iptables"
fi
# ── MAC-keyed matching (address-family independent) ────────────────────────
#
# Everything in this package is keyed on the IPv4 address a DHCP lease gives a
# device, and every enforcement rule matches "ip saddr"/"ip daddr" — so IPv6
# walks past all of it (issue #67: 9.96 Mbit/s over v4 against a 10 Mbit/s cap,
# 151 Mbit/s over v6 to the same endpoint).
#
# The fix is NOT "add ip6 saddr next to ip saddr": with SLAAC and privacy
# extensions a client holds several v6 addresses at once and rotates them on a
# timer, so a rule written against one stops matching without warning — the
# same silent bypass, reached more slowly. The MAC does not rotate, and on the
# hooks used here the client's own frame is still intact, so it is what the v6
# rules are keyed on.
#
# Only the directions where that holds are covered; see tctl_block_add and
# tctl_ratelimit_add for which, and README/COMPATIBILITY for what is not.
# Named by absolute path, as it must be on the router; the tests sed this line
# into their scratch directory rather than have the shipped script carry a
# test-only override (same approach as the ledger and shapes paths below).
TCTL_LEASES_FILE="/tmp/dhcp.leases"
# The MAC behind an IPv4 address: DHCP lease first, neighbour table second.
# Fails (non-zero, no output) when neither knows it — a routed/downstream
# client has no lease and no neighbour entry here, and there is nothing to key
# a rule on. Callers must report that rather than assume coverage.
#
# The result is validated strictly, not merely trimmed: it is interpolated
# into an nft rule string, so anything but six hex pairs is refused.
tctl_lookup_mac() {
local addr="$1" mac
tctl_validate_ip "$addr" || return 1
mac=$(awk -v ip="$addr" '$3 == ip { print $2; exit }' "$TCTL_LEASES_FILE" 2>/dev/null)
[ -z "$mac" ] && mac=$(ip neigh show 2>/dev/null | awk -v ip="$addr" '
$1 == ip { for (i = 1; i < NF; i++) if ($i == "lladdr") { print $(i+1); exit } }')
mac=$(printf '%s' "$mac" | tr 'A-F' 'a-f')
case "$mac" in
[0-9a-f][0-9a-f]:[0-9a-f][0-9a-f]:[0-9a-f][0-9a-f]:[0-9a-f][0-9a-f]:[0-9a-f][0-9a-f]:[0-9a-f][0-9a-f]) ;;
*) return 1 ;;
esac
printf '%s\n' "$mac"
}
# True when this address is the next hop of a route, i.e. a router rather than
# an end device. Its MAC is the source of every packet it FORWARDS, so a
# MAC-keyed rule aimed at it would also hit every client behind it — blocking
# one device would black-hole a whole subnet. Those addresses are excluded and
# the caller says so out loud.
tctl_ip_is_nexthop() {
local addr="$1"
[ -n "$addr" ] || return 1
ip route show 2>/dev/null | awk -v n="$addr" '
{ for (i = 1; i < NF; i++) if ($i == "via" && $(i+1) == n) { hit = 1; exit } }
END { exit hit ? 0 : 1 }'
}
# The MAC to key a target's IPv6 rules on, or failure. A CIDR target has no
# single MAC; a next hop's MAC is not its own traffic.
tctl_target_mac() {
local t="$1"
case "$t" in
*/32) t=${t%/32} ;;
*/*) return 1 ;;
esac
tctl_ip_is_nexthop "$t" && return 1
tctl_lookup_mac "$t"
}
# Flush a device's live IPv6 flows.
#
# "conntrack -D -s <v4addr>" only touches the IPv4 table, so without this an
# established v6 flow survives a block that was just installed — and with flow
# offload it is never re-evaluated by the forward chain again, so it survives
# indefinitely. That is the same "a block that does not block" failure in a new
# place, on the offload-enabled hardware most likely to hit it.
#
# Addresses come from a one-shot neighbour snapshot. Reading them is sound even
# though a RULE written against them would not be: the snapshot cannot go stale
# between here and the next statement, whereas a rule has to stay correct for
# as long as the block lasts.
tctl_conntrack_flush_v6() {
local mac="$1" addr
[ -n "$mac" ] || return 0
command -v conntrack >/dev/null 2>&1 || return 0
ip -6 neigh show 2>/dev/null | awk -v m="$mac" '
{ for (i = 1; i < NF; i++) if ($i == "lladdr" && tolower($(i+1)) == m) { print $1; break } }
' | while read -r addr; do
case "$addr" in
''|*[!0-9a-fA-F:]*) continue ;;
esac
conntrack -D -f ipv6 -s "$addr" >/dev/null 2>&1
conntrack -D -f ipv6 -d "$addr" >/dev/null 2>&1
done
return 0
}
# ── Rate Limiting (policer) ────────────────────────────────────────────────
# Rate limits are symmetric: the same ceiling is policed in both directions.
@@ -27,7 +123,7 @@ tctl_ratelimit_add() {
local rate_kbyte=$((rate_kbit / 8))
[ "$rate_kbyte" -lt 1 ] && rate_kbyte=1
local slug dl_expr ul_expr
local slug dl_expr ul_expr ul6_expr mac
slug=$(tctl_target_slug "$ip")
if [ "$mode" = "each" ]; then
dl_expr="ip daddr $ip meter tctl_d_$slug { ip daddr limit rate over ${rate_kbyte} kbytes/second }"
@@ -37,6 +133,31 @@ tctl_ratelimit_add() {
ul_expr="ip saddr $ip limit rate over ${rate_kbyte} kbytes/second"
fi
# Upload over IPv6, keyed on the MAC. This direction and only this one:
# at LAN ingress the frame is still the client's own, so "ether saddr"
# identifies it whatever address family it is using. Download over IPv6 is
# policed at LAN egress, where the destination MAC is the next hop's — it
# needs the device's current v6 address set and is deliberately not
# attempted here.
#
# Scoped to IPv6 on purpose: IPv4 keeps being policed by exactly the rule it
# always was, so nothing about existing behaviour depends on whether this
# lookup succeeds — and an unscoped ether rule would police a dual-stack
# client's v4 traffic TWICE, halving the ceiling it was given. Its own
# bucket, too: sharing one would make the v4 and v6 halves cannibalise each
# other's allowance, which is not what "10 Mbit/s" has ever meant here.
#
# The scope is "meta protocol ip6", NOT "meta nfproto ipv6" — these chains
# are in the NETDEV family, where nft rejects nfproto outright ("meta
# nfproto is only useful in the inet family", verified with nft --check
# against nftables 1.1.1 / kernel 6.6). nfproto is what tctl_block_add uses,
# because that rule lives in inet fw4. Same intent, different family, and
# the wrong one does not silently under-match — it fails to load.
ul6_expr=""
if mac=$(tctl_target_mac "$ip"); then
ul6_expr="meta protocol ip6 ether saddr $mac limit rate over ${rate_kbyte} kbytes/second"
fi
if [ "$TCTL_FW" = "nft" ]; then
nft add table netdev tm_ratelimit 2>/dev/null
@@ -79,7 +200,7 @@ tctl_ratelimit_add() {
# One chain per ingress device: a device that refuses the hook then
# only loses its own chain instead of taking the whole set with it.
local dev chain
local ul_ok=0
local ul_ok=0 ul6_ok=0
for dev in $(tctl_ingress_devices); do
chain=$(tctl_ingress_chain "$dev")
nft add chain netdev tm_ratelimit "$chain" \
@@ -87,10 +208,21 @@ tctl_ratelimit_add() {
nft add rule netdev tm_ratelimit "$chain" \
"$ul_expr counter drop comment \"${comment}_ul\"" 2>/dev/null \
&& ul_ok=1
[ -n "$ul6_expr" ] || continue
nft add rule netdev tm_ratelimit "$chain" \
"$ul6_expr counter drop comment \"${comment}_ul6\"" 2>/dev/null \
&& ul6_ok=1
done
# shellcheck disable=SC2034
[ "$ul_ok" = "1" ] || TCTL_RL_UPLOAD_FAILED=1
# Set for the caller to report: "limited" means v4 only unless this is 1.
# shellcheck disable=SC2034
TCTL_RL_UPLOAD6_OK="$ul6_ok"
else
# fw3/iptables stays IPv4-only. A half-built ip6tables path would be
# worse than a stated gap; see docs/COMPATIBILITY.md.
# shellcheck disable=SC2034
TCTL_RL_UPLOAD6_OK=0
iptables -t mangle -A FORWARD -d "$ip" -m hashlimit \
--hashlimit-above "${rate_kbit}kbit/sec" --hashlimit-burst "${rate_kbit}kbit" \
--hashlimit-mode dstip --hashlimit-name "rl_${comment}" \
@@ -107,11 +239,18 @@ tctl_ratelimit_remove() {
local chain h
if [ "$TCTL_FW" = "nft" ]; then
# Scan the whole table once: rules for this IP live in dl (daddr) and
# in one ul_<dev> chain per ingress device (saddr).
# Scan the whole table once: rules for this IP live in dl (daddr), in
# one ul_<dev> chain per ingress device (saddr), and — for a host with
# a known MAC — a second, IPv6-scoped rule in each of those (_ul6).
#
# Each comment is matched in FULL, closing quote included. The suffixes
# are prefixes of one another ("_ul" of "_ul6"), and every comment ends
# in an address slug, so a substring match would have a limit on
# 192.168.1.1 delete 192.168.1.10's rules as well.
nft -a list table netdev tm_ratelimit 2>/dev/null | awk -v cmt="$comment" '
/^[ \t]*chain [a-zA-Z0-9_]+ \{/ { chain = $2; next }
index($0, "\"" cmt "\"") || index($0, "\"" cmt "_ul\"") {
index($0, "\"" cmt "\"") || index($0, "\"" cmt "_ul\"") ||
index($0, "\"" cmt "_ul6\"") {
for (i = 1; i < NF; i++)
if ($i == "handle") { print chain, $(i+1); break }
}' | while read -r chain h; do
@@ -133,12 +272,45 @@ tctl_ratelimit_list() {
# ── Internet Blocking ──────────────────────────────────────────────────────
# A block is two rules: the address-keyed one for IPv4, and a MAC-keyed one
# scoped to IPv6.
#
# This is the row of issue #67 that matters most. A rate limit that under-
# delivers shows up in a speed test; a block that reports success while the
# device keeps full, unmetered IPv6 access is invisible until it matters — and
# "block internet" is what people reach for as a parental control.
#
# The MAC is resolved HERE rather than in trafficctl-block.sh because three
# callers reach this function: the block script, the Telegram bot, and the
# ifup-lan restore hook. Resolving it in the script would bring every persisted
# block back IPv4-only after each reboot.
#
# "meta nfproto ipv6" keeps the two rules disjoint: IPv4 is still dropped by
# precisely the rule it always was, so a failed MAC lookup costs the v6 half
# and nothing else. (nfproto is correct HERE, in the inet family; the limiter's
# netdev chains need "meta protocol ip6" instead — see tctl_ratelimit_add.)
# `insert`, not `add`, for the same reason the v4 rule uses it — fw4's forward
# chain accepts established/offloaded flows near the top, and a rule appended
# below that is dead.
tctl_block_add() {
local ip="$1" comment="$2"
local ip="$1" comment="$2" mac rc
if [ "$TCTL_FW" = "nft" ]; then
nft insert rule inet fw4 forward "ip saddr $ip counter drop comment \"$comment\""
rc=$?
# Whether the v6 half is live. Read by callers; see trafficctl-block.sh.
# shellcheck disable=SC2034
TCTL_BLOCK_IPV6=0
if mac=$(tctl_target_mac "$ip"); then
nft insert rule inet fw4 forward \
"meta nfproto ipv6 ether saddr $mac counter drop comment \"${comment}_mac\"" \
2>/dev/null && TCTL_BLOCK_IPV6=1
fi
return "$rc"
else
# fw3/iptables stays IPv4-only; see docs/COMPATIBILITY.md.
# shellcheck disable=SC2034
TCTL_BLOCK_IPV6=0
iptables -I FORWARD -s "$ip" -j DROP -m comment --comment "$comment"
fi
}
@@ -149,8 +321,12 @@ tctl_block_remove() {
if [ "$TCTL_FW" = "nft" ]; then
# Match the quoted comment in full: comments end in the address, so a
# substring match on 192.168.1.1 also deletes the rule for 192.168.1.10.
# The IPv6 half carries no address to match on at all, only its own
# "_mac" comment — which is why it is listed explicitly here rather
# than being caught by a looser pattern.
for h in $(nft -a list chain inet fw4 forward 2>/dev/null \
| awk -v cmt="$comment" 'index($0, "\"" cmt "\"") {
| awk -v cmt="$comment" 'index($0, "\"" cmt "\"") ||
index($0, "\"" cmt "_mac\"") {
for (i = 1; i < NF; i++)
if ($i == "handle") { print $(i+1); break }
}'); do
@@ -162,9 +338,17 @@ tctl_block_remove() {
}
tctl_is_blocked() {
local ip="$1"
local ip="$1" dump comment
if [ "$TCTL_FW" = "nft" ]; then
nft list chain inet fw4 forward 2>/dev/null | grep -q "ip saddr $ip .*drop"
dump=$(nft list chain inet fw4 forward 2>/dev/null)
# The v4 rule is still found by address, so blocks written by versions
# that derived the comment from the caller's label keep reading as
# blocked.
echo "$dump" | grep -q "ip saddr $ip .*drop" && return 0
# The v6 rule names a MAC, not an address — its comment is the only
# handle on it, and it is matched in full for the reason above.
comment=$(tctl_block_comment "$ip")
echo "$dump" | grep -qF "comment \"${comment}_mac\""
else
# The source renders as its own column, "ip" or "ip/32"; an unanchored
# match would report 192.168.1.10's rule as belonging to 192.168.1.1.
@@ -699,7 +883,7 @@ tctl_persist_remove() {
# traffic: an entry is appended only for a MAC that is not already listed.
TCTL_SEEN_FILE="/etc/trafficctl/seen_macs"
TCTL_SEEN_MAX=1000
TCTL_LEASES_FILE="/tmp/dhcp.leases"
# TCTL_LEASES_FILE is set once at the top of this file (overridable for tests).
TCTL_SHAPES_FILE="/etc/trafficctl/shapes.json"
tctl_seen_normalize() {
@@ -59,8 +59,19 @@ else
tctl_ratelimit_remove "$IP" "$COMMENT" 2>/dev/null
TCTL_RL_DOWNLOAD_FAILED=0
TCTL_RL_UPLOAD_FAILED=0
TCTL_RL_UPLOAD6_OK=0
tctl_ratelimit_add "$IP" "$RATE" "$COMMENT" "$MODE"
# IPv6 coverage is partial by construction and the operator has to know
# which part: upload is policed on the MAC, download is not policed at all
# over v6 (see tctl_ratelimit_add). Claiming "both directions" without
# this note is how issue #67 stayed invisible.
if [ "$TCTL_RL_UPLOAD6_OK" = "1" ]; then
V6NOTE=" [IPv6: upload only]"
else
V6NOTE=" [IPv4 only]"
fi
# A half-applied limit is a silent trap: report exactly which direction
# is live rather than claiming success for both.
if [ "$TCTL_RL_DOWNLOAD_FAILED" = "1" ] && [ "$TCTL_RL_UPLOAD_FAILED" = "1" ]; then
@@ -70,10 +81,10 @@ else
tctl_persist_enabled && tctl_persist_save "ratelimit" "$IP" "$RATE"
tctl_log "ratelimit_set" "$IP" "${RATE}kbit" "${TCTL_VIA:-cli}" "${TCTL_SRC:-local}"
if [ "$TCTL_RL_DOWNLOAD_FAILED" = "1" ]; then
echo "{\"ok\":true,\"msg\":\"rate limit ${RATE} kbit/s applied to $IP UPLOAD ONLY — WAN device not resolvable\"}"
echo "{\"ok\":true,\"ipv6_upload\":$([ "$TCTL_RL_UPLOAD6_OK" = "1" ] && echo true || echo false),\"msg\":\"rate limit ${RATE} kbit/s applied to $IP UPLOAD ONLY — WAN device not resolvable$V6NOTE\"}"
elif [ "$TCTL_RL_UPLOAD_FAILED" = "1" ]; then
echo "{\"ok\":true,\"msg\":\"rate limit ${RATE} kbit/s applied to $IP DOWNLOAD ONLY — no LAN ingress device\"}"
echo "{\"ok\":true,\"ipv6_upload\":false,\"msg\":\"rate limit ${RATE} kbit/s applied to $IP DOWNLOAD ONLY — no LAN ingress device [IPv4 only]\"}"
else
echo "{\"ok\":true,\"msg\":\"rate limit ${RATE} kbit/s for $IP (both directions, $MODE)\"}"
echo "{\"ok\":true,\"ipv6_upload\":$([ "$TCTL_RL_UPLOAD6_OK" = "1" ] && echo true || echo false),\"msg\":\"rate limit ${RATE} kbit/s for $IP (both directions, $MODE)$V6NOTE\"}"
fi
fi
@@ -303,7 +303,12 @@ lookup_mac() {
lookup_blocked() {
local ip="$1"
if [ "$TCTL_FW" = "nft" ]; then
echo "$FWD_DUMP" | grep -q "ip saddr $ip .*drop" && echo 1 || echo 0
echo "$FWD_DUMP" | grep -q "ip saddr $ip .*drop" && { echo 1; return; }
# The IPv6 half of a block matches a MAC and carries no address, so it
# is found by its own full comment. Without this the table reports "not
# blocked" for a device whose v6 rule is the only one left standing.
echo "$FWD_DUMP" | grep -qF "comment \"$(tctl_block_comment "$ip")_mac\"" \
&& echo 1 || echo 0
else
echo "$FWD_DUMP" | awk -v ip="$ip" '
$3 == "DROP" {
@@ -320,7 +325,13 @@ lookup_blocked() {
lookup_block_bytes() {
local ip="$1" b
if [ "$TCTL_FW" = "nft" ]; then
b=$(echo "$FWD_DUMP" | grep "ip saddr $ip " | grep -oE 'bytes [0-9]+' | awk '{print $2}' | head -1)
# Both halves of the block, summed: the v4 rule keyed on the address
# and the v6 rule keyed on the MAC. Counting only the first would make
# a dual-stack device's dropped bytes read low for the same reason the
# traffic used to escape entirely.
b=$(echo "$FWD_DUMP" | grep -e "ip saddr $ip " \
-e "comment \"$(tctl_block_comment "$ip")_mac\"" \
| grep -oE 'bytes [0-9]+' | awk '{ t += $2 } END { printf "%.0f", t }')
else
b=$(echo "$FWD_DUMP" | awk -v ip="$ip" '
$3 == "DROP" {
+3 -3
View File
@@ -17,12 +17,12 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-wwand
PKG_VERSION:=1.6.8_p17
PKG_RELEASE:=34
PKG_VERSION:=1.6.8_p18
PKG_RELEASE:=35
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/ddimension/luci-app-wwand.git
PKG_SOURCE_VERSION:=376f2dc5c658f06f88cb3b917e65e9e7d58a6e85
PKG_SOURCE_VERSION:=355e135f79b954c7fc01d6135a11d60adcaf04a7
PKG_MIRROR_HASH:=skip
PKG_LICENSE:=GPL-2.0-only
+55 -52
View File
@@ -1,7 +1,7 @@
msgid ""
msgstr ""
"Project-Id-Version: \n"
"PO-Revision-Date: 2026-03-08 17:32+0000\n"
"PO-Revision-Date: 2026-09-28 18:23+0000\n"
"Last-Translator: Pavel Borecki <pavel.borecki@gmail.com>\n"
"Language-Team: Czech <https://hosted.weblate.org/projects/openwrt/"
"lucimodulesluci-mod-dashboard/cs/>\n"
@@ -10,39 +10,39 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=((n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2);\n"
"X-Generator: Weblate 5.17-dev\n"
"X-Generator: Weblate 2026.10.dev0\n"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:97
msgid "%d clients"
msgstr ""
msgstr "%d klientů"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:89
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:124
msgid "%d core"
msgid_plural "%d cores"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
msgstr[0] "%d jádro"
msgstr[1] "%d jádra"
msgstr[2] "%d jader"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/charts.js:277
msgid "%d min ago"
msgstr ""
msgstr "před %d min"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:72
msgid "%d of %d SSIDs active"
msgstr ""
msgstr "%d z %d SSID identifikátorů aktivních"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/charts.js:277
msgid "%d s ago"
msgstr ""
msgstr "před %d s"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:108
msgid "15 min: %.2f"
msgstr ""
msgstr "15 min: %.2f"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:108
msgid "5 min: %.2f"
msgstr ""
msgstr "5 min: %.2f"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:322
msgid "Active"
@@ -50,7 +50,7 @@ msgstr "Aktivní"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/20_lan.js:40
msgid "Active leases"
msgstr ""
msgstr "Aktivní zápůjčky"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/10_router.js:295
msgid "Architecture"
@@ -63,7 +63,7 @@ msgstr "BSSID"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:218
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:372
msgid "Band"
msgstr ""
msgstr "Frekvenční pásmo"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:334
msgid "Bitrate"
@@ -71,22 +71,22 @@ msgstr "Bitová rychlost"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:173
msgid "Buffered"
msgstr ""
msgstr "Ve vyrovnávací paměti"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:141
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:166
msgid "CPU"
msgstr ""
msgstr "Procesor"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:43
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:87
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:162
msgid "CPU usage"
msgstr ""
msgstr "Vytížení procesoru"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:176
msgid "Cached"
msgstr ""
msgstr "V mezipaměti"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:328
msgid "Channel"
@@ -94,7 +94,7 @@ msgstr "Kanál"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/history.js:52
msgid "Collecting data..."
msgstr ""
msgstr "Shromažďování údajů…"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/10_router.js:72
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/10_router.js:104
@@ -126,7 +126,7 @@ msgstr "Rychlé info"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/layout.js:13
msgid "Detail tabs"
msgstr ""
msgstr "Karty podrobností"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:352
msgid "Devices Connected"
@@ -163,11 +163,11 @@ msgstr "Brána v6"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/charts.js:25
msgid "Gbit/s"
msgstr ""
msgstr "Gbit/s"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:111
msgid "Good"
msgstr ""
msgstr "Dobrý"
#: modules/luci-mod-dashboard/root/usr/share/rpcd/acl.d/luci-mod-dashboard.json:31
msgid "Grant access to DHCP status display"
@@ -219,7 +219,7 @@ msgstr "IPv6 předpona"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:206
msgid "Interface is disabled"
msgstr ""
msgstr "Rozhraní je zakázáno"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/10_router.js:21
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/10_router.js:23
@@ -230,7 +230,7 @@ msgstr "Internet"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/charts.js:25
msgid "Kbit/s"
msgstr ""
msgstr "Kbit/s"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/10_router.js:285
msgid "Kernel Version"
@@ -239,21 +239,21 @@ msgstr "Verze jádra systému"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/index.js:251
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/index.js:298
msgid "Layout"
msgstr ""
msgstr "Rozložení"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/layout.js:12
msgid "Live charts"
msgstr ""
msgstr "Živé grafy"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:45
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:106
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:154
msgid "Load average"
msgstr ""
msgstr "Průměrné vytížení"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/index.js:298
msgid "Loading view…"
msgstr ""
msgstr "Načítání pohledu…"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/10_router.js:280
msgid "Local Time"
@@ -272,11 +272,11 @@ msgstr "Mbitů/s"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:97
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:142
msgid "Memory"
msgstr ""
msgstr "Operační paměť"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:170
msgid "Memory used"
msgstr ""
msgstr "Využití operační paměti"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/10_router.js:290
msgid "Model"
@@ -285,31 +285,31 @@ msgstr "Model"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:119
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:231
msgid "No RX signal"
msgstr ""
msgstr "Žádný signál příjmu"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/20_lan.js:40
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/20_lan.js:53
msgid "No active leases"
msgstr ""
msgstr "Žádné aktivní zápůjčky"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/history.js:52
msgid "No data received"
msgstr ""
msgstr "Neobdrženy žádné údaje"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:88
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:113
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:180
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:244
msgid "No wireless clients connected"
msgstr ""
msgstr "Nepřipojení žádní WiFi klienti"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:219
msgid "Noise floor"
msgstr ""
msgstr "Práh šumu"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/10_router.js:74
msgid "Not configured or no address acquired"
msgstr ""
msgstr "Nenastaveno nebo není žádná adresa zapotřebí"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/10_router.js:72
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/10_router.js:104
@@ -327,7 +327,7 @@ msgstr "Protokol"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:47
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:202
msgid "Resources"
msgstr ""
msgstr "Prostředky"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:217
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:316
@@ -337,11 +337,11 @@ msgstr "SSID"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/layout.js:61
msgid "Save & Apply"
msgstr ""
msgstr "Uložit a použít"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:219
msgid "Signal"
msgstr ""
msgstr "Signál"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:35
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:96
@@ -351,11 +351,11 @@ msgstr "Síla signálu"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/layout.js:11
msgid "Status cards"
msgstr ""
msgstr "Stavové karty"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:180
msgid "Swap used"
msgstr ""
msgstr "Využití odkládacího prostoru (swap)"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/10_router.js:24
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/10_router.js:317
@@ -366,13 +366,16 @@ msgstr "Systém"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:130
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:137
msgid "System load"
msgstr ""
msgstr "Vytížení systému"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/layout.js:18
msgid ""
"The lists follow the page from top to bottom. What is in a list is shown, in "
"that order: drag an entry to move it. An empty list shows the defaults."
msgstr ""
"Seznamy následují stránku odshora dolů. Co je v seznamu je zobrazeno, v "
"takovém pořadí: pokud chcete položku přesunout, přetáhněte ji. Prázdný "
"seznam zobrazuje výchozí."
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/20_lan.js:54
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:245
@@ -402,11 +405,11 @@ msgstr "Doba chodu"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/15_traffic.js:10
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/15_traffic.js:13
msgid "WAN traffic"
msgstr ""
msgstr "WAN provoz"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:112
msgid "Weak"
msgstr ""
msgstr "Slabý"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:28
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:37
@@ -416,31 +419,31 @@ msgstr "Bezdrátová síť"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:36
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:159
msgid "Wireless client data usage"
msgstr ""
msgstr "Využití dat WiFi klientem"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:34
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:84
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:87
msgid "Wireless client distribution"
msgstr ""
msgstr "Rozprostření WiFi klienta"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:33
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:70
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:288
msgid "Wireless clients"
msgstr ""
msgstr "WiFi klienti"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/charts.js:25
msgid "bit/s"
msgstr ""
msgstr "bit/s"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:85
msgid "by SSID"
msgstr ""
msgstr "podle SSID"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:87
msgid "clients"
msgstr ""
msgstr "klienti"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:97
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:111
@@ -450,15 +453,15 @@ msgstr ""
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:231
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:234
msgid "dBm"
msgstr ""
msgstr "dBm"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:89
msgid "load %.2f"
msgstr ""
msgstr "vytížení %.2f"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/charts.js:278
msgid "now"
msgstr ""
msgstr "nyní"
#~ msgid "Strength"
#~ msgstr "Síla"
+55 -52
View File
@@ -1,8 +1,8 @@
msgid ""
msgstr ""
"Project-Id-Version: \n"
"PO-Revision-Date: 2026-06-27 12:02+0000\n"
"Last-Translator: apemay <apemay.dev@gmail.com>\n"
"PO-Revision-Date: 2026-09-27 20:14+0000\n"
"Last-Translator: Franco Castillo <castillofrancodamian@gmail.com>\n"
"Language-Team: Spanish <https://hosted.weblate.org/projects/openwrt/"
"lucimodulesluci-mod-dashboard/es/>\n"
"Language: es\n"
@@ -10,38 +10,38 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 2026.7.dev0\n"
"X-Generator: Weblate 2026.10.dev0\n"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:97
msgid "%d clients"
msgstr ""
msgstr "%d clientes"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:89
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:124
msgid "%d core"
msgid_plural "%d cores"
msgstr[0] ""
msgstr[1] ""
msgstr[0] "%d núcleo"
msgstr[1] "%d núcleos"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/charts.js:277
msgid "%d min ago"
msgstr ""
msgstr "Hace %d minutos"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:72
msgid "%d of %d SSIDs active"
msgstr ""
msgstr "%d de %d SSID activos"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/charts.js:277
msgid "%d s ago"
msgstr ""
msgstr "Hace %d s"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:108
msgid "15 min: %.2f"
msgstr ""
msgstr "15 minutos: %.2f"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:108
msgid "5 min: %.2f"
msgstr ""
msgstr "5 minutos: %.2f"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:322
msgid "Active"
@@ -49,7 +49,7 @@ msgstr "Activo"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/20_lan.js:40
msgid "Active leases"
msgstr ""
msgstr "Asignaciones activas"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/10_router.js:295
msgid "Architecture"
@@ -62,7 +62,7 @@ msgstr "BSSID"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:218
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:372
msgid "Band"
msgstr ""
msgstr "Banda"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:334
msgid "Bitrate"
@@ -70,22 +70,22 @@ msgstr "Tasa de bits"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:173
msgid "Buffered"
msgstr ""
msgstr "En búfer"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:141
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:166
msgid "CPU"
msgstr ""
msgstr "CPU"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:43
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:87
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:162
msgid "CPU usage"
msgstr ""
msgstr "Uso de CPU"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:176
msgid "Cached"
msgstr ""
msgstr "En caché"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:328
msgid "Channel"
@@ -93,7 +93,7 @@ msgstr "Canal"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/history.js:52
msgid "Collecting data..."
msgstr ""
msgstr "Recopilando datos..."
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/10_router.js:72
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/10_router.js:104
@@ -125,7 +125,7 @@ msgstr "Panel"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/layout.js:13
msgid "Detail tabs"
msgstr ""
msgstr "Pestañas de detalles"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:352
msgid "Devices Connected"
@@ -162,11 +162,11 @@ msgstr "GatewayV6"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/charts.js:25
msgid "Gbit/s"
msgstr ""
msgstr "Gbit/s"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:111
msgid "Good"
msgstr ""
msgstr "Buena"
#: modules/luci-mod-dashboard/root/usr/share/rpcd/acl.d/luci-mod-dashboard.json:31
msgid "Grant access to DHCP status display"
@@ -218,7 +218,7 @@ msgstr "Prefijo IPv6"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:206
msgid "Interface is disabled"
msgstr ""
msgstr "La interfaz está desactivada"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/10_router.js:21
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/10_router.js:23
@@ -229,7 +229,7 @@ msgstr "Internet"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/charts.js:25
msgid "Kbit/s"
msgstr ""
msgstr "Kbit/s"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/10_router.js:285
msgid "Kernel Version"
@@ -238,21 +238,21 @@ msgstr "Versión del núcleo"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/index.js:251
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/index.js:298
msgid "Layout"
msgstr ""
msgstr "Disposición"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/layout.js:12
msgid "Live charts"
msgstr ""
msgstr "Gráficos en tiempo real"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:45
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:106
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:154
msgid "Load average"
msgstr ""
msgstr "Carga promedio"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/index.js:298
msgid "Loading view…"
msgstr ""
msgstr "Cargando vista…"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/10_router.js:280
msgid "Local Time"
@@ -271,11 +271,11 @@ msgstr "Mbit/s"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:97
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:142
msgid "Memory"
msgstr ""
msgstr "Memoria"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:170
msgid "Memory used"
msgstr ""
msgstr "Memoria utilizada"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/10_router.js:290
msgid "Model"
@@ -284,31 +284,31 @@ msgstr "Modelo"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:119
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:231
msgid "No RX signal"
msgstr ""
msgstr "SIn señal RX"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/20_lan.js:40
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/20_lan.js:53
msgid "No active leases"
msgstr ""
msgstr "Sin asignaciones activas"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/history.js:52
msgid "No data received"
msgstr ""
msgstr "No se recibieron datos"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:88
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:113
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:180
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:244
msgid "No wireless clients connected"
msgstr ""
msgstr "No hay clientes inalámbricos conectados"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:219
msgid "Noise floor"
msgstr ""
msgstr "Nivel de ruido"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/10_router.js:74
msgid "Not configured or no address acquired"
msgstr ""
msgstr "No configurado o no se ha obtenido ninguna dirección"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/10_router.js:72
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/10_router.js:104
@@ -326,7 +326,7 @@ msgstr "Protocolo"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:47
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:202
msgid "Resources"
msgstr ""
msgstr "Recursos"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:217
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:316
@@ -336,11 +336,11 @@ msgstr "SSID"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/layout.js:61
msgid "Save & Apply"
msgstr ""
msgstr "Guardar y aplicar"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:219
msgid "Signal"
msgstr ""
msgstr "Señal"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:35
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:96
@@ -350,11 +350,11 @@ msgstr "Intensidad de la señal"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/layout.js:11
msgid "Status cards"
msgstr ""
msgstr "Tarjetas de estado"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:180
msgid "Swap used"
msgstr ""
msgstr "Intercambio usado"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/10_router.js:24
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/10_router.js:317
@@ -365,13 +365,16 @@ msgstr "Sistema"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:130
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:137
msgid "System load"
msgstr ""
msgstr "Carga del sistema"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/layout.js:18
msgid ""
"The lists follow the page from top to bottom. What is in a list is shown, in "
"that order: drag an entry to move it. An empty list shows the defaults."
msgstr ""
"Las listas se muestran de arriba hacia abajo. El contenido de cada lista se "
"muestra en ese orden: arrastre una entrada para moverla. Una lista vacía "
"muestra los valores predeterminados."
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/20_lan.js:54
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:245
@@ -401,11 +404,11 @@ msgstr "Tiempo de actividad"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/15_traffic.js:10
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/15_traffic.js:13
msgid "WAN traffic"
msgstr ""
msgstr "Tráfico WAN"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:112
msgid "Weak"
msgstr ""
msgstr "Débil"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:28
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:37
@@ -415,31 +418,31 @@ msgstr "Wi-Fi"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:36
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:159
msgid "Wireless client data usage"
msgstr ""
msgstr "Uso de datos del cliente inalámbrico"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:34
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:84
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:87
msgid "Wireless client distribution"
msgstr ""
msgstr "Distribución de clientes inalámbricos"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:33
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:70
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:288
msgid "Wireless clients"
msgstr ""
msgstr "Clientes inalámbricos"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/charts.js:25
msgid "bit/s"
msgstr ""
msgstr "bit/s"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:85
msgid "by SSID"
msgstr ""
msgstr "por SSID"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:87
msgid "clients"
msgstr ""
msgstr "clientes"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:97
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:111
@@ -449,15 +452,15 @@ msgstr ""
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:231
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:234
msgid "dBm"
msgstr ""
msgstr "dBm"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:89
msgid "load %.2f"
msgstr ""
msgstr "carga %.2f"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/charts.js:278
msgid "now"
msgstr ""
msgstr "ahora"
#~ msgid "Strength"
#~ msgstr "Intensidad"
+55 -52
View File
@@ -1,7 +1,7 @@
msgid ""
msgstr ""
"Project-Id-Version: \n"
"PO-Revision-Date: 2026-08-03 04:18+0000\n"
"PO-Revision-Date: 2026-09-27 19:33+0000\n"
"Last-Translator: Matthaiks <kitynska@gmail.com>\n"
"Language-Team: Polish <https://hosted.weblate.org/projects/openwrt/"
"lucimodulesluci-mod-dashboard/pl/>\n"
@@ -11,39 +11,39 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
"|| n%100>=20) ? 1 : 2);\n"
"X-Generator: Weblate 2026.8.dev0\n"
"X-Generator: Weblate 2026.10.dev0\n"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:97
msgid "%d clients"
msgstr ""
msgstr "%d klientów"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:89
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:124
msgid "%d core"
msgid_plural "%d cores"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
msgstr[0] "%d rdzeń"
msgstr[1] "%d rdzenie"
msgstr[2] "%d rdzeni"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/charts.js:277
msgid "%d min ago"
msgstr ""
msgstr "%d min. temu"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:72
msgid "%d of %d SSIDs active"
msgstr ""
msgstr "Aktywnych %d z %d identyfikatorów SSID"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/charts.js:277
msgid "%d s ago"
msgstr ""
msgstr "%d sek. temu"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:108
msgid "15 min: %.2f"
msgstr ""
msgstr "15 min: %.2f"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:108
msgid "5 min: %.2f"
msgstr ""
msgstr "5 min: %.2f"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:322
msgid "Active"
@@ -51,7 +51,7 @@ msgstr "Aktywna"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/20_lan.js:40
msgid "Active leases"
msgstr ""
msgstr "Aktywne dzierżawy"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/10_router.js:295
msgid "Architecture"
@@ -64,7 +64,7 @@ msgstr "BSSID"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:218
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:372
msgid "Band"
msgstr ""
msgstr "Pasmo"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:334
msgid "Bitrate"
@@ -72,22 +72,22 @@ msgstr "Szybkość transmisji"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:173
msgid "Buffered"
msgstr ""
msgstr "Buforowana"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:141
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:166
msgid "CPU"
msgstr ""
msgstr "CPU"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:43
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:87
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:162
msgid "CPU usage"
msgstr ""
msgstr "Użycie CPU"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:176
msgid "Cached"
msgstr ""
msgstr "Podręczna"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:328
msgid "Channel"
@@ -95,7 +95,7 @@ msgstr "Kanał"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/history.js:52
msgid "Collecting data..."
msgstr ""
msgstr "Trwa zbieranie danych..."
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/10_router.js:72
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/10_router.js:104
@@ -127,7 +127,7 @@ msgstr "Info panel"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/layout.js:13
msgid "Detail tabs"
msgstr ""
msgstr "Karty szczegółów"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:352
msgid "Devices Connected"
@@ -164,11 +164,11 @@ msgstr "BramaV6"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/charts.js:25
msgid "Gbit/s"
msgstr ""
msgstr "Gbit/s"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:111
msgid "Good"
msgstr ""
msgstr "Dobre"
#: modules/luci-mod-dashboard/root/usr/share/rpcd/acl.d/luci-mod-dashboard.json:31
msgid "Grant access to DHCP status display"
@@ -220,7 +220,7 @@ msgstr "Prefiks IPv6"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:206
msgid "Interface is disabled"
msgstr ""
msgstr "Interfejs jest wyłączony"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/10_router.js:21
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/10_router.js:23
@@ -231,7 +231,7 @@ msgstr "Internet"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/charts.js:25
msgid "Kbit/s"
msgstr ""
msgstr "Kbit/s"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/10_router.js:285
msgid "Kernel Version"
@@ -240,21 +240,21 @@ msgstr "Wersja kernela"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/index.js:251
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/index.js:298
msgid "Layout"
msgstr ""
msgstr "Układ"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/layout.js:12
msgid "Live charts"
msgstr ""
msgstr "Wykresy na żywo"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:45
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:106
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:154
msgid "Load average"
msgstr ""
msgstr "Średnie obciążenie"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/index.js:298
msgid "Loading view…"
msgstr ""
msgstr "Ładowanie widoku…"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/10_router.js:280
msgid "Local Time"
@@ -273,11 +273,11 @@ msgstr "Mbit/s"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:97
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:142
msgid "Memory"
msgstr ""
msgstr "Pamięć"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:170
msgid "Memory used"
msgstr ""
msgstr "Użyta pamięć"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/10_router.js:290
msgid "Model"
@@ -286,31 +286,31 @@ msgstr "Model"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:119
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:231
msgid "No RX signal"
msgstr ""
msgstr "Brak sygnału RX"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/20_lan.js:40
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/20_lan.js:53
msgid "No active leases"
msgstr ""
msgstr "Brak aktywnych dzierżaw"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/history.js:52
msgid "No data received"
msgstr ""
msgstr "Nie otrzymano danych"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:88
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:113
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:180
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:244
msgid "No wireless clients connected"
msgstr ""
msgstr "Brak podłączonych klientów Wi-Fi"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:219
msgid "Noise floor"
msgstr ""
msgstr "Poziom szumu tła"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/10_router.js:74
msgid "Not configured or no address acquired"
msgstr ""
msgstr "Nie skonfigurowano lub nie uzyskano adresu"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/10_router.js:72
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/10_router.js:104
@@ -328,7 +328,7 @@ msgstr "Protokół"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:47
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:202
msgid "Resources"
msgstr ""
msgstr "Zasoby"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:217
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:316
@@ -338,11 +338,11 @@ msgstr "SSID"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/layout.js:61
msgid "Save & Apply"
msgstr ""
msgstr "Zapisz i zastosuj"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:219
msgid "Signal"
msgstr ""
msgstr "Sygnał"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:35
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:96
@@ -352,11 +352,11 @@ msgstr "Siła sygnału"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/layout.js:11
msgid "Status cards"
msgstr ""
msgstr "Karty statusu"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:180
msgid "Swap used"
msgstr ""
msgstr "Użyta pamięć wymiany"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/10_router.js:24
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/10_router.js:317
@@ -367,13 +367,16 @@ msgstr "System"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:130
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:137
msgid "System load"
msgstr ""
msgstr "Obciążenie systemu"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/layout.js:18
msgid ""
"The lists follow the page from top to bottom. What is in a list is shown, in "
"that order: drag an entry to move it. An empty list shows the defaults."
msgstr ""
"Listy biegną wzdłuż strony od góry do dołu. Zawartość listy jest wyświetlana "
"w określonej kolejności; aby przenieść element, należy go przeciągnąć. Pusta "
"lista prezentuje ustawienia domyślne."
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/20_lan.js:54
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:245
@@ -403,11 +406,11 @@ msgstr "Czas pracy"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/15_traffic.js:10
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/15_traffic.js:13
msgid "WAN traffic"
msgstr ""
msgstr "Ruch WAN"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:112
msgid "Weak"
msgstr ""
msgstr "Słabe"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:28
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:37
@@ -417,31 +420,31 @@ msgstr "Wi-Fi"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:36
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:159
msgid "Wireless client data usage"
msgstr ""
msgstr "Użycie danych klientów Wi-Fi"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:34
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:84
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:87
msgid "Wireless client distribution"
msgstr ""
msgstr "Rozkład klientów Wi-Fi"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:33
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:70
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:288
msgid "Wireless clients"
msgstr ""
msgstr "Klienty Wi-Fi"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/charts.js:25
msgid "bit/s"
msgstr ""
msgstr "bit/s"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:85
msgid "by SSID"
msgstr ""
msgstr "wg SSID"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:87
msgid "clients"
msgstr ""
msgstr "klienty"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:97
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:111
@@ -451,15 +454,15 @@ msgstr ""
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:231
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:234
msgid "dBm"
msgstr ""
msgstr "dBm"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:89
msgid "load %.2f"
msgstr ""
msgstr "obciążenie %.2f"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/charts.js:278
msgid "now"
msgstr ""
msgstr "teraz"
#~ msgid "Strength"
#~ msgstr "Siła"
+8 -8
View File
@@ -1,8 +1,8 @@
msgid ""
msgstr ""
"Project-Id-Version: \n"
"PO-Revision-Date: 2026-05-15 10:43+0000\n"
"Last-Translator: ssantos <ssantos@web.de>\n"
"PO-Revision-Date: 2026-09-28 19:10+0000\n"
"Last-Translator: Pavel Borecki <pavel.borecki@gmail.com>\n"
"Language-Team: Portuguese <https://hosted.weblate.org/projects/openwrt/"
"lucimodulesluci-mod-dashboard/pt/>\n"
"Language: pt\n"
@@ -10,7 +10,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 2026.5.dev0\n"
"X-Generator: Weblate 2026.10.dev0\n"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:97
msgid "%d clients"
@@ -62,7 +62,7 @@ msgstr "BSSID"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:218
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:372
msgid "Band"
msgstr ""
msgstr "Banda"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:334
msgid "Bitrate"
@@ -162,7 +162,7 @@ msgstr "GatewayV6"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/charts.js:25
msgid "Gbit/s"
msgstr ""
msgstr "Gbit/s"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:111
msgid "Good"
@@ -229,7 +229,7 @@ msgstr "Internet"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/charts.js:25
msgid "Kbit/s"
msgstr ""
msgstr "Kbit/s"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/10_router.js:285
msgid "Kernel Version"
@@ -431,7 +431,7 @@ msgstr ""
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/charts.js:25
msgid "bit/s"
msgstr ""
msgstr "bit/s"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:85
msgid "by SSID"
@@ -449,7 +449,7 @@ msgstr ""
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:231
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:234
msgid "dBm"
msgstr ""
msgstr "dBm"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:89
msgid "load %.2f"
+8 -8
View File
@@ -1,8 +1,8 @@
msgid ""
msgstr ""
"Project-Id-Version: \n"
"PO-Revision-Date: 2026-05-16 22:08+0000\n"
"Last-Translator: Volenski <volenski@users.noreply.hosted.weblate.org>\n"
"PO-Revision-Date: 2026-09-29 12:45+0000\n"
"Last-Translator: Pavel Borecki <pavel.borecki@gmail.com>\n"
"Language-Team: Portuguese (Brazil) <https://hosted.weblate.org/projects/"
"openwrt/lucimodulesluci-mod-dashboard/pt_BR/>\n"
"Language: pt_BR\n"
@@ -10,7 +10,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n > 1;\n"
"X-Generator: Weblate 2026.6.dev0\n"
"X-Generator: Weblate 2026.10.dev0\n"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:97
msgid "%d clients"
@@ -62,7 +62,7 @@ msgstr "BSSID"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:218
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:372
msgid "Band"
msgstr ""
msgstr "Banda"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:334
msgid "Bitrate"
@@ -162,7 +162,7 @@ msgstr "GatewayV6"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/charts.js:25
msgid "Gbit/s"
msgstr ""
msgstr "Gbit/s"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:111
msgid "Good"
@@ -229,7 +229,7 @@ msgstr "Internet"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/charts.js:25
msgid "Kbit/s"
msgstr ""
msgstr "Kbit/s"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/10_router.js:285
msgid "Kernel Version"
@@ -431,7 +431,7 @@ msgstr ""
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/charts.js:25
msgid "bit/s"
msgstr ""
msgstr "bit/s"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:85
msgid "by SSID"
@@ -449,7 +449,7 @@ msgstr ""
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:231
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:234
msgid "dBm"
msgstr ""
msgstr "dBm"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:89
msgid "load %.2f"
+53 -51
View File
@@ -1,6 +1,6 @@
msgid ""
msgstr ""
"PO-Revision-Date: 2026-08-31 06:44+0000\n"
"PO-Revision-Date: 2026-09-28 18:23+0000\n"
"Last-Translator: Hosted Weblate user 54392 "
"<hamburger2048@users.noreply.hosted.weblate.org>\n"
"Language-Team: Chinese (Simplified Han script) <https://hosted.weblate.org/"
@@ -9,37 +9,37 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Weblate 2026.9.dev0\n"
"X-Generator: Weblate 2026.10.dev0\n"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:97
msgid "%d clients"
msgstr ""
msgstr "%d 个客户端"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:89
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:124
msgid "%d core"
msgid_plural "%d cores"
msgstr[0] ""
msgstr[0] "%d 个核心"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/charts.js:277
msgid "%d min ago"
msgstr ""
msgstr "%d 分钟前"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:72
msgid "%d of %d SSIDs active"
msgstr ""
msgstr "%d 个 SSID 活跃,共 %d 个"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/charts.js:277
msgid "%d s ago"
msgstr ""
msgstr "%d 秒前"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:108
msgid "15 min: %.2f"
msgstr ""
msgstr "15 分钟: %.2f"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:108
msgid "5 min: %.2f"
msgstr ""
msgstr "5 分钟: %.2f"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:322
msgid "Active"
@@ -47,7 +47,7 @@ msgstr "活动"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/20_lan.js:40
msgid "Active leases"
msgstr ""
msgstr "活跃租约"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/10_router.js:295
msgid "Architecture"
@@ -60,7 +60,7 @@ msgstr "BSSID"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:218
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:372
msgid "Band"
msgstr ""
msgstr "频段"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:334
msgid "Bitrate"
@@ -68,22 +68,22 @@ msgstr "速率"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:173
msgid "Buffered"
msgstr ""
msgstr "已缓冲"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:141
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:166
msgid "CPU"
msgstr ""
msgstr "CPU"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:43
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:87
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:162
msgid "CPU usage"
msgstr ""
msgstr "CPU 使用量"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:176
msgid "Cached"
msgstr ""
msgstr "已缓存"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:328
msgid "Channel"
@@ -91,7 +91,7 @@ msgstr "信道"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/history.js:52
msgid "Collecting data..."
msgstr ""
msgstr "正在收集数据…"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/10_router.js:72
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/10_router.js:104
@@ -123,7 +123,7 @@ msgstr "主页"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/layout.js:13
msgid "Detail tabs"
msgstr ""
msgstr "详情标签页"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:352
msgid "Devices Connected"
@@ -160,11 +160,11 @@ msgstr "IPv6 网关"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/charts.js:25
msgid "Gbit/s"
msgstr ""
msgstr "Gbit/s"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:111
msgid "Good"
msgstr ""
msgstr "好"
#: modules/luci-mod-dashboard/root/usr/share/rpcd/acl.d/luci-mod-dashboard.json:31
msgid "Grant access to DHCP status display"
@@ -216,7 +216,7 @@ msgstr "IPv6 前缀"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:206
msgid "Interface is disabled"
msgstr ""
msgstr "接口被禁用"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/10_router.js:21
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/10_router.js:23
@@ -227,7 +227,7 @@ msgstr "互联网"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/charts.js:25
msgid "Kbit/s"
msgstr ""
msgstr "Kbit/s"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/10_router.js:285
msgid "Kernel Version"
@@ -236,21 +236,21 @@ msgstr "内核版本"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/index.js:251
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/index.js:298
msgid "Layout"
msgstr ""
msgstr "布局"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/layout.js:12
msgid "Live charts"
msgstr ""
msgstr "实时图表"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:45
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:106
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:154
msgid "Load average"
msgstr ""
msgstr "平均负载"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/index.js:298
msgid "Loading view…"
msgstr ""
msgstr "加载视图中…"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/10_router.js:280
msgid "Local Time"
@@ -269,11 +269,11 @@ msgstr "Mbit/s"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:97
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:142
msgid "Memory"
msgstr ""
msgstr "内存"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:170
msgid "Memory used"
msgstr ""
msgstr "所用内存"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/10_router.js:290
msgid "Model"
@@ -282,31 +282,31 @@ msgstr "型号"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:119
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:231
msgid "No RX signal"
msgstr ""
msgstr "无 RX 信号"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/20_lan.js:40
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/20_lan.js:53
msgid "No active leases"
msgstr ""
msgstr "无活跃租约"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/history.js:52
msgid "No data received"
msgstr ""
msgstr "无已接收数据"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:88
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:113
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:180
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:244
msgid "No wireless clients connected"
msgstr ""
msgstr "无已连接的无线客户端"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:219
msgid "Noise floor"
msgstr ""
msgstr "噪音层"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/10_router.js:74
msgid "Not configured or no address acquired"
msgstr ""
msgstr "未配置或未取得地址"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/10_router.js:72
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/10_router.js:104
@@ -324,7 +324,7 @@ msgstr "协议"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:47
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:202
msgid "Resources"
msgstr ""
msgstr "资源"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:217
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:316
@@ -334,11 +334,11 @@ msgstr "SSID"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/layout.js:61
msgid "Save & Apply"
msgstr ""
msgstr "保存并应用"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:219
msgid "Signal"
msgstr ""
msgstr "信号"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:35
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:96
@@ -348,11 +348,11 @@ msgstr "信号强度"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/layout.js:11
msgid "Status cards"
msgstr ""
msgstr "状态卡"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:180
msgid "Swap used"
msgstr ""
msgstr "所用的 Swap"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/10_router.js:24
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/10_router.js:317
@@ -363,13 +363,15 @@ msgstr "系统"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:130
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:137
msgid "System load"
msgstr ""
msgstr "系统负载"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/layout.js:18
msgid ""
"The lists follow the page from top to bottom. What is in a list is shown, in "
"that order: drag an entry to move it. An empty list shows the defaults."
msgstr ""
"列表从上到下跟踪页面。列表中内容以该顺序显示:拖动一个条目移动它。空列表显示"
"默认。"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/20_lan.js:54
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:245
@@ -394,16 +396,16 @@ msgstr "上行"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/10_router.js:210
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/10_router.js:275
msgid "Uptime"
msgstr "运行时间"
msgstr "已运行时长"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/15_traffic.js:10
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/15_traffic.js:13
msgid "WAN traffic"
msgstr ""
msgstr "WAN 流量"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:112
msgid "Weak"
msgstr ""
msgstr "弱"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:28
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:37
@@ -413,31 +415,31 @@ msgstr "无线"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:36
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:159
msgid "Wireless client data usage"
msgstr ""
msgstr "无线客户端数据用量"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:34
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:84
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:87
msgid "Wireless client distribution"
msgstr ""
msgstr "无线客户端分布"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:33
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:70
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:288
msgid "Wireless clients"
msgstr ""
msgstr "无线客户端"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/charts.js:25
msgid "bit/s"
msgstr ""
msgstr "bit/s"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:85
msgid "by SSID"
msgstr ""
msgstr "按 SSID"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:87
msgid "clients"
msgstr ""
msgstr "客户端"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:97
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:111
@@ -447,15 +449,15 @@ msgstr ""
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:231
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/30_wifi.js:234
msgid "dBm"
msgstr ""
msgstr "dBm"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/include/12_resources.js:89
msgid "load %.2f"
msgstr ""
msgstr "加载 %.2f"
#: modules/luci-mod-dashboard/htdocs/luci-static/resources/view/dashboard/lib/charts.js:278
msgid "now"
msgstr ""
msgstr "立即"
#~ msgid "Strength"
#~ msgstr "强度"
+3 -3
View File
@@ -50,12 +50,12 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=wwand
PKG_VERSION:=1.6.8_p74
PKG_RELEASE:=63
PKG_VERSION:=1.6.8_p76
PKG_RELEASE:=64
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/ddimension/wwand.git
PKG_SOURCE_VERSION:=d146e6b2a30cbbb2737aa4bfb0403322623a81ad
PKG_SOURCE_VERSION:=ebef0751ce0f1ef3148db9dafee41eee90f91496
PKG_MIRROR_HASH:=skip
PKG_LICENSE:=GPL-2.0-only