diff --git a/luci-app-trafficctl/Makefile b/luci-app-trafficctl/Makefile index 822ddd2bf..7773a3def 100644 --- a/luci-app-trafficctl/Makefile +++ b/luci-app-trafficctl/Makefile @@ -1,8 +1,8 @@ include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-trafficctl -PKG_VERSION:=1.22.0 -PKG_RELEASE:=12 +PKG_VERSION:=1.23.0 +PKG_RELEASE:=13 PKG_LICENSE:=Apache-2.0 PKG_MAINTAINER:=Denis Iusupov @@ -57,5 +57,6 @@ fi exit 0 endef -# call BuildPackage - OpenWrt buildroot will pick up the standard LuCI layout -$(eval $(call BuildPackage,$(PKG_NAME))) +# No explicit $(eval $(call BuildPackage,...)) here: luci.mk already calls it +# for the main package, so the explicit call was a duplicate. Spotted by +# @ntbowen in #73. diff --git a/luci-app-trafficctl/htdocs/luci-static/resources/view/trafficctl/portfw.js b/luci-app-trafficctl/htdocs/luci-static/resources/view/trafficctl/portfw.js index 817a7112e..2f28e3b29 100644 --- a/luci-app-trafficctl/htdocs/luci-static/resources/view/trafficctl/portfw.js +++ b/luci-app-trafficctl/htdocs/luci-static/resources/view/trafficctl/portfw.js @@ -45,9 +45,9 @@ function fmtRate(kbit) { kbit = kbit || 0; if (kbit >= 1000) { var m = kbit / 1000; - return (m === Math.floor(m) ? m : m.toFixed(1)) + ' Mbit/s'; + return (m === Math.floor(m) ? m : m.toFixed(1)) + ' ' + _('Mbit/s'); } - return kbit + ' kbit/s'; + return kbit + ' ' + _('kbit/s'); } function scopeOf(row) { @@ -122,7 +122,7 @@ return view.extend({ 'class': 'cbi-button cbi-button-remove', 'style': 'font-size:11px;padding:1px 6px;margin-left:6px', 'title': _('Remove limit') - }, '✕'); + }, _('✕')); rmBtn.addEventListener('click', function() { rmBtn.disabled = true; self.doCtl('limit', row, 0); @@ -136,7 +136,7 @@ return view.extend({ var b = E('button', { 'class': 'cbi-button', 'style': 'font-size:10px;padding:0 5px;margin-right:2px', - 'title': _('Limit inbound to') + ' ' + fmtRate(p.kbit) + 'title': _('Limit inbound to %s').format(fmtRate(p.kbit)) }, p.label); b.addEventListener('click', function() { b.disabled = true; diff --git a/luci-app-trafficctl/htdocs/luci-static/resources/view/trafficctl/status.js b/luci-app-trafficctl/htdocs/luci-static/resources/view/trafficctl/status.js index 407360435..03d36416b 100644 --- a/luci-app-trafficctl/htdocs/luci-static/resources/view/trafficctl/status.js +++ b/luci-app-trafficctl/htdocs/luci-static/resources/view/trafficctl/status.js @@ -249,13 +249,13 @@ var callConfigGet = rpc.declare({ var RATE_PRESETS = [ {v:'0', l: _('Off')}, - {v:'1000', l:'1 Mbit/s'}, - {v:'2000', l:'2 Mbit/s'}, - {v:'5000', l:'5 Mbit/s'}, - {v:'10000', l:'10 Mbit/s'}, - {v:'25000', l:'25 Mbit/s'}, - {v:'50000', l:'50 Mbit/s'}, - {v:'100000', l:'100 Mbit/s'}, + {v:'1000', l:_('1 Mbit/s')}, + {v:'2000', l:_('2 Mbit/s')}, + {v:'5000', l:_('5 Mbit/s')}, + {v:'10000', l:_('10 Mbit/s')}, + {v:'25000', l:_('25 Mbit/s')}, + {v:'50000', l:_('50 Mbit/s')}, + {v:'100000', l:_('100 Mbit/s')}, {v:'custom', l: _('Custom…')} ]; @@ -334,11 +334,11 @@ function renderTotalCell(cell, total, liveVal, since, pending, degraded) { } cell.appendChild(document.createTextNode(fmtBytes(total))); var tip = since - ? (_('Accumulated since') + ' ' + new Date(since * 1000).toLocaleString()) + ? (_('Accumulated since %s').format(new Date(since * 1000).toLocaleString())) : _('Accumulated on the router'); tip += '\n' + _('Resets when the router reboots.'); if (liveVal != null && liveVal >= 0) { - tip += '\n' + _('Currently tracked connections hold') + ' ' + fmtBytes(liveVal); + tip += '\n' + _('Currently tracked connections hold %s').format(fmtBytes(liveVal)); } cell.title = tip; } @@ -346,16 +346,16 @@ function renderTotalCell(cell, total, liveVal, since, pending, degraded) { function fmtSpeed(bps) { if (!bps || bps < 1) return '—'; var bits = bps * 8; - if (bits < 1000) return bits.toFixed(0) + ' bit/s'; - if (bits < 1000000) { var k = bits/1000; return (k % 1 === 0 ? k.toFixed(0) : k.toFixed(1)) + ' Kbit/s'; } - if (bits < 1000000000) { var m = bits/1000000; return (m % 1 === 0 ? m.toFixed(0) : m.toFixed(1)) + ' Mbit/s'; } - var g = bits/1000000000; return (g % 1 === 0 ? g.toFixed(0) : g.toFixed(2)) + ' Gbit/s'; + if (bits < 1000) return bits.toFixed(0) + ' ' + _('bit/s'); + if (bits < 1000000) { var k = bits/1000; return (k % 1 === 0 ? k.toFixed(0) : k.toFixed(1)) + ' ' + _('Kbit/s'); } + if (bits < 1000000000) { var m = bits/1000000; return (m % 1 === 0 ? m.toFixed(0) : m.toFixed(1)) + ' ' + _('Mbit/s'); } + var g = bits/1000000000; return (g % 1 === 0 ? g.toFixed(0) : g.toFixed(2)) + ' ' + _('Gbit/s'); } function fmtRate(kbit) { if (!kbit || kbit <= 0) return '—'; var mbit = kbit / 1000; - if (mbit >= 1) return (mbit % 1 === 0 ? mbit.toFixed(0) : mbit.toFixed(1)) + ' Mbit/s'; - return kbit + ' kbit/s'; + if (mbit >= 1) return (mbit % 1 === 0 ? mbit.toFixed(0) : mbit.toFixed(1)) + ' ' + _('Mbit/s'); + return kbit + ' ' + _('kbit/s'); } // Render a "↓ down / ↑ up" speed cell. Both halves come from the same @@ -703,14 +703,14 @@ function renderFullGraph(history, limitKbit, width, height) { dlLeg.setAttribute('x', legendX); dlLeg.setAttribute('y', legendY); dlLeg.setAttribute('text-anchor', 'end'); dlLeg.setAttribute('font-size', '9'); dlLeg.setAttribute('fill', 'var(--tc-speed)'); dlLeg.setAttribute('font-weight', '600'); - dlLeg.textContent = '↓ DL'; + dlLeg.textContent = _('↓ DL'); svg.appendChild(dlLeg); if (hasUpload) { var ulLeg = document.createElementNS(ns, 'text'); ulLeg.setAttribute('x', legendX); ulLeg.setAttribute('y', legendY + 12); ulLeg.setAttribute('text-anchor', 'end'); ulLeg.setAttribute('font-size', '9'); ulLeg.setAttribute('fill', 'var(--tc-ok)'); ulLeg.setAttribute('font-weight', '600'); - ulLeg.textContent = '↑ UL'; + ulLeg.textContent = _('↑ UL'); svg.appendChild(ulLeg); } @@ -967,10 +967,10 @@ function buildOverviewPanel(ifaces, ifHistory, speedMap, nameByIp, showOther, on // ── headline tiles ────────────────────────────────────────────────── var head = E('div', { 'class': 'tc-ov-tiles' }, [ - mkOvTile(_('Download') + ' · ' + (primary.label || primary.dev), - fmtSpeed(primaryRate.down), fmtBytes(primary.rx_bytes) + ' ' + _('total'), 'tc-c-speed'), - mkOvTile(_('Upload') + ' · ' + (primary.label || primary.dev), - fmtSpeed(primaryRate.up), fmtBytes(primary.tx_bytes) + ' ' + _('total'), 'tc-c-ok'), + mkOvTile(_('Download · %s').format(primary.label || primary.dev), + fmtSpeed(primaryRate.down), _('%s total').format(fmtBytes(primary.rx_bytes)), 'tc-c-speed'), + mkOvTile(_('Upload · %s').format(primary.label || primary.dev), + fmtSpeed(primaryRate.up), _('%s total').format(fmtBytes(primary.tx_bytes)), 'tc-c-ok'), mkOvTile(_('Active devices'), String(activeDevices), _('sending or receiving now'), '') ]); @@ -1337,7 +1337,7 @@ function buildSummaryTable(rows, sortCol, sortDir, onSort, onSelect, speedMap, d if (sd && (sd.current_up||0) > 1024) { cellMap._speed_up.className = 'td tc-right tc-mono tc-speed-active'; cellMap._speed_up.textContent = fmtSpeed(sd.current_up); } else { cellMap._speed_up.className = 'td tc-right tc-mono tc-speed-idle'; cellMap._speed_up.textContent = sd ? fmtSpeed(sd.current_up||0) : '—'; } - var sparkTip = r._throttle_kbit > 0 ? (_('Limit') + ': ' + fmtRate(r._throttle_kbit)) : ''; + var sparkTip = r._throttle_kbit > 0 ? (_('Limit: %s').format(fmtRate(r._throttle_kbit))) : ''; cellMap._spark = E('div', { 'class': 'td tc-center', 'style': 'padding:2px 4px', 'data-spark-ip': r.ip, 'data-tip': sparkTip || undefined }); var sparkSvg = renderSparkline(speedHistory[r.ip], globalSpeedMax, 60, 20, r._throttle_kbit); if (sparkSvg) cellMap._spark.appendChild(sparkSvg); @@ -1369,7 +1369,7 @@ function buildSummaryTable(rows, sortCol, sortDir, onSort, onSelect, speedMap, d if (lastTs) { var ago = Math.floor((Date.now()/1000) - lastTs); var agoStr = ago < 60 ? ago + 's' : ago < 3600 ? Math.floor(ago/60) + 'm' : Math.floor(ago/3600) + 'h'; - tip = _('Last seen') + ': ' + lastType + ', ' + agoStr + ' ' + _('ago'); + tip = _('Last seen: %s, %s ago').format(lastType, agoStr); } } linkBadge = E('span', { 'class': 'tc-c-faint', 'style': 'cursor:help', 'title': tip }, '?'); @@ -1425,7 +1425,7 @@ function buildSummaryTable(rows, sortCol, sortDir, onSort, onSelect, speedMap, d cellMap._backlog = E('div', { 'class': 'td tc-center', 'data-backlog-ip': r.ip }, backlogBadge); var cells = visibleCols.map(function(c) { return cellMap[c.key]; }); - var row = E('div', { 'class': 'tr', 'title': _('Click to inspect') + ' ' + r.name }, cells); + var row = E('div', { 'class': 'tr', 'title': _('Click to inspect %s').format(r.name) }, cells); row.addEventListener('click', function() { addRecentDevice(r.ip, r.name); onSelect(r.ip, r.name); }); return row; }); @@ -1519,7 +1519,7 @@ function buildExtendedStatsPanel(ip, shapeMap, dropMap, speedMap) { } if (sm.memory_used != null) addRow(_('Queue memory'), fmtBytes(sm.memory_used), null); if (sm.lended != null || sm.borrowed != null) { - addRow(_('Lended') + ' / ' + _('Borrowed'), (sm.lended || 0) + ' / ' + (sm.borrowed || 0), null); + addRow(_('Lended / Borrowed'), (sm.lended || 0) + ' / ' + (sm.borrowed || 0), null); } if (spd && sm.rate_kbit > 0) { var currentBps = spd.current || 0; @@ -1612,7 +1612,7 @@ function buildExtendedStatsLegend(shapeMap, dropMap) { } return E('div', { 'class': 'tc-ext-panel tc-ext-panel--sticky' }, [ - E('div', { 'class': 'tc-ext-panel__title' }, _('Extended Statistics') + ' (' + _('all devices') + ')'), + E('div', { 'class': 'tc-ext-panel__title' }, _('Extended Statistics (all devices)')), E('div', { 'class': 'tc-ext-col-flex' }, rows) ]); } @@ -2302,8 +2302,8 @@ return view.extend({ 'class': 'tc-custom-input' }); var customUnitBtns = E('span', {'class':'tc-custom-unit-btns'}); var _customUnit = 'mbit'; - var mbitBtn = E('span', {'style':'padding:4px 8px;font-size:11px;cursor:pointer;background:var(--tc-speed);color:#fff'}, 'Mbit/s'); - var kbitBtn = E('span', {'style':'padding:4px 8px;font-size:11px;cursor:pointer;background:var(--tc-bg);color:currentColor'}, 'kbit/s'); + var mbitBtn = E('span', {'style':'padding:4px 8px;font-size:11px;cursor:pointer;background:var(--tc-speed);color:#fff'}, _('Mbit/s')); + var kbitBtn = E('span', {'style':'padding:4px 8px;font-size:11px;cursor:pointer;background:var(--tc-bg);color:currentColor'}, _('kbit/s')); function updateUnitBtns() { mbitBtn.style.background = _customUnit === 'mbit' ? 'var(--tc-speed)' : 'var(--tc-bg)'; mbitBtn.style.color = _customUnit === 'mbit' ? '#fff' : 'currentColor'; @@ -2552,7 +2552,7 @@ return view.extend({ function removeSubnetLimit(target, mode, btn) { btn.disabled = true; - setStatus(statusDiv, 'loading', _('Removing limit on') + ' ' + target + '…'); + setStatus(statusDiv, 'loading', _('Removing limit on %s…').format(target)); callRatelimit(target, 0, '', mode).then(function(res) { setStatus(statusDiv, (res && res.ok) ? 'ok' : 'error', (res && res.msg) || _('Throttle removed')); @@ -2591,7 +2591,7 @@ return view.extend({ }, isShared ? _('shared between them') : _('each')), E('span', {'class':'tc-c-muted tc-subnet-limits__drops'}, l.packets > 0 - ? (String(l.packets) + ' ' + _('packets dropped') + ' (' + fmtBytes(l.bytes) + ')') + ? (_('%d packets dropped (%s)').format(l.packets, fmtBytes(l.bytes))) : _('nothing dropped yet')), removeBtn ])); @@ -2604,8 +2604,8 @@ return view.extend({ list.forEach(function(s) { if (!s || !s.cidr) { return; } addSubnetChip(s.cidr, s.cidr, s.kind === 'routed' - ? (_('Routed via') + ' ' + s.device) - : (_('On') + ' ' + s.device)); + ? (_('Routed via %s').format(s.device)) + : (_('On %s').format(s.device))); }); updateSubnetChips(); updateScopeExplain(); @@ -3098,10 +3098,10 @@ return view.extend({ var protoUdp = Number(data.protocols.udp) || 0; var protoOther = Number(data.protocols.other) || 0; var connCount = protoTcp + protoUdp + protoOther; - var parts = [_('Connections') + ': '+connCount+'']; + var parts = [_('Connections: %d').format(connCount)]; if (connCount > 0) { - parts.push('TCP: '+protoTcp+''); - parts.push('UDP: '+protoUdp+''); + parts.push(_('TCP: %d').format(protoTcp)); + parts.push(_('UDP: %d').format(protoUdp)); if (data.tcp_states) { Object.keys(data.tcp_states).forEach(function(s) { parts.push(escHtml(s)+': '+(Number(data.tcp_states[s]) || 0)+''); @@ -3109,15 +3109,15 @@ return view.extend({ } } if ((data.shape_kbit || 0) > 0) { - parts.push(_('Shaped') + ': 🌊 '+fmtRate(data.shape_kbit)+''); + parts.push(_('Shaped: 🌊 %s').format(fmtRate(data.shape_kbit))); var sm = self._shapeMap[data.ip || searchSelect.getValue()] || {}; - if ((sm.backlog||0) > 0) parts.push(_('Queued') + ': '+fmtBytes(sm.backlog)+''); - if ((sm.bytes||0) > 0) parts.push(_('Passed') + ': '+fmtBytes(sm.bytes)+''); + if ((sm.backlog||0) > 0) parts.push(_('Queued: %s').format(fmtBytes(sm.backlog))); + if ((sm.bytes||0) > 0) parts.push(_('Passed: %s').format(fmtBytes(sm.bytes))); } else if ((data.rate_limit_kbit || 0) > 0) { - parts.push(_('Speed limit') + ': ⚡ '+fmtRate(data.rate_limit_kbit)+''); + parts.push(_('Speed limit: ⚡ %s').format(fmtRate(data.rate_limit_kbit))); var dm = self._dropMap[data.ip || searchSelect.getValue()] || {}; if ((dm.packets||0) > 0) { - parts.push(_('Dropped') + ': 🚫 '+(Number(dm.packets) || 0)+' pkts / '+fmtBytes(dm.bytes||0)+''); + parts.push(_('Dropped: 🚫 %d pkts / %s').format(Number(dm.packets) || 0, fmtBytes(dm.bytes||0))); } } // Listed but still associated is not a block, so it must not @@ -3130,11 +3130,11 @@ return view.extend({ } else if (data.wifi_blocked) { wifiPart = '  |  📵 ' + _('WiFi blocked') + ' ('+escHtml(data.mac||'') + ')'; } else { - wifiPart = data.mac ? '  |  MAC: '+escHtml(data.mac)+'' : ''; + wifiPart = data.mac ? '  |  ' + _('MAC:') + ' ' + escHtml(data.mac) + '' : ''; } statsDiv.className = 'alert-message ' + (data.blocked ? 'error' : 'info'); statsDiv.innerHTML = (data.blocked - ? '⛔ ' + _('BLOCKED') + ' — '+(Number(data.block_packets) || 0)+' pkts, '+fmtBytes(Number(data.block_bytes) || 0)+' ' + _('dropped') + '  |  ' + ? '⛔ ' + _('BLOCKED') + ' — ' + _('%d pkts, %s dropped').format(Number(data.block_packets) || 0, fmtBytes(Number(data.block_bytes) || 0)) + '  |  ' : '') + parts.join('  |  ') + wifiPart; } @@ -3323,9 +3323,9 @@ return view.extend({ } var parts = []; - parts.push(E('span', {}, [document.createTextNode(_('Active') + ': '), E('b', {}, String(rows.length))])); - parts.push(E('span', {}, [document.createTextNode(_('Blocked') + ': '), mkFilterVal('blocked', 'var(--tc-err)', String(blocked))])); - parts.push(E('span', {}, [document.createTextNode(_('WiFi') + ': '), mkFilterVal('wifi_blocked', 'var(--tc-warn)', String(wifiBlk))])); + parts.push(E('span', {}, [document.createTextNode(_('Active:') + ' '), E('b', {}, String(rows.length))])); + parts.push(E('span', {}, [document.createTextNode(_('Blocked:') + ' '), mkFilterVal('blocked', 'var(--tc-err)', String(blocked))])); + parts.push(E('span', {}, [document.createTextNode(_('WiFi:') + ' '), mkFilterVal('wifi_blocked', 'var(--tc-warn)', String(wifiBlk))])); // Without this the header counts a device as blocked on the strength of // the uci maclist alone — which is how a router can report "WiFi: 2" // while both of those devices are browsing. @@ -3333,13 +3333,13 @@ return view.extend({ parts.push(E('span', { 'style': 'color:var(--tc-err);font-weight:700;cursor:help', 'title': _('Devices on the WiFi deny list that are still connected — those blocks are not in effect on the running radio.') - }, '⚠ ' + wifiPending + ' ' + _('not applied'))); + }, '⚠ ' + _('%d not applied').format(wifiPending))); } - if (limited > 0) parts.push(E('span', {}, [document.createTextNode(_('Limited') + ': '), mkFilterVal('limited', 'var(--tc-warn)', '⚡' + limited)])); - if (shaped > 0) parts.push(E('span', {}, [document.createTextNode(_('Shaped') + ': '), mkFilterVal('shaped', 'var(--tc-speed)', '🌊' + shaped)])); + if (limited > 0) parts.push(E('span', {}, [document.createTextNode(_('Limited:') + ' '), mkFilterVal('limited', 'var(--tc-warn)', '⚡' + limited)])); + if (shaped > 0) parts.push(E('span', {}, [document.createTextNode(_('Shaped:') + ' '), mkFilterVal('shaped', 'var(--tc-speed)', '🌊' + shaped)])); if (totalDropPkts > 0) { parts.push(E('span', {}, [ - document.createTextNode(_('Dropped') + ': '), E('b', {'style':'color:var(--tc-err)'}, '🚫' + totalDropPkts) + document.createTextNode(_('Dropped:') + ' '), E('b', {'style':'color:var(--tc-err)'}, '🚫' + totalDropPkts) ])); } @@ -3912,7 +3912,7 @@ return view.extend({ var limitBtns = []; RATE_PRESETS.forEach(function(p) { if (p.v === '0' || p.v === 'custom') return; - limitBtns.push(E('span', {'class':'tg-kbd-btn'}, p.l.replace(' Mbit/s', 'M').replace(/\s/g, ''))); + limitBtns.push(E('span', {'class':'tg-kbd-btn'}, p.l.replace(' Mbit/s','M').replace(/\s/g,''))); }); for (var li = 0; li < limitBtns.length; li += 3) { kbdBubble.appendChild(E('div', {'class':'tg-kbd-row'}, limitBtns.slice(li, li + 3))); @@ -3922,7 +3922,7 @@ return view.extend({ var shapeBtns = []; RATE_PRESETS.forEach(function(p) { if (p.v === '0' || p.v === 'custom') return; - shapeBtns.push(E('span', {'class':'tg-kbd-btn'}, '🔧 ' + p.l.replace(' Mbit/s', 'M').replace(/\s/g, ''))); + shapeBtns.push(E('span', {'class':'tg-kbd-btn'}, '🔧 ' + p.l.replace(' Mbit/s','M').replace(/\s/g,''))); }); for (var si = 0; si < shapeBtns.length; si += 3) { kbdBubble.appendChild(E('div', {'class':'tg-kbd-row'}, shapeBtns.slice(si, si + 3))); @@ -4180,17 +4180,14 @@ return view.extend({ var modeLabels = { 'none': ['⊘', _('No offload'), 'var(--tc-muted)'], 'software': ['◑', _('Software offload'), 'var(--tc-speed)'], - // tctl_get_offload_mode splits software offload on the flowtable - // counter flag, so this value reaches the badge too. Without an - // entry the badge fell through to '?' and the raw mode string. - 'software-counter': ['◑', _('Software offload'), 'var(--tc-speed)'], + 'software-counter': ['◑', _('Software offload (counter)'), 'var(--tc-speed)'], 'hardware-counter': ['●', _('Hardware offload'), 'var(--tc-warn)'], 'hardware': ['●', _('Hardware offload'), 'var(--tc-warn)'] }; var ml = modeLabels[cfg.offload_mode] || ['?', cfg.offload_mode, 'var(--tc-muted)']; var modeBadge = E('div', {'style':'margin-bottom:10px;font-size:12px'}, [ E('span', {'style':'color:'+ml[2]+';font-size:15px;margin-right:4px'}, ml[0]), - E('span', {'style':'color:var(--tc-muted)'}, _('Current mode: ')), + E('span', {'style':'color:var(--tc-muted)'}, _('Current mode:') + ' '), E('b', {}, ml[1]) ]); @@ -4338,9 +4335,9 @@ return view.extend({ var sep = E('span', {'style': 'opacity:0.35;margin:0 6px'}, '|'); var docLinks = para([ - link(openwrtUrl, 'OpenWrt: Flow offloading ↗'), sep.cloneNode(true), - link(kernelUrl, 'Linux kernel: nf_flowtable ↗'), sep.cloneNode(true), - link(nftUrl, 'nftables: Flowtables ↗') + link(openwrtUrl, _('OpenWrt: Flow offloading ↗')), sep.cloneNode(true), + link(kernelUrl, _('Linux kernel: nf_flowtable ↗')), sep.cloneNode(true), + link(nftUrl, _('nftables: Flowtables ↗')) ]); if (mode === 'hardware-counter') { @@ -4349,15 +4346,15 @@ return view.extend({ icon = '⚠️'; body = E('div', {}, [ para(bold(_('Hardware flow offloading active — real-time speed monitoring unavailable.'))), - para([_('The flowtable '), + para([_('The flowtable') + ' ', E('code', {}, 'counter'), - _(' flag should sync hardware byte counts back to conntrack, but on many ' + + ' ' + _('flag should sync hardware byte counts back to conntrack, but on many ' + 'platforms (e.g. Mediatek Filogic) the driver does not implement the stats ' + 'callback, so conntrack counters remain frozen for active flows.')]), para(bold(_('To restore speed monitoring:'))), E('ul', {'class': 'tc-offload-ul'}, [ E('li', {}, [ - _('Disable hardware offload (keeps software offload): '), + _('Disable hardware offload (keeps software offload):') + ' ', E('code', {}, 'uci set firewall.@defaults[0].flow_offloading_hw=0 && uci commit firewall && fw4 reload'), ]), E('li', {}, _('Or disable all flow offload in LuCI → Network → Firewall → General Settings.')), @@ -4379,12 +4376,12 @@ return view.extend({ E('li', {}, _('Traffic shaping (tc/HTB) — bypassed for offloaded flows')), E('li', {}, _('Rate limiting — applies only to new connections')), ]), - para([_('WiFi blocking and internet blocking of new connections still work. '), + para([_('WiFi blocking and internet blocking of new connections still work.') + ' ', _('Shaped devices are usually not offloaded (kernel detects the HTB qdisc).')]), para(bold(_('How to get full functionality without disabling offload:'))), - para([_('The flowtable '), + para([_('The flowtable') + ' ', E('code', {}, 'counter'), - _(' flag (Linux 5.7+, set automatically by fw4/nftables) periodically syncs ' + + ' ' + _('flag (Linux 5.7+, set automatically by fw4/nftables) periodically syncs ' + 'hardware byte counts back to conntrack — trafficctl detects this and all features work normally.')]), para(_('If your current firmware uses fw3 (iptables) or ships a kernel older than 5.7, ' + 'a router with modern OpenWrt and fw4 support will have this working out of the box.')), @@ -4407,9 +4404,9 @@ return view.extend({ para(bold(_('Software flow offloading is active, with counter sync.'))), para(_('The kernel fast-paths established connections through a flowtable for ' + 'higher throughput.')), - para([_('The flowtable carries the '), + para([_('The flowtable carries the') + ' ', E('code', {}, 'counter'), - _(' flag (Linux 5.7+, set automatically by fw4), so offloaded byte counts are ' + + ' ' + _('flag (Linux 5.7+, set automatically by fw4), so offloaded byte counts are ' + 'synced back to conntrack. Speed and totals come from conntrack as usual and ' + 'are accurate — the nftables counter fallback is not needed on this router.')]), para(_('Traffic shaping and blocking are not affected.')), diff --git a/luci-app-trafficctl/po/templates/luci-app-trafficctl.pot b/luci-app-trafficctl/po/templates/luci-app-trafficctl.pot index bcbe00480..ca1baa04c 100644 --- a/luci-app-trafficctl/po/templates/luci-app-trafficctl.pot +++ b/luci-app-trafficctl/po/templates/luci-app-trafficctl.pot @@ -1,9 +1,83 @@ msgid "" msgstr "Content-Type: text/plain; charset=UTF-8" +msgid "\"all\" for every device, or a CIDR such as 10.0.20.0/24" +msgstr "" + +msgid "%d not applied" +msgstr "" + +msgid "%d packets dropped (%s)" +msgstr "" + +msgid "%d pkts, %s dropped" +msgstr "" + +msgid "%s total" +msgstr "" + +msgid "1 Mbit/s" +msgstr "" + +msgid "1 hour" +msgstr "" + +msgid "10 Mbit/s" +msgstr "" + +msgid "100 Mbit/s" +msgstr "" + +msgid "15 min" +msgstr "" + +msgid "2 Mbit/s" +msgstr "" + +msgid "25 Mbit/s" +msgstr "" + +msgid "4 hours" +msgstr "" + +msgid "5 Mbit/s" +msgstr "" + +msgid "50 Mbit/s" +msgstr "" + +msgid "" +"Accelerates routing in the kernel via nftables flowtable. Speed monitoring and t" +"raffic shaping work normally." +msgstr "" + +msgid "Accumulated on the router" +msgstr "" + +msgid "Accumulated since %s" +msgstr "" + +msgid "Action" +msgstr "" + +msgid "Active connections in conntrack" +msgstr "" + msgid "Active devices" msgstr "" +msgid "Active inbound connections" +msgstr "" + +msgid "Active subnet limits" +msgstr "" + +msgid "Active:" +msgstr "" + +msgid "Activity" +msgstr "" + msgid "Adding to" msgstr "" @@ -13,67 +87,345 @@ msgstr "" msgid "All active devices" msgstr "" +msgid "All devices" +msgstr "" + +msgid "All of" +msgstr "" + +msgid "App" +msgstr "" + +msgid "" +"Applied once, the first time a device appears on the network. A device that alre" +"ady has a limit is left alone." +msgstr "" + +msgid "Applied — firewall reloading" +msgstr "" + msgid "Apply" msgstr "" +msgid "Applying…" +msgstr "" + +msgid "Auto-refresh interval for summary table" +msgstr "" + msgid "Avg" msgstr "" -msgid "Block" -msgstr "" - msgid "BLOCKED" msgstr "" -msgid "Blocked" +msgid "Behind a downstream router (routed subnet)" msgstr "" -msgid "Blocking" +msgid "Block Internet" +msgstr "" + +msgid "Block WiFi" +msgstr "" + +msgid "Blocked from WiFi (MAC deny list)" +msgstr "" + +msgid "Blocked:" +msgstr "" + +msgid "Blocking, rate limiting, and traffic shaping continue to work regardless." +msgstr "" + +msgid "Blocks" +msgstr "" + +msgid "Bot commands:" +msgstr "" + +msgid "Bot is running" +msgstr "" + +msgid "Bot is stopped" +msgstr "" + +msgid "Bridge port — these bytes are also counted in its bridge" +msgstr "" + +msgid "" +"Bridge ports, switch conduits and ifb mirrors. Their bytes are already counted i" +"n the bridge or uplink above." +msgstr "" + +msgid "Byte totals unavailable on this router." msgstr "" msgid "Bytes" msgstr "" +msgid "Bytes dropped" +msgstr "" + msgid "Bytes queued in tc" msgstr "" +msgid "Bytes queued in traffic shaper" +msgstr "" + +msgid "Bytes received from remote clients" +msgstr "" + +msgid "Bytes sent back to remote clients" +msgstr "" + +msgid "CPU load (1 min)" +msgstr "" + msgid "Calculating…" msgstr "" +msgid "Cancel" +msgstr "" + +msgid "Carries the default route" +msgstr "" + +msgid "Chat ID" +msgstr "" + +msgid "Chat ID:" +msgstr "" + msgid "Click a row to inspect that device. Download speed updates every 2 seconds." msgstr "" +msgid "Click again to confirm" +msgstr "" + msgid "Click header to sort." msgstr "" -msgid "Click to inspect" +msgid "Click to inspect %s" +msgstr "" + +msgid "Click to toggle column visibility" +msgstr "" + +msgid "Clients" +msgstr "" + +msgid "Collecting interface counters…" +msgstr "" + +msgid "Config" +msgstr "" + +msgid "Connection interface (WiFi band, LAN port or routed)" msgstr "" msgid "Connections" msgstr "" +msgid "Connections: %d" +msgstr "" + msgid "Conns" msgstr "" +msgid "Control" +msgstr "" + +msgid "Current download speed (bytes/sec from router to device)" +msgstr "" + +msgid "Current mode:" +msgstr "" + +msgid "Current upload speed (bytes/sec from device to router)" +msgstr "" + +msgid "Currently tracked connections hold %s" +msgstr "" + +msgid "Custom" +msgstr "" + +msgid "Customize message" +msgstr "" + msgid "Custom…" msgstr "" +msgid "Cut all internet access" +msgstr "" + +msgid "Cut internet" +msgstr "" + +msgid "Cutting…" +msgstr "" + msgid "DL Speed" msgstr "" +msgid "Destination" +msgstr "" + msgid "Device" msgstr "" -msgid "Dropped" +msgid "Device hostname from DHCP lease" +msgstr "" + +msgid "Device name" +msgstr "" + +msgid "Devices already known:" +msgstr "" + +msgid "" +"Devices keep talking to each other and to the router — only the way out is close" +"d. Traffic is stopped on the way in, so a transparent proxy on the router (podko" +"p, passwall) cannot carry it out either. Remote access that reaches the router t" +"hrough a LAN host (Tailscale, a tunnel, a jump host) stops working while this is" +" on." +msgstr "" + +msgid "" +"Devices on the WiFi deny list that are still connected — those blocks are not in" +" effect on the running radio." +msgstr "" + +msgid "Disable hardware offload (keeps software offload):" +msgstr "" + +msgid "Dismiss" +msgstr "" + +msgid "Display" +msgstr "" + +msgid "Distinct remote clients" +msgstr "" + +msgid "Done" +msgstr "" + +msgid "Download · %s" +msgstr "" + +msgid "Drop" +msgstr "" + +msgid "Drop ratio" +msgstr "" + +msgid "Dropped:" +msgstr "" + +msgid "Dropped: 🚫 %d pkts / %s" +msgstr "" + +msgid "Drops" +msgstr "" + +msgid "Drops excess packets (instant enforcement, low overhead)" msgstr "" msgid "Dst IP" msgstr "" +msgid "ECN marks" +msgstr "" + +msgid "EWMA" +msgstr "" + +msgid "Enabled" +msgstr "" + +msgid "Enter a custom speed value" +msgstr "" + +msgid "Every device in" +msgstr "" + +msgid "Every device this router forwards for" +msgstr "" + +msgid "Ext. port" +msgstr "" + +msgid "Extended" +msgstr "" + +msgid "Extended Statistics" +msgstr "" + +msgid "Extended Statistics (all devices)" +msgstr "" + +msgid "External (WAN) port" +msgstr "" + +msgid "Filter connections by protocol" +msgstr "" + +msgid "Flow Offload" +msgstr "" + +msgid "" +"Flow offload is active and the kernel does not support the nftables counter maps" +" trafficctl uses in that mode, so byte counters stop updating for offloaded conn" +"ections. Speeds and totals would be far too low, so they are not shown. Disablin" +"g hardware offload in Settings → Flow Offload restores them." +msgstr "" + +msgid "Flow: /devices → select device → inline keyboard with enabled actions above" +msgstr "" + +msgid "Flows" +msgstr "" + +msgid "" +"Forwarded traffic only — this kernel cannot install the prerouting rule, so traf" +"fic handled by a transparent proxy on the router would not be caught." +msgstr "" + +msgid "Full control" +msgstr "" + +msgid "Gbit/s" +msgstr "" + +msgid "Global internet cut needs nftables (fw4). This router is running iptables." +msgstr "" + +msgid "Graph" +msgstr "" + msgid "Group" msgstr "" -msgid "Group by" +msgid "Group connections table rows" +msgstr "" + +msgid "HTML:" +msgstr "" + +msgid "Hardware MAC address" +msgstr "" + +msgid "Hardware flow offload" +msgstr "" + +msgid "Hardware flow offloading active — real-time speed monitoring unavailable." +msgstr "" + +msgid "Hardware flow offloading is active." +msgstr "" + +msgid "Hardware offload" msgstr "" msgid "Hostname" @@ -82,100 +434,478 @@ msgstr "" msgid "Hostname / Dst IP" msgstr "" +msgid "" +"How often live data is polled: speeds, graphs, drop/backlog counters, and the co" +"nnection table in device view" +msgstr "" + +msgid "How to get full functionality without disabling offload:" +msgstr "" + +msgid "IP address" +msgstr "" + +msgid "" +"If your current firmware uses fw3 (iptables) or ships a kernel older than 5.7, a" +" router with modern OpenWrt and fw4 support will have this working out of the bo" +"x." +msgstr "" + +msgid "Iface" +msgstr "" + +msgid "In" +msgstr "" + +msgid "" +"In this mode the kernel's conntrack, firewall, and tc are bypassed for offloaded" +" flows:" +msgstr "" + +msgid "Inbound rate limit (drops packets above the rate)" +msgstr "" + +msgid "" +"Inbound traffic control for DNAT port forwards and router-local open ports confi" +"gured in the firewall. Pause drops all inbound traffic on the port instantly (wi" +"thout touching the firewall config); Limit polices the inbound rate." +msgstr "" + msgid "Inet" msgstr "" -msgid "Limited" +msgid "Inline keyboard preview:" msgstr "" -msgid "Limiter (drop)" +msgid "Interfaces" +msgstr "" + +msgid "Internet access status (paused = traffic blocked)" +msgstr "" + +msgid "Internet is cut for all devices" +msgstr "" + +msgid "Kbit/s" +msgstr "" + +msgid "Keep after reboot" +msgstr "" + +msgid "Kept after reboot — undoing it needs LAN or physical access." +msgstr "" + +msgid "Known devices" +msgstr "" + +msgid "LAN keeps working" +msgstr "" + +msgid "Last seen: %s, %s ago" +msgstr "" + +msgid "Lended / Borrowed" +msgstr "" + +msgid "Limit" +msgstr "" + +msgid "Limit inbound to %s" +msgstr "" + +msgid "Limit new devices" +msgstr "" + +msgid "Limit: %s" +msgstr "" + +msgid "Limited devices" +msgstr "" + +msgid "Limited subnets" +msgstr "" + +msgid "Limited:" +msgstr "" + +msgid "Limiter" msgstr "" msgid "Limiter (nft drop)" msgstr "" -msgid "Limiting" +msgid "Limiting %s → %s (%s)…" +msgstr "" + +msgid "Limiting → %s…" +msgstr "" + +msgid "Link" +msgstr "" + +msgid "Linux kernel: nf_flowtable ↗" +msgstr "" + +msgid "Loading…" +msgstr "" + +msgid "Local IP address" +msgstr "" + +msgid "Log categories" +msgstr "" + +msgid "Logging" +msgstr "" + +msgid "Logging & Persistence" +msgstr "" + +msgid "MAC address" +msgstr "" + +msgid "MAC:" msgstr "" msgid "Max" msgstr "" -msgid "Mode" +msgid "Mbit/s" +msgstr "" + +msgid "Method" +msgstr "" + +msgid "Name" +msgstr "" + +msgid "New Device Defaults" +msgstr "" + +msgid "New devices" +msgstr "" + +msgid "No action buttons enabled" msgstr "" msgid "No active connections." msgstr "" -msgid "No active devices." +msgid "No active devices" +msgstr "" + +msgid "No activity recorded yet." +msgstr "" + +msgid "" +"No baseline recorded yet. It is taken from the current leases when you switch th" +"is on." +msgstr "" + +msgid "No devices match filter." +msgstr "" + +msgid "No extended stats available for this device." +msgstr "" + +msgid "No extended stats available." +msgstr "" + +msgid "No offload" +msgstr "" + +msgid "No port forwards or WAN-open ports are configured in the firewall." msgstr "" msgid "None (per-flow)" msgstr "" +msgid "" +"Not counted: flow offload is active and this router cannot provide the nftables " +"counters trafficctl uses in that mode, so the kernel byte counters stop updating" +" for offloaded connections. Any total here would be far too low. See Settings → " +"Flow Offload." +msgstr "" + +msgid "Not enough data yet" +msgstr "" + +msgid "" +"Not measurable while flow offload is active: in that mode the router counts byte" +"s per address only, so traffic cannot be split by protocol. See Settings → Flow " +"Offload." +msgstr "" + +msgid "Note: speed is measured before shaper — bursts above limit are normal" +msgstr "" + +msgid "Notifications" +msgstr "" + +msgid "Notifications only" +msgstr "" + msgid "Off" msgstr "" +msgid "" +"Offloads routing to the hardware engine (PPE/NPU). Requires software offload. ⚠ " +"On many platforms (e.g. Mediatek Filogic) the driver does not report byte counte" +"rs back to the kernel — real-time speed monitoring will show zero." +msgstr "" + +msgid "On %s" +msgstr "" + +msgid "" +"On the WiFi deny list but still connected — the block is NOT in effect on the ru" +"nning radio. Install hostapd-utils so blocks apply immediately, or restart WiFi " +"(disconnects every client)." +msgstr "" + +msgid "One bucket for the whole subnet" +msgstr "" + +msgid "One bucket per device inside the subnet" +msgstr "" + msgid "Open DHCP/DNS bindings" msgstr "" -msgid "Passed" +msgid "OpenWrt: Flow offloading ↗" +msgstr "" + +msgid "Operation failed" +msgstr "" + +msgid "Or disable all flow offload in LuCI → Network → Firewall → General Settings." +msgstr "" + +msgid "Other interfaces" +msgstr "" + +msgid "Out" +msgstr "" + +msgid "Overlimits" +msgstr "" + +msgid "Overrides the DHCP lease or DNS name. Leave empty to clear the custom name." +msgstr "" + +msgid "Overview" +msgstr "" + +msgid "Packets dropped" +msgstr "" + +msgid "Packets dropped by rate limiter" +msgstr "" + +msgid "Passed: %s" +msgstr "" + +msgid "Paste bot token" +msgstr "" + +msgid "Pause" +msgstr "" + +msgid "Persist rules" +msgstr "" + +msgid "Pick a rate to apply it to this target, or Off to remove its limit." +msgstr "" + +msgid "Poll" +msgstr "" + +msgid "Polling is off — set the Poll interval to see throughput." msgstr "" msgid "Port" msgstr "" +msgid "Port Forwards & Open Ports" +msgstr "" + +msgid "Preview:" +msgstr "" + msgid "Proto" msgstr "" msgid "Protocol" msgstr "" -msgid "Queued" +msgid "Queue" +msgstr "" + +msgid "Queue memory" +msgstr "" + +msgid "Queued: %s" +msgstr "" + +msgid "Queues excess traffic (smoother streaming, lower jitter)" +msgstr "" + +msgid "Rate" +msgstr "" + +msgid "Rate limiting — applies only to new connections" +msgstr "" + +msgid "Ratelimits" +msgstr "" + +msgid "Received by this interface" +msgstr "" + +msgid "Recent" msgstr "" msgid "Refresh" msgstr "" +msgid "Remove" +msgstr "" + +msgid "Remove limit" +msgstr "" + msgid "Removing from" msgstr "" -msgid "Removing throttle for" +msgid "Removing limit on %s…" msgstr "" -msgid "Reverse DNS" +msgid "Removing throttle…" msgstr "" -msgid "Run" +msgid "Rename device" +msgstr "" + +msgid "Rename failed" +msgstr "" + +msgid "Resets when the router reboots." +msgstr "" + +msgid "Restore internet" +msgstr "" + +msgid "Restores by itself in" +msgstr "" + +msgid "Restoring…" +msgstr "" + +msgid "Resume" +msgstr "" + +msgid "Routed via %s" msgstr "" msgid "Running…" msgstr "" +msgid "Save" +msgstr "" + +msgid "Save as router default" +msgstr "" + +msgid "Saving…" +msgstr "" + msgid "Scanning all devices…" msgstr "" +msgid "Search device (name, IP, MAC)…" +msgstr "" + +msgid "Sending…" +msgstr "" + +msgid "Sent by this interface" +msgstr "" + msgid "Service" msgstr "" -msgid "Shaped" +msgid "Set a rate above 0 — with no rate this does nothing." msgstr "" -msgid "Shaper (queue)" +msgid "Settings" +msgstr "" + +msgid "Shaped devices" +msgstr "" + +msgid "Shaped devices are usually not offloaded (kernel detects the HTB qdisc)." +msgstr "" + +msgid "Shaped:" +msgstr "" + +msgid "Shaped: 🌊 %s" +msgstr "" + +msgid "Shaper" msgstr "" msgid "Shaper (tc/HTB queue)" msgstr "" -msgid "Shaping" +msgid "Shapes" msgstr "" -msgid "Show" +msgid "Shaping → %s…" +msgstr "" + +msgid "Share:" +msgstr "" + +msgid "Show/hide token" +msgstr "" + +msgid "Simple" +msgstr "" + +msgid "Simple = arithmetic mean, EWMA = exponential weighted moving average" +msgstr "" + +msgid "Software flow offload" +msgstr "" + +msgid "Software flow offloading is active." +msgstr "" + +msgid "Software offload" +msgstr "" + +msgid "Software offload (counter)" msgstr "" msgid "Speed Limit" msgstr "" -msgid "Speed limit" +msgid "Speed graph. Window = avg time. Orange dashed line = speed limit" +msgstr "" + +msgid "" +"Speed is measured via nftables counters installed at a higher priority (before t" +"he flowtable), so graphs are accurate." +msgstr "" + +msgid "Speed limit: ⚡ %s" +msgstr "" + +msgid "Speed limit: shaper (queue) or limiter (drop)" +msgstr "" + +msgid "Speed monitoring — conntrack byte counters are not updated" msgstr "" msgid "State" @@ -184,154 +914,91 @@ msgstr "" msgid "Stats" msgstr "" -msgid "Throttle" +msgid "Stays off until you switch it back." +msgstr "" + +msgid "" +"Store the current Poll and Window as this router's defaults, for browsers that h" +"ave not set their own" +msgstr "" + +msgid "Switch it off" +msgstr "" + +msgid "Syslog" +msgstr "" + +msgid "" +"TCP bytes transferred, accumulated on the router. Not measurable while flow offl" +"oad is active" +msgstr "" + +msgid "TCP: %d" +msgstr "" + +msgid "Table & Speed" +msgstr "" + +msgid "Target:" +msgstr "" + +msgid "Telegram" +msgstr "" + +msgid "Telegram Bot" +msgstr "" + +msgid "Test" +msgstr "" + +msgid "" +"The auto-revert helper is not running; this page re-checks the deadline while it" +" is open." +msgstr "" + +msgid "The flowtable" +msgstr "" + +msgid "The internet cut is switched on, but its firewall rule is missing." +msgstr "" + +msgid "" +"The kernel fast-paths established connections through a flowtable, bypassing con" +"ntrack byte updates for higher throughput." +msgstr "" + +msgid "" +"The router offloads established connections from the CPU to the hardware (NIC/So" +"C), achieving 2–3× higher throughput and lower CPU usage." msgstr "" msgid "Throttle removed" msgstr "" -msgid "Traffic Control" +msgid "Time window for speed averaging" msgstr "" -msgid "Unblock" +msgid "To restore speed monitoring:" msgstr "" -msgid "Unblocking" +msgid "Token:" msgstr "" -msgid "Unknown error" +msgid "Top application by traffic" msgstr "" -msgid "WiFi" +msgid "Top application by traffic (needs the netifyd DPI agent)" msgstr "" -msgid "WiFi Block" +msgid "Top talkers" msgstr "" -msgid "WiFi Unblock" +msgid "" +"Total bytes transferred (download + upload), accumulated on the router. Resets o" +"n reboot; hover a cell for the start time" msgstr "" -msgid "WiFi blocked" -msgstr "" - -msgid "WiFi deny list" -msgstr "" - -msgid "blocked" -msgstr "" - -msgid "connections" -msgstr "" - -msgid "dropped" -msgstr "" - -msgid "groups from" -msgstr "" - -msgid "ok" -msgstr "" - -msgid "resolving…" -msgstr "" - -msgid "value" -msgstr "" - -msgid "Extended stats" -msgstr "" - -msgid "Extended Statistics" -msgstr "" - -msgid "Poll" -msgstr "" - -msgid "Avg window" -msgstr "" - -msgid "Method" -msgstr "" - -msgid "Simple" -msgstr "" - -msgid "EWMA" -msgstr "" - -msgid "Drops" -msgstr "" - -msgid "Overlimits" -msgstr "" - -msgid "ECN marks" -msgstr "" - -msgid "Flows" -msgstr "" - -msgid "new" -msgstr "" - -msgid "old" -msgstr "" - -msgid "Queue memory" -msgstr "" - -msgid "Lended" -msgstr "" - -msgid "Borrowed" -msgstr "" - -msgid "Utilization" -msgstr "" - -msgid "Packets dropped" -msgstr "" - -msgid "Bytes dropped" -msgstr "" - -msgid "Drop ratio" -msgstr "" - -msgid "No extended stats available for this device." -msgstr "" - -msgid "What do these stats mean?" -msgstr "" - -msgid "packets dropped by queue overflow" -msgstr "" - -msgid "rate exceeded events" -msgstr "" - -msgid "congestion signals without drop" -msgstr "" - -msgid "active concurrent connections in queue" -msgstr "" - -msgid "bytes allocated by the queue discipline" -msgstr "" - -msgid "own-rate vs parent-rate packets" -msgstr "" - -msgid "current speed as percentage of rate limit" -msgstr "" - -msgid "traffic discarded by nft policer" -msgstr "" - -msgid "percentage of total traffic that was dropped" -msgstr "" - -msgid "Shaped devices" +msgid "Total dropped" msgstr "" msgid "Total drops" @@ -340,17 +1007,331 @@ msgstr "" msgid "Total queue memory" msgstr "" -msgid "Limited devices" +msgid "Total since boot (RX / TX)" msgstr "" -msgid "Total dropped" +msgid "Traffic Control" +msgstr "" + +msgid "" +"Traffic is flowing right now. It is normally re-asserted within seconds — if thi" +"s persists, switch it off and on again." +msgstr "" + +msgid "Traffic shaping (tc/HTB) — bypassed for offloaded flows" +msgstr "" + +msgid "Traffic shaping and blocking are not affected." +msgstr "" + +msgid "" +"UDP bytes transferred, accumulated on the router. Not measurable while flow offl" +"oad is active" +msgstr "" + +msgid "UDP: %d" +msgstr "" + +msgid "UL Speed" +msgstr "" + +msgid "Unblock Internet" +msgstr "" + +msgid "Unblock WiFi" +msgstr "" + +msgid "Unknown error" +msgstr "" + +msgid "Unknown — device unreachable" +msgstr "" + +msgid "Until I switch it back" +msgstr "" + +msgid "Upload · %s" +msgstr "" + +msgid "Utilization" +msgstr "" + +msgid "Variables" +msgstr "" + +msgid "Visible columns" +msgstr "" + +msgid "WAN IP" +msgstr "" + +msgid "Waiting for a second sample…" +msgstr "" + +msgid "Waiting for the first byte sample." +msgstr "" + +msgid "Whole network" +msgstr "" + +msgid "WiFi SSID" +msgstr "" + +msgid "WiFi band (2.4/5GHz)" +msgstr "" + +msgid "WiFi block NOT in effect" +msgstr "" + +msgid "WiFi blocked" +msgstr "" + +msgid "WiFi blocking and internet blocking of new connections still work." +msgstr "" + +msgid "WiFi deny list" +msgstr "" + +msgid "WiFi signal (dBm)" +msgstr "" + +msgid "WiFi:" +msgstr "" + +msgid "Window" +msgstr "" + +msgid "You will need LAN or physical access to undo this — a reboot will not clear it." +msgstr "" + +msgid "active concurrent connections in queue" +msgstr "" + +msgid "all active blocks, limits, and shapes" +msgstr "" + +msgid "between them — one bucket for the whole subnet." +msgstr "" + +msgid "bit/s" +msgstr "" + +msgid "blocked" +msgstr "" + +msgid "bytes allocated by the queue discipline" +msgstr "" + +msgid "clear filter" +msgstr "" + +msgid "command list and bot mode" +msgstr "" + +msgid "congestion signals without drop" +msgstr "" + +msgid "connection type (5G, LAN)" +msgstr "" + +msgid "connections" +msgstr "" + +msgid "current speed as percentage of rate limit" +msgstr "" + +msgid "date (2026-05-26)" +msgstr "" + +msgid "date + time" +msgstr "" + +msgid "device connections" +msgstr "" + +msgid "device hostname" +msgstr "" + +msgid "device list → tap device → action buttons" +msgstr "" + +msgid "disabled" +msgstr "" + +msgid "down" +msgstr "" + +msgid "dropped" +msgstr "" + +msgid "each" +msgstr "" + +msgid "every device" +msgstr "" + +msgid "failed" +msgstr "" + +msgid "" +"flag (Linux 5.7+, set automatically by fw4/nftables) periodically syncs hardware" +" byte counts back to conntrack — trafficctl detects this and all features work n" +"ormally." +msgstr "" + +msgid "" +"flag should sync hardware byte counts back to conntrack, but on many platforms (" +"e.g. Mediatek Filogic) the driver does not implement the stats callback, so conn" +"track counters remain frozen for active flows." +msgstr "" + +msgid "groups from" +msgstr "" + +msgid "h" +msgstr "" + +msgid "" +"is not one of the subnets this router monitors, so a limit on it would be accept" +"ed but never enforced. A firewall zone other than \"lan\" with masq=1 (the usual g" +"uest-VLAN setup) is excluded. Check with: uci show firewall | grep -E \"name=|mas" +"q=\"" +msgstr "" + +msgid "kbit/s" +msgstr "" + +msgid "line break" +msgstr "" + +msgid "may use" +msgstr "" + +msgid "min" +msgstr "" + +msgid "network interface" +msgstr "" + +msgid "new" +msgstr "" + +msgid "nftables: Flowtables ↗" +msgstr "" + +msgid "nothing dropped yet" +msgstr "" + +msgid "of its own." +msgstr "" + +msgid "old" +msgstr "" + +msgid "on the deny list but still connected; install hostapd-utils or restart WiFi" +msgstr "" + +msgid "open port" +msgstr "" + +msgid "own-rate vs parent-rate packets" +msgstr "" + +msgid "packets dropped by queue overflow" +msgstr "" + +msgid "paused" +msgstr "" + +msgid "per device" +msgstr "" + +msgid "percentage of total traffic that was dropped" msgstr "" msgid "pkts" msgstr "" -msgid "No extended stats available." +msgid "port" msgstr "" -msgid "all devices" +msgid "port forward" +msgstr "" + +msgid "rDNS" +msgstr "" + +msgid "rate exceeded events" +msgstr "" + +msgid "resolving…" +msgstr "" + +msgid "routed" +msgstr "" + +msgid "router" +msgstr "" + +msgid "router hostname" +msgstr "" + +msgid "router uptime" +msgstr "" + +msgid "s" +msgstr "" + +msgid "saved" +msgstr "" + +msgid "sending or receiving now" +msgstr "" + +msgid "share" +msgstr "" + +msgid "shared" +msgstr "" + +msgid "shared between them" +msgstr "" + +msgid "these are never limited by this setting." +msgstr "" + +msgid "time (14:32)" +msgstr "" + +msgid "total connected clients" +msgstr "" + +msgid "traffic discarded by nft policer" +msgstr "" + +msgid "under a minute" +msgstr "" + +msgid "value" +msgstr "" + +msgid "↑ UL" +msgstr "" + +msgid "" +"↓ / ↑ are relative to the interface: on the WAN ↓ is your download, on a LAN bri" +"dge ↓ is what the LAN sent upstream." +msgstr "" + +msgid "↓ DL" +msgstr "" + +msgid "" +"🆕 New device\n" +"{{ name }} ({{ ip }})\n" +"MAC: {{ mac }}\n" +"Link: {{ link }}\n" +msgstr "" + +msgid "🆕 New device\\n{{ name }} ({{ ip }})\\nMAC: {{ mac }}\\nLink: {{ link }}" msgstr "" diff --git a/luci-app-trafficctl/po/zh-cn b/luci-app-trafficctl/po/zh-cn new file mode 120000 index 000000000..8d69574dd --- /dev/null +++ b/luci-app-trafficctl/po/zh-cn @@ -0,0 +1 @@ +zh_Hans \ No newline at end of file diff --git a/luci-app-trafficctl/po/zh_Hans/luci-app-trafficctl.po b/luci-app-trafficctl/po/zh_Hans/luci-app-trafficctl.po new file mode 100644 index 000000000..4c58f7202 --- /dev/null +++ b/luci-app-trafficctl/po/zh_Hans/luci-app-trafficctl.po @@ -0,0 +1,1361 @@ +msgid "" +msgstr "" +"Content-Type: text/plain; charset=UTF-8\n" +"Project-Id-Version: luci-app-trafficctl\n" +"Language: zh_Hans\n" +"MIME-Version: 1.0\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" + +msgid "\"all\" for every device, or a CIDR such as 10.0.20.0/24" +msgstr "\"all\"表示全部设备,或类似 10.0.20.0/24 的 CIDR" + +msgid "%d not applied" +msgstr "%d 个未生效" + +msgid "%d packets dropped (%s)" +msgstr "%d 个丢包 (%s)" + +msgid "%d pkts, %s dropped" +msgstr "%d 个包,%s 已丢弃" + +msgid "%s total" +msgstr "%s 总计" + +msgid "1 Mbit/s" +msgstr "1 Mbit/s" + +msgid "1 hour" +msgstr "1 小时" + +msgid "10 Mbit/s" +msgstr "10 Mbit/s" + +msgid "100 Mbit/s" +msgstr "100 Mbit/s" + +msgid "15 min" +msgstr "15 分钟" + +msgid "2 Mbit/s" +msgstr "2 Mbit/s" + +msgid "25 Mbit/s" +msgstr "25 Mbit/s" + +msgid "4 hours" +msgstr "4 小时" + +msgid "5 Mbit/s" +msgstr "5 Mbit/s" + +msgid "50 Mbit/s" +msgstr "50 Mbit/s" + +msgid "" +"Accelerates routing in the kernel via nftables flowtable. Speed monitoring and t" +"raffic shaping work normally." +msgstr "通过 nftables flowtable 在内核中加速路由。速度监控和流量整形正常工作。" + +msgid "Accumulated on the router" +msgstr "在路由器上累计" + +msgid "Accumulated since %s" +msgstr "自 %s 起累计" + +msgid "Action" +msgstr "操作" + +msgid "Active connections in conntrack" +msgstr "conntrack 中的活动连接" + +msgid "Active devices" +msgstr "活动设备" + +msgid "Active inbound connections" +msgstr "活动入站连接" + +msgid "Active subnet limits" +msgstr "活动子网限速" + +msgid "Active:" +msgstr "活动:" + +msgid "Activity" +msgstr "活动日志" + +msgid "Adding to" +msgstr "添加到" + +msgid "All" +msgstr "全部" + +msgid "All active devices" +msgstr "全部活动设备" + +msgid "All devices" +msgstr "所有设备" + +msgid "All of" +msgstr "所有" + +msgid "App" +msgstr "应用" + +msgid "" +"Applied once, the first time a device appears on the network. A device that alre" +"ady has a limit is left alone." +msgstr "首次有设备出现在网络时应用一次。已有速度限制的设备不会受影响。" + +msgid "Applied — firewall reloading" +msgstr "已应用 — 防火墙重新加载中" + +msgid "Apply" +msgstr "应用" + +msgid "Applying…" +msgstr "应用中…" + +msgid "Auto-refresh interval for summary table" +msgstr "概览表自动刷新间隔" + +msgid "Avg" +msgstr "平均" + +msgid "BLOCKED" +msgstr "已阻断" + +msgid "Behind a downstream router (routed subnet)" +msgstr "位于下游路由器后(路由子网)" + +msgid "Block Internet" +msgstr "阻断互联网" + +msgid "Block WiFi" +msgstr "阻断 WiFi" + +msgid "Blocked from WiFi (MAC deny list)" +msgstr "已从 WiFi 阻断(MAC 拒绝列表)" + +msgid "Blocked:" +msgstr "已阻断:" + +msgid "Blocking, rate limiting, and traffic shaping continue to work regardless." +msgstr "阻断、限速和流量整形功能仍然有效。" + +msgid "Blocks" +msgstr "阻断" + +msgid "Bot commands:" +msgstr "机器人命令:" + +msgid "Bot is running" +msgstr "机器人运行中" + +msgid "Bot is stopped" +msgstr "机器人已停止" + +msgid "Bridge port — these bytes are also counted in its bridge" +msgstr "桥接端口 — 这些字节也计入其所在网桥" + +msgid "" +"Bridge ports, switch conduits and ifb mirrors. Their bytes are already counted i" +"n the bridge or uplink above." +msgstr "桥接端口、交换机通道和 ifb 镜像。它们的字节已计入上方网桥或上行链路。" + +msgid "Byte totals unavailable on this router." +msgstr "此路由器的字节总计不可用。" + +msgid "Bytes" +msgstr "字节" + +msgid "Bytes dropped" +msgstr "丢包字节数" + +msgid "Bytes queued in tc" +msgstr "tc 中队列字节数" + +msgid "Bytes queued in traffic shaper" +msgstr "流量整形器中队列字节数" + +msgid "Bytes received from remote clients" +msgstr "从远程客户端接收的字节数" + +msgid "Bytes sent back to remote clients" +msgstr "发送回远程客户端的字节数" + +msgid "CPU load (1 min)" +msgstr "CPU 负载(1 分钟)" + +msgid "Calculating…" +msgstr "计算中…" + +msgid "Cancel" +msgstr "取消" + +msgid "Carries the default route" +msgstr "承载默认路由" + +msgid "Chat ID" +msgstr "聊天 ID" + +msgid "Chat ID:" +msgstr "聊天 ID:" + +msgid "Click a row to inspect that device. Download speed updates every 2 seconds." +msgstr "点击行查看该设备。下载速度每 2 秒更新。" + +msgid "Click again to confirm" +msgstr "再次点击确认" + +msgid "Click header to sort." +msgstr "点击表头排序。" + +msgid "Click to inspect %s" +msgstr "点击查看 %s" + +msgid "Click to toggle column visibility" +msgstr "点击切换列显示" + +msgid "Clients" +msgstr "客户端" + +msgid "Collecting interface counters…" +msgstr "正在收集接口计数器…" + +msgid "Config" +msgstr "配置" + +msgid "Connection interface (WiFi band, LAN port or routed)" +msgstr "连接接口(WiFi 频段、LAN 端口或路由)" + +msgid "Connections" +msgstr "连接" + +msgid "Connections: %d" +msgstr "连接数:%d" + +msgid "Conns" +msgstr "连接数" + +msgid "Control" +msgstr "控制" + +msgid "Current download speed (bytes/sec from router to device)" +msgstr "当前下载速度(字节/秒,从路由器到设备)" + +msgid "Current mode:" +msgstr "当前模式:" + +msgid "Current upload speed (bytes/sec from device to router)" +msgstr "当前上传速度(字节/秒,从设备到路由器)" + +msgid "Currently tracked connections hold %s" +msgstr "当前跟踪的连接持有 %s" + +msgid "Custom" +msgstr "自定义" + +msgid "Customize message" +msgstr "自定义消息" + +msgid "Custom…" +msgstr "自定义…" + +msgid "Cut all internet access" +msgstr "切断所有互联网访问" + +msgid "Cut internet" +msgstr "切断互联网" + +msgid "Cutting…" +msgstr "切断中…" + +msgid "DL Speed" +msgstr "下载速度" + +msgid "Destination" +msgstr "目标" + +msgid "Device" +msgstr "设备" + +msgid "Device hostname from DHCP lease" +msgstr "来自 DHCP 租约的设备主机名" + +msgid "Device name" +msgstr "设备名称" + +msgid "Devices already known:" +msgstr "已知设备:" + +msgid "" +"Devices keep talking to each other and to the router — only the way out is close" +"d. Traffic is stopped on the way in, so a transparent proxy on the router (podko" +"p, passwall) cannot carry it out either. Remote access that reaches the router t" +"hrough a LAN host (Tailscale, a tunnel, a jump host) stops working while this is" +" on." +msgstr "" +"设备之间及与路由器仍可通信 — 只是对外出口被关闭。入站流量也被阻止,因此路由器上的透明代理(podkop、passwall)也无法将其带出。通过 LAN 主机" +"(Tailscale、隧道、跳转主机)到达路由器的远程访问在此期间停止工作。" + +msgid "" +"Devices on the WiFi deny list that are still connected — those blocks are not in" +" effect on the running radio." +msgstr "仍在连接的 WiFi 拒绝列表设备 — 这些阻断在当前无线未生效。" + +msgid "Disable hardware offload (keeps software offload):" +msgstr "关闭硬件卸载(保留软件卸载):" + +msgid "Dismiss" +msgstr "关闭" + +msgid "Display" +msgstr "显示" + +msgid "Distinct remote clients" +msgstr "不同远程客户端数" + +msgid "Done" +msgstr "完成" + +msgid "Download · %s" +msgstr "下载 · %s" + +msgid "Drop" +msgstr "丢包" + +msgid "Drop ratio" +msgstr "丢包率" + +msgid "Dropped:" +msgstr "已丢弃:" + +msgid "Dropped: 🚫 %d pkts / %s" +msgstr "已丢弃:🚫 %d 个包 / %s" + +msgid "Drops" +msgstr "丢包数" + +msgid "Drops excess packets (instant enforcement, low overhead)" +msgstr "丢弃超额数据包(即时生效、开销低)" + +msgid "Dst IP" +msgstr "目标 IP" + +msgid "ECN marks" +msgstr "ECN 标记" + +msgid "EWMA" +msgstr "指数加权移动平均" + +msgid "Enabled" +msgstr "已启用" + +msgid "Enter a custom speed value" +msgstr "输入自定义速度值" + +msgid "Every device in" +msgstr "每个设备在" + +msgid "Every device this router forwards for" +msgstr "此路由器转发的每个设备" + +msgid "Ext. port" +msgstr "外部端口" + +msgid "Extended" +msgstr "扩展" + +msgid "Extended Statistics" +msgstr "扩展统计" + +msgid "Extended Statistics (all devices)" +msgstr "扩展统计(所有设备)" + +msgid "External (WAN) port" +msgstr "外部(WAN)端口" + +msgid "Filter connections by protocol" +msgstr "按协议筛选连接" + +msgid "Flow Offload" +msgstr "流卸载" + +msgid "" +"Flow offload is active and the kernel does not support the nftables counter maps" +" trafficctl uses in that mode, so byte counters stop updating for offloaded conn" +"ections. Speeds and totals would be far too low, so they are not shown. Disablin" +"g hardware offload in Settings → Flow Offload restores them." +msgstr "" +"流卸载已启用,但内核不支持 trafficctl 在该模式下使用的 nftables 计数器映射,因此卸载连接的字节计数器停止更新。速度和总计会严重偏低,故不显" +"示。在 设置 → 流卸载 中关闭硬件卸载可恢复。" + +msgid "Flow: /devices → select device → inline keyboard with enabled actions above" +msgstr "流程:/devices → 选择设备 → 使用上方启用的操作内联键盘" + +msgid "Flows" +msgstr "流数" + +msgid "" +"Forwarded traffic only — this kernel cannot install the prerouting rule, so traf" +"fic handled by a transparent proxy on the router would not be caught." +msgstr "仅转发流量 — 此内核无法安装 prerouting 规则,因此路由器上透明代理处理的流量不会被捕获。" + +msgid "Full control" +msgstr "完全控制" + +msgid "Gbit/s" +msgstr "Gbit/s" + +msgid "Global internet cut needs nftables (fw4). This router is running iptables." +msgstr "全局断网需要 nftables(fw4)。此路由器正在运行 iptables。" + +msgid "Graph" +msgstr "图表" + +msgid "Group" +msgstr "分组" + +msgid "Group connections table rows" +msgstr "对连接表行进行分组" + +msgid "HTML:" +msgstr "HTML:" + +msgid "Hardware MAC address" +msgstr "硬件 MAC 地址" + +msgid "Hardware flow offload" +msgstr "硬件流卸载" + +msgid "Hardware flow offloading active — real-time speed monitoring unavailable." +msgstr "硬件流卸载已启用 — 实时速度监控不可用。" + +msgid "Hardware flow offloading is active." +msgstr "硬件流卸载已启用。" + +msgid "Hardware offload" +msgstr "硬件卸载" + +msgid "Hostname" +msgstr "主机名" + +msgid "Hostname / Dst IP" +msgstr "主机名 / 目标 IP" + +msgid "" +"How often live data is polled: speeds, graphs, drop/backlog counters, and the co" +"nnection table in device view" +msgstr "实时数据轮询频率:速度、图表、丢包/队列计数器以及设备视图的连接表" + +msgid "How to get full functionality without disabling offload:" +msgstr "如何在不关闭卸载的情况下获得完整功能:" + +msgid "IP address" +msgstr "IP 地址" + +msgid "" +"If your current firmware uses fw3 (iptables) or ships a kernel older than 5.7, a" +" router with modern OpenWrt and fw4 support will have this working out of the bo" +"x." +msgstr "如果你当前固件使用 fw3(iptables)或内核版本低于 5.7,现代 OpenWrt 且支持 fw4 的路由器将开箱即用。" + +msgid "Iface" +msgstr "接口" + +msgid "In" +msgstr "入站" + +msgid "" +"In this mode the kernel's conntrack, firewall, and tc are bypassed for offloaded" +" flows:" +msgstr "此模式下,内核的 conntrack、防火墙和 tc 会绕过已卸载流:" + +msgid "Inbound rate limit (drops packets above the rate)" +msgstr "入站速率限制(超过速率则丢包)" + +msgid "" +"Inbound traffic control for DNAT port forwards and router-local open ports confi" +"gured in the firewall. Pause drops all inbound traffic on the port instantly (wi" +"thout touching the firewall config); Limit polices the inbound rate." +msgstr "针对防火墙中配置的 DNAT 端口转发和路由器本地开放端口的入站流量控制。暂停会立即丢弃该端口所有入站流量(不修改防火墙配置);限制会对入站速率进行管制。" + +msgid "Inet" +msgstr "互联网" + +msgid "Inline keyboard preview:" +msgstr "内联键盘预览:" + +msgid "Interfaces" +msgstr "接口" + +msgid "Internet access status (paused = traffic blocked)" +msgstr "互联网访问状态(暂停 = 流量已阻断)" + +msgid "Internet is cut for all devices" +msgstr "所有设备的互联网已切断" + +msgid "Kbit/s" +msgstr "Kbit/s" + +msgid "Keep after reboot" +msgstr "重启后保持" + +msgid "Kept after reboot — undoing it needs LAN or physical access." +msgstr "重启后仍保持 — 撤销需要 LAN 或物理访问。" + +msgid "Known devices" +msgstr "已知设备" + +msgid "LAN keeps working" +msgstr "LAN 保持可用" + +msgid "Last seen: %s, %s ago" +msgstr "上次出现:%s,%s 前" + +msgid "Lended / Borrowed" +msgstr "借出 / 借用" + +msgid "Limit" +msgstr "限速" + +msgid "Limit inbound to %s" +msgstr "入站限速到 %s" + +msgid "Limit new devices" +msgstr "限制新设备" + +msgid "Limit: %s" +msgstr "限速:%s" + +msgid "Limited devices" +msgstr "已限速设备" + +msgid "Limited subnets" +msgstr "已限速子网" + +msgid "Limited:" +msgstr "已限速:" + +msgid "Limiter" +msgstr "限速器" + +msgid "Limiter (nft drop)" +msgstr "限速器(nft 丢包)" + +msgid "Limiting %s → %s (%s)…" +msgstr "正在限速 %s → %s (%s)…" + +msgid "Limiting → %s…" +msgstr "正在限速 → %s…" + +msgid "Link" +msgstr "链路" + +msgid "Linux kernel: nf_flowtable ↗" +msgstr "Linux 内核:nf_flowtable ↗" + +msgid "Loading…" +msgstr "加载中…" + +msgid "Local IP address" +msgstr "本地 IP 地址" + +msgid "Log categories" +msgstr "日志类别" + +msgid "Logging" +msgstr "日志记录" + +msgid "Logging & Persistence" +msgstr "日志与持久化" + +msgid "MAC address" +msgstr "MAC 地址" + +msgid "MAC:" +msgstr "MAC:" + +msgid "Max" +msgstr "最大" + +msgid "Mbit/s" +msgstr "Mbit/s" + +msgid "Method" +msgstr "方法" + +msgid "Name" +msgstr "名称" + +msgid "New Device Defaults" +msgstr "新设备默认值" + +msgid "New devices" +msgstr "新设备" + +msgid "No action buttons enabled" +msgstr "未启用任何操作按钮" + +msgid "No active connections." +msgstr "无活动连接。" + +msgid "No active devices" +msgstr "无活动设备" + +msgid "No activity recorded yet." +msgstr "尚未记录活动。" + +msgid "" +"No baseline recorded yet. It is taken from the current leases when you switch th" +"is on." +msgstr "尚未记录基线。开启时会从当前租约获取。" + +msgid "No devices match filter." +msgstr "没有设备符合筛选条件。" + +msgid "No extended stats available for this device." +msgstr "该设备无可用扩展统计。" + +msgid "No extended stats available." +msgstr "无可用扩展统计。" + +msgid "No offload" +msgstr "无卸载" + +msgid "No port forwards or WAN-open ports are configured in the firewall." +msgstr "防火墙中未配置端口转发或 WAN 开放端口。" + +msgid "None (per-flow)" +msgstr "无(每流)" + +msgid "" +"Not counted: flow offload is active and this router cannot provide the nftables " +"counters trafficctl uses in that mode, so the kernel byte counters stop updating" +" for offloaded connections. Any total here would be far too low. See Settings → " +"Flow Offload." +msgstr "" +"未计数:流卸载已启用,此路由器无法提供 trafficctl 在该模式下使用的 nftables 计数器,因此内核字节计数器停止更新。任何显示值都会严重偏低。参" +"见 设置 → 流卸载。" + +msgid "Not enough data yet" +msgstr "数据不足" + +msgid "" +"Not measurable while flow offload is active: in that mode the router counts byte" +"s per address only, so traffic cannot be split by protocol. See Settings → Flow " +"Offload." +msgstr "流卸载启用时无法测量:该模式下路由器仅按地址计数字节,因此无法按协议拆分。参见 设置 → 流卸载。" + +msgid "Note: speed is measured before shaper — bursts above limit are normal" +msgstr "注意:速度在整形器之前测量 — 超出限制的突发属正常" + +msgid "Notifications" +msgstr "通知" + +msgid "Notifications only" +msgstr "仅通知" + +msgid "Off" +msgstr "关闭" + +msgid "" +"Offloads routing to the hardware engine (PPE/NPU). Requires software offload. ⚠ " +"On many platforms (e.g. Mediatek Filogic) the driver does not report byte counte" +"rs back to the kernel — real-time speed monitoring will show zero." +msgstr "" +"将路由卸载到硬件引擎(PPE/NPU)。需要软件卸载。⚠ 在许多平台(如 Mediatek Filogic)上,驱动不会将字节计数器报告回内核 — 实时速度监控" +"将显示为零。" + +msgid "On %s" +msgstr "在 %s 上" + +msgid "" +"On the WiFi deny list but still connected — the block is NOT in effect on the ru" +"nning radio. Install hostapd-utils so blocks apply immediately, or restart WiFi " +"(disconnects every client)." +msgstr "在 WiFi 拒绝列表中但仍连接 — 阻断在当前无线未生效。安装 hostapd-utils 可立即生效,或重启 WiFi(会断开所有客户端)。" + +msgid "One bucket for the whole subnet" +msgstr "整个子网一个桶" + +msgid "One bucket per device inside the subnet" +msgstr "子网内每个设备一个桶" + +msgid "Open DHCP/DNS bindings" +msgstr "打开 DHCP/DNS 绑定" + +msgid "OpenWrt: Flow offloading ↗" +msgstr "OpenWrt:Flow offloading ↗" + +msgid "Operation failed" +msgstr "操作失败" + +msgid "Or disable all flow offload in LuCI → Network → Firewall → General Settings." +msgstr "或在 LuCI → 网络 → 防火墙 → 常规设置 中关闭所有流卸载。" + +msgid "Other interfaces" +msgstr "其他接口" + +msgid "Out" +msgstr "出站" + +msgid "Overlimits" +msgstr "超限次数" + +msgid "Overrides the DHCP lease or DNS name. Leave empty to clear the custom name." +msgstr "覆盖 DHCP 租约或 DNS 名称。留空可清除自定义名称。" + +msgid "Overview" +msgstr "总览" + +msgid "Packets dropped" +msgstr "丢包数" + +msgid "Packets dropped by rate limiter" +msgstr "限速器丢包数" + +msgid "Passed: %s" +msgstr "通过:%s" + +msgid "Paste bot token" +msgstr "粘贴机器人令牌" + +msgid "Pause" +msgstr "暂停" + +msgid "Persist rules" +msgstr "持久化规则" + +msgid "Pick a rate to apply it to this target, or Off to remove its limit." +msgstr "选择速率以应用到该目标,或选择关闭以移除其限制。" + +msgid "Poll" +msgstr "轮询" + +msgid "Polling is off — set the Poll interval to see throughput." +msgstr "轮询已关闭 — 设置轮询间隔以查看吞吐量。" + +msgid "Port" +msgstr "端口" + +msgid "Port Forwards & Open Ports" +msgstr "端口转发与开放端口" + +msgid "Preview:" +msgstr "预览:" + +msgid "Proto" +msgstr "协议" + +msgid "Protocol" +msgstr "协议" + +msgid "Queue" +msgstr "队列" + +msgid "Queue memory" +msgstr "队列内存" + +msgid "Queued: %s" +msgstr "队列中:%s" + +msgid "Queues excess traffic (smoother streaming, lower jitter)" +msgstr "对超额流量排队(更流畅流媒体、更低抖动)" + +msgid "Rate" +msgstr "速率" + +msgid "Rate limiting — applies only to new connections" +msgstr "速率限制 — 仅适用于新连接" + +msgid "Ratelimits" +msgstr "速率限制" + +msgid "Received by this interface" +msgstr "该接口接收" + +msgid "Recent" +msgstr "最近" + +msgid "Refresh" +msgstr "刷新" + +msgid "Remove" +msgstr "移除" + +msgid "Remove limit" +msgstr "移除限速" + +msgid "Removing from" +msgstr "从…移除" + +msgid "Removing limit on %s…" +msgstr "正在移除 %s 上的限速…" + +msgid "Removing throttle…" +msgstr "正在移除节流…" + +msgid "Rename device" +msgstr "重命名设备" + +msgid "Rename failed" +msgstr "重命名失败" + +msgid "Resets when the router reboots." +msgstr "路由器重启时重置。" + +msgid "Restore internet" +msgstr "恢复互联网" + +msgid "Restores by itself in" +msgstr "自动恢复于" + +msgid "Restoring…" +msgstr "恢复中…" + +msgid "Resume" +msgstr "恢复" + +msgid "Routed via %s" +msgstr "经由 %s 路由" + +msgid "Running…" +msgstr "运行中…" + +msgid "Save" +msgstr "保存" + +msgid "Save as router default" +msgstr "保存为路由器默认值" + +msgid "Saving…" +msgstr "保存中…" + +msgid "Scanning all devices…" +msgstr "正在扫描所有设备…" + +msgid "Search device (name, IP, MAC)…" +msgstr "搜索设备(名称、IP、MAC)…" + +msgid "Sending…" +msgstr "发送中…" + +msgid "Sent by this interface" +msgstr "该接口发送" + +msgid "Service" +msgstr "服务" + +msgid "Set a rate above 0 — with no rate this does nothing." +msgstr "设置大于 0 的速率 — 否则无效。" + +msgid "Settings" +msgstr "设置" + +msgid "Shaped devices" +msgstr "已整形设备" + +msgid "Shaped devices are usually not offloaded (kernel detects the HTB qdisc)." +msgstr "已整形设备通常不会被卸载(内核会检测到 HTB qdisc)。" + +msgid "Shaped:" +msgstr "已整形:" + +msgid "Shaped: 🌊 %s" +msgstr "已整形:🌊 %s" + +msgid "Shaper" +msgstr "整形器" + +msgid "Shaper (tc/HTB queue)" +msgstr "整形器(tc/HTB 队列)" + +msgid "Shapes" +msgstr "整形" + +msgid "Shaping → %s…" +msgstr "正在整形 → %s…" + +msgid "Share:" +msgstr "共享:" + +msgid "Show/hide token" +msgstr "显示/隐藏令牌" + +msgid "Simple" +msgstr "简单" + +msgid "Simple = arithmetic mean, EWMA = exponential weighted moving average" +msgstr "简单 = 算术平均,EWMA = 指数加权移动平均" + +msgid "Software flow offload" +msgstr "软件流卸载" + +msgid "Software flow offloading is active." +msgstr "软件流卸载已启用。" + +msgid "Software offload" +msgstr "软件卸载" + +msgid "Software offload (counter)" +msgstr "软件流卸载(计数器模式)" + +msgid "Speed Limit" +msgstr "速度限制" + +msgid "Speed graph. Window = avg time. Orange dashed line = speed limit" +msgstr "速度图表。窗口 = 平均时间。橙色虚线 = 速度限制" + +msgid "" +"Speed is measured via nftables counters installed at a higher priority (before t" +"he flowtable), so graphs are accurate." +msgstr "速度通过安装在更高优先级(flowtable 之前)的 nftables 计数器测量,因此图表准确。" + +msgid "Speed limit: ⚡ %s" +msgstr "速度限制:⚡ %s" + +msgid "Speed limit: shaper (queue) or limiter (drop)" +msgstr "速度限制:整形器(队列)或限速器(丢包)" + +msgid "Speed monitoring — conntrack byte counters are not updated" +msgstr "速度监控 — conntrack 字节计数器未更新" + +msgid "State" +msgstr "状态" + +msgid "Stats" +msgstr "统计" + +msgid "Stays off until you switch it back." +msgstr "保持关闭直到你手动恢复。" + +msgid "" +"Store the current Poll and Window as this router's defaults, for browsers that h" +"ave not set their own" +msgstr "将当前轮询和窗口保存为此路由器的默认值,供未自行设置的浏览器使用" + +msgid "Switch it off" +msgstr "关闭它" + +msgid "Syslog" +msgstr "系统日志" + +msgid "" +"TCP bytes transferred, accumulated on the router. Not measurable while flow offl" +"oad is active" +msgstr "TCP 已传输字节,在路由器上累计。流卸载启用时无法测量" + +msgid "TCP: %d" +msgstr "TCP:%d" + +msgid "Table & Speed" +msgstr "表格与速度" + +msgid "Target:" +msgstr "目标:" + +msgid "Telegram" +msgstr "Telegram" + +msgid "Telegram Bot" +msgstr "Telegram 机器人" + +msgid "Test" +msgstr "测试" + +msgid "" +"The auto-revert helper is not running; this page re-checks the deadline while it" +" is open." +msgstr "自动恢复助手未运行;此页面在打开时会重新检查截止时间。" + +msgid "The flowtable" +msgstr "flowtable" + +msgid "The internet cut is switched on, but its firewall rule is missing." +msgstr "互联网切断已开启,但其防火墙规则缺失。" + +msgid "" +"The kernel fast-paths established connections through a flowtable, bypassing con" +"ntrack byte updates for higher throughput." +msgstr "内核通过 flowtable 对已建立连接进行快速转发,绕过 conntrack 字节更新以提高吞吐量。" + +msgid "" +"The router offloads established connections from the CPU to the hardware (NIC/So" +"C), achieving 2–3× higher throughput and lower CPU usage." +msgstr "路由器将已建立连接从 CPU 卸载到硬件(NIC/SoC),实现 2–3 倍更高吞吐量和更低 CPU 占用。" + +msgid "Throttle removed" +msgstr "节流已移除" + +msgid "Time window for speed averaging" +msgstr "速度平均时间窗口" + +msgid "To restore speed monitoring:" +msgstr "恢复速度监控:" + +msgid "Token:" +msgstr "令牌:" + +msgid "Top application by traffic" +msgstr "按流量排序的热门应用" + +msgid "Top application by traffic (needs the netifyd DPI agent)" +msgstr "按流量排序的热门应用(需要 netifyd DPI 代理)" + +msgid "Top talkers" +msgstr " Top 发言人" + +msgid "" +"Total bytes transferred (download + upload), accumulated on the router. Resets o" +"n reboot; hover a cell for the start time" +msgstr "总传输字节(下载 + 上传),在路由器上累计。重启时重置;悬停单元格查看开始时间" + +msgid "Total dropped" +msgstr "总丢包" + +msgid "Total drops" +msgstr "总丢包数" + +msgid "Total queue memory" +msgstr "总队列内存" + +msgid "Total since boot (RX / TX)" +msgstr "启动以来总计(RX / TX)" + +msgid "Traffic Control" +msgstr "流量控制" + +msgid "" +"Traffic is flowing right now. It is normally re-asserted within seconds — if thi" +"s persists, switch it off and on again." +msgstr "流量目前正在流通。通常会在数秒内重新生效 — 如持续存在,请关闭后再开启。" + +msgid "Traffic shaping (tc/HTB) — bypassed for offloaded flows" +msgstr "流量整形(tc/HTB)— 对卸载流被绕过" + +msgid "Traffic shaping and blocking are not affected." +msgstr "流量整形和阻断不受影响。" + +msgid "" +"UDP bytes transferred, accumulated on the router. Not measurable while flow offl" +"oad is active" +msgstr "UDP 已传输字节,在路由器上累计。流卸载启用时无法测量" + +msgid "UDP: %d" +msgstr "UDP:%d" + +msgid "UL Speed" +msgstr "上传速度" + +msgid "Unblock Internet" +msgstr "解除互联网阻断" + +msgid "Unblock WiFi" +msgstr "解除 WiFi 阻断" + +msgid "Unknown error" +msgstr "未知错误" + +msgid "Unknown — device unreachable" +msgstr "未知 — 设备不可达" + +msgid "Until I switch it back" +msgstr "直到我手动恢复" + +msgid "Upload · %s" +msgstr "上传 · %s" + +msgid "Utilization" +msgstr "利用率" + +msgid "Variables" +msgstr "变量" + +msgid "Visible columns" +msgstr "可见列" + +msgid "WAN IP" +msgstr "WAN IP" + +msgid "Waiting for a second sample…" +msgstr "等待第二个样本…" + +msgid "Waiting for the first byte sample." +msgstr "等待第一个字节样本。" + +msgid "Whole network" +msgstr "整个网络" + +msgid "WiFi SSID" +msgstr "WiFi SSID" + +msgid "WiFi band (2.4/5GHz)" +msgstr "WiFi 频段(2.4/5GHz)" + +msgid "WiFi block NOT in effect" +msgstr "WiFi 阻断未生效" + +msgid "WiFi blocked" +msgstr "WiFi 已阻断" + +msgid "WiFi blocking and internet blocking of new connections still work." +msgstr "WiFi 阻断和新连接的互联网阻断仍然有效。" + +msgid "WiFi deny list" +msgstr "WiFi 拒绝列表" + +msgid "WiFi signal (dBm)" +msgstr "WiFi 信号(dBm)" + +msgid "WiFi:" +msgstr "WiFi:" + +msgid "Window" +msgstr "窗口" + +msgid "You will need LAN or physical access to undo this — a reboot will not clear it." +msgstr "你需要 LAN 或物理访问来撤销此操作 — 重启不会清除。" + +msgid "active concurrent connections in queue" +msgstr "队列中活动并发连接" + +msgid "all active blocks, limits, and shapes" +msgstr "所有活动阻断、限速和整形" + +msgid "between them — one bucket for the whole subnet." +msgstr "它们之间 — 整个子网一个桶。" + +msgid "bit/s" +msgstr "bit/s" + +msgid "blocked" +msgstr "已阻断" + +msgid "bytes allocated by the queue discipline" +msgstr "队列规则分配的字节数" + +msgid "clear filter" +msgstr "清除筛选" + +msgid "command list and bot mode" +msgstr "命令列表和机器人模式" + +msgid "congestion signals without drop" +msgstr "未丢包的拥塞信号" + +msgid "connection type (5G, LAN)" +msgstr "连接类型(5G、LAN)" + +msgid "connections" +msgstr "个连接" + +msgid "current speed as percentage of rate limit" +msgstr "当前速度占速率限制的百分比" + +msgid "date (2026-05-26)" +msgstr "日期(2026-05-26)" + +msgid "date + time" +msgstr "日期 + 时间" + +msgid "device connections" +msgstr "设备连接数" + +msgid "device hostname" +msgstr "设备主机名" + +msgid "device list → tap device → action buttons" +msgstr "设备列表 → 点击设备 → 操作按钮" + +msgid "disabled" +msgstr "已禁用" + +msgid "down" +msgstr "断开" + +msgid "dropped" +msgstr "已丢弃" + +msgid "each" +msgstr "每个" + +msgid "every device" +msgstr "每个设备" + +msgid "failed" +msgstr "失败" + +msgid "" +"flag (Linux 5.7+, set automatically by fw4/nftables) periodically syncs hardware" +" byte counts back to conntrack — trafficctl detects this and all features work n" +"ormally." +msgstr "" +" counter 标志(Linux 5.7+,由 fw4/nftables 自动设置)会定期将硬件字节数同步回 conntrack — trafficctl 检" +"测到后所有功能均正常工作。" + +msgid "" +"flag should sync hardware byte counts back to conntrack, but on many platforms (" +"e.g. Mediatek Filogic) the driver does not implement the stats callback, so conn" +"track counters remain frozen for active flows." +msgstr "" +" counter 标志应将硬件字节数同步回 conntrack,但在许多平台(如 Mediatek Filogic)上驱动未实现统计回调,因此 conntrac" +"k 计数器对已激活流保持冻结。" + +msgid "groups from" +msgstr "个分组来自" + +msgid "h" +msgstr "小时" + +msgid "" +"is not one of the subnets this router monitors, so a limit on it would be accept" +"ed but never enforced. A firewall zone other than \"lan\" with masq=1 (the usual g" +"uest-VLAN setup) is excluded. Check with: uci show firewall | grep -E \"name=|mas" +"q=\"" +msgstr "" +"不是此路由器监控的子网之一,因此对其设置的限制会被接受但永不执行。除 \"lan\" 外且 masq=1 的防火墙区域(通常是访客 VLAN)被排除。检查:uci " +"show firewall | grep -E \"name=|masq=\"" + +msgid "kbit/s" +msgstr "kbit/s" + +msgid "line break" +msgstr "换行" + +msgid "may use" +msgstr "可使用" + +msgid "min" +msgstr "分钟" + +msgid "network interface" +msgstr "网络接口" + +msgid "new" +msgstr "新" + +msgid "nftables: Flowtables ↗" +msgstr "nftables:Flowtables ↗" + +msgid "nothing dropped yet" +msgstr "尚无丢包" + +msgid "of its own." +msgstr "其自有速率。" + +msgid "old" +msgstr "旧" + +msgid "on the deny list but still connected; install hostapd-utils or restart WiFi" +msgstr "在拒绝列表中但仍连接;安装 hostapd-utils 或重启 WiFi" + +msgid "open port" +msgstr "开放端口" + +msgid "own-rate vs parent-rate packets" +msgstr "自身速率与父速率数据包" + +msgid "packets dropped by queue overflow" +msgstr "队列溢出丢包数" + +msgid "paused" +msgstr "已暂停" + +msgid "per device" +msgstr "每个设备" + +msgid "percentage of total traffic that was dropped" +msgstr "被丢弃流量占总流量的百分比" + +msgid "pkts" +msgstr "包" + +msgid "port" +msgstr "端口" + +msgid "port forward" +msgstr "端口转发" + +msgid "rDNS" +msgstr "rDNS" + +msgid "rate exceeded events" +msgstr "速率超出事件" + +msgid "resolving…" +msgstr "解析中…" + +msgid "routed" +msgstr "已路由" + +msgid "router" +msgstr "路由器" + +msgid "router hostname" +msgstr "路由器主机名" + +msgid "router uptime" +msgstr "路由器运行时间" + +msgid "s" +msgstr "秒" + +msgid "saved" +msgstr "已保存" + +msgid "sending or receiving now" +msgstr "当前正在发送或接收" + +msgid "share" +msgstr "共享" + +msgid "shared" +msgstr "共享" + +msgid "shared between them" +msgstr "它们之间共享" + +msgid "these are never limited by this setting." +msgstr "这些从不受此设置限制。" + +msgid "time (14:32)" +msgstr "时间(14:32)" + +msgid "total connected clients" +msgstr "总连接客户端数" + +msgid "traffic discarded by nft policer" +msgstr "nft 管制器丢弃的流量" + +msgid "under a minute" +msgstr "不足一分钟" + +msgid "value" +msgstr "数值" + +msgid "↑ UL" +msgstr "↑ 上传" + +msgid "" +"↓ / ↑ are relative to the interface: on the WAN ↓ is your download, on a LAN bri" +"dge ↓ is what the LAN sent upstream." +msgstr "↓ / ↑ 相对于接口:在 WAN 上 ↓ 是你的下载,在 LAN 网桥上 ↓ 是 LAN 向上游发送的数据。" + +msgid "↓ DL" +msgstr "↓ 下载" + +msgid "" +"🆕 New device\n" +"{{ name }} ({{ ip }})\n" +"MAC: {{ mac }}\n" +"Link: {{ link }}\n" +msgstr "" +"🆕 新设备\n" +"{{ name }} ({{ ip }})\n" +"MAC:{{ mac }}\n" +"Link:{{ link }}\n" + +msgid "🆕 New device\\n{{ name }} ({{ ip }})\\nMAC: {{ mac }}\\nLink: {{ link }}" +msgstr "🆕 新设备\\n{{ name }} ({{ ip }})\\nMAC:{{ mac }}\\nLink:{{ link }}" diff --git a/luci-mod-status/Makefile b/luci-mod-status/Makefile index 93aed7460..6ce3a2260 100644 --- a/luci-mod-status/Makefile +++ b/luci-mod-status/Makefile @@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk LUCI_TITLE:=LuCI Status Pages LUCI_DEPENDS:=+luci-base +luci-app-oui +luci-mod-dashboard +libiwinfo +rpcd-mod-iwinfo +rpcd-mod-rrdns +ucode +ucode-mod-fs +ucode-mod-ubus +ucode-mod-socket -PKG_RELEASE:=34 +PKG_RELEASE:=35 PKG_BUILD_DEPENDS:=iwinfo PKG_LICENSE:=Apache-2.0 diff --git a/luci-mod-status/htdocs/luci-static/resources/view/status/include/26_ports.js b/luci-mod-status/htdocs/luci-static/resources/view/status/include/26_ports.js index c555ad6af..f72a202fb 100644 --- a/luci-mod-status/htdocs/luci-static/resources/view/status/include/26_ports.js +++ b/luci-mod-status/htdocs/luci-static/resources/view/status/include/26_ports.js @@ -214,8 +214,9 @@ function addResolvedPort(knownPorts, seenPorts, role, device, mapping, board, sw }); } else { - const deviceInfo = L.isObject(board) && L.isObject(board['network-device']) - ? board['network-device'][value] : null; + const devices = L.isObject(board) + ? (board['network-device'] || board.network_device) : null; + const deviceInfo = L.isObject(devices) ? devices[value] : null; addKnownPort(knownPorts, seenPorts, role, value, L.isObject(deviceInfo) && isString(deviceInfo.label) ? { label: deviceInfo.label } : null);