Merge pull request #1245 from danweller18/feature_js_headers
authorJo-Philipp Wich <jo@mein.io>
Wed, 2 Aug 2017 08:34:59 +0000 (10:34 +0200)
committerGitHub <noreply@github.com>
Wed, 2 Aug 2017 08:34:59 +0000 (10:34 +0200)
luci-base: Remove request headers that are set automatically by browser

112 files changed:
applications/luci-app-aria2/po/sv/aria2.po
applications/luci-app-clamav/po/sv/clamav.po
applications/luci-app-commands/po/sv/commands.po
applications/luci-app-ddns/po/sv/ddns.po
applications/luci-app-diag-core/po/sv/diag_core.po
applications/luci-app-firewall/luasrc/model/cbi/firewall/zone-details.lua
applications/luci-app-mwan3/luasrc/model/cbi/mwan/interface.lua
applications/luci-app-mwan3/luasrc/model/cbi/mwan/member.lua
applications/luci-app-mwan3/luasrc/model/cbi/mwan/policy.lua
applications/luci-app-mwan3/luasrc/model/cbi/mwan/rule.lua
applications/luci-app-mwan3/luasrc/view/mwan/advanced_diagnostics.htm
applications/luci-app-mwan3/luasrc/view/mwan/advanced_troubleshooting.htm
applications/luci-app-mwan3/luasrc/view/mwan/advanced_wirelessconfig.htm
applications/luci-app-mwan3/luasrc/view/mwan/openwrt_overview_status.htm
applications/luci-app-mwan3/luasrc/view/mwan/overview_detailed.htm
applications/luci-app-mwan3/luasrc/view/mwan/overview_interface.htm
applications/luci-app-mwan3/po/ja/mwan3.po
applications/luci-app-mwan3/po/templates/mwan3.pot
applications/luci-app-mwan3/po/zh-cn/mwan3.po
applications/luci-app-nlbwmon/Makefile [new file with mode: 0644]
applications/luci-app-nlbwmon/htdocs/luci-static/resources/nlbw.chart.min.js [new file with mode: 0644]
applications/luci-app-nlbwmon/luasrc/controller/nlbw.lua [new file with mode: 0644]
applications/luci-app-nlbwmon/luasrc/model/cbi/nlbw/config.lua [new file with mode: 0644]
applications/luci-app-nlbwmon/luasrc/view/nlbw/backup.htm [new file with mode: 0644]
applications/luci-app-nlbwmon/luasrc/view/nlbw/display.htm [new file with mode: 0644]
applications/luci-app-nlbwmon/po/ja/nlbwmon.po [new file with mode: 0644]
applications/luci-app-nlbwmon/po/templates/nlbwmon.pot [new file with mode: 0644]
applications/luci-app-nlbwmon/root/etc/uci-defaults/40_luci-nlbwmon [new file with mode: 0644]
applications/luci-app-shadowsocks-libev/Makefile
applications/luci-app-shadowsocks-libev/luasrc/controller/shadowsocks-libev.lua
applications/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev.lua [deleted file]
applications/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev/instance-details.lua [new file with mode: 0644]
applications/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev/instances.lua [new file with mode: 0644]
applications/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev/rules.lua [new file with mode: 0644]
applications/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev/servers.lua [new file with mode: 0644]
applications/luci-app-shadowsocks-libev/luasrc/model/shadowsocks-libev.lua [new file with mode: 0644]
applications/luci-app-shadowsocks-libev/luasrc/view/shadowsocks-libev/add_instance.htm [new file with mode: 0644]
applications/luci-app-shadowsocks-libev/po/pt-br/shadowsocks-libev.po [deleted file]
applications/luci-app-shadowsocks-libev/po/sv/shadowsocks-libev.po [deleted file]
applications/luci-app-shadowsocks-libev/po/templates/shadowsocks-libev.pot [deleted file]
applications/luci-app-shadowsocks-libev/po/zh-cn/shadowsocks-libev.po [deleted file]
applications/luci-app-statistics/luasrc/statistics/rrdtool/definitions/apcups.lua
applications/luci-app-statistics/po/ca/statistics.po
applications/luci-app-statistics/po/cs/statistics.po
applications/luci-app-statistics/po/de/statistics.po
applications/luci-app-statistics/po/el/statistics.po
applications/luci-app-statistics/po/en/statistics.po
applications/luci-app-statistics/po/es/statistics.po
applications/luci-app-statistics/po/fr/statistics.po
applications/luci-app-statistics/po/he/statistics.po
applications/luci-app-statistics/po/hu/statistics.po
applications/luci-app-statistics/po/it/statistics.po
applications/luci-app-statistics/po/ja/statistics.po
applications/luci-app-statistics/po/ms/statistics.po
applications/luci-app-statistics/po/no/statistics.po
applications/luci-app-statistics/po/pl/statistics.po
applications/luci-app-statistics/po/pt-br/statistics.po
applications/luci-app-statistics/po/pt/statistics.po
applications/luci-app-statistics/po/ro/statistics.po
applications/luci-app-statistics/po/ru/statistics.po
applications/luci-app-statistics/po/sk/statistics.po
applications/luci-app-statistics/po/sv/statistics.po
applications/luci-app-statistics/po/templates/statistics.pot
applications/luci-app-statistics/po/tr/statistics.po
applications/luci-app-statistics/po/uk/statistics.po
applications/luci-app-statistics/po/vi/statistics.po
applications/luci-app-statistics/po/zh-cn/statistics.po
applications/luci-app-statistics/po/zh-tw/statistics.po
applications/luci-app-travelmate/luasrc/controller/travelmate.lua
applications/luci-app-travelmate/luasrc/model/cbi/travelmate/overview_tab.lua
applications/luci-app-travelmate/luasrc/model/cbi/travelmate/wifi_add.lua [new file with mode: 0644]
applications/luci-app-travelmate/luasrc/model/cbi/travelmate/wifi_delete.lua [new file with mode: 0644]
applications/luci-app-travelmate/luasrc/model/cbi/travelmate/wifi_edit.lua [new file with mode: 0644]
applications/luci-app-travelmate/luasrc/model/cbi/travelmate/wifi_order.lua [new file with mode: 0644]
applications/luci-app-travelmate/luasrc/view/travelmate/runtime.htm
applications/luci-app-travelmate/luasrc/view/travelmate/stations.htm [new file with mode: 0644]
applications/luci-app-travelmate/luasrc/view/travelmate/wifi_scan.htm [new file with mode: 0644]
applications/luci-app-travelmate/po/ja/travelmate.po
applications/luci-app-travelmate/po/pt-br/travelmate.po
applications/luci-app-travelmate/po/templates/travelmate.pot
applications/luci-app-watchcat/po/sv/watchcat.po
applications/luci-app-wifischedule/po/sv/wifischedule.po
applications/luci-app-wireguard/po/sv/wireguard.po
modules/luci-base/htdocs/luci-static/resources/cbi.js
modules/luci-base/luasrc/sys.lua
modules/luci-base/po/ca/base.po
modules/luci-base/po/cs/base.po
modules/luci-base/po/de/base.po
modules/luci-base/po/el/base.po
modules/luci-base/po/en/base.po
modules/luci-base/po/es/base.po
modules/luci-base/po/fr/base.po
modules/luci-base/po/he/base.po
modules/luci-base/po/hu/base.po
modules/luci-base/po/it/base.po
modules/luci-base/po/ja/base.po
modules/luci-base/po/ko/base.po
modules/luci-base/po/ms/base.po
modules/luci-base/po/no/base.po
modules/luci-base/po/pl/base.po
modules/luci-base/po/pt-br/base.po
modules/luci-base/po/pt/base.po
modules/luci-base/po/ro/base.po
modules/luci-base/po/ru/base.po
modules/luci-base/po/sk/base.po
modules/luci-base/po/sv/base.po
modules/luci-base/po/templates/base.pot
modules/luci-base/po/tr/base.po
modules/luci-base/po/uk/base.po
modules/luci-base/po/vi/base.po
modules/luci-base/po/zh-cn/base.po
modules/luci-base/po/zh-tw/base.po

index a7f41f2..3a12936 100644 (file)
@@ -1,5 +1,5 @@
 msgid ""
-msgstr "Content-Type: text/plain; charset=UTF-8"
+msgstr "Content-Type: text/plain; charset=UTF-8\n"
 
 msgid "\"Falloc\" is not available in all cases."
 msgstr ""
index 589d5f9..37de249 100644 (file)
@@ -1,5 +1,5 @@
 msgid ""
-msgstr "Content-Type: text/plain; charset=UTF-8"
+msgstr "Content-Type: text/plain; charset=UTF-8\n"
 
 msgid "10"
 msgstr "10"
index 8cb1923..a944fdb 100644 (file)
@@ -104,8 +104,8 @@ msgid ""
 "This page allows you to configure custom shell commands which can be easily "
 "invoked from the web interface."
 msgstr ""
-"Den här sidan tillåter dig att ställa in anpassade skalkommandon som lättast kan "
-"åberopas från webbgränssnittet."
+"Den här sidan tillåter dig att ställa in anpassade skalkommandon som lättast "
+"kan åberopas från webbgränssnittet."
 
 msgid "Waiting for command to complete..."
 msgstr "Väntar på att kommandot ska slutföras..."
index 780a2f9..9373fea 100644 (file)
@@ -1,5 +1,5 @@
 msgid ""
-msgstr "Content-Type: text/plain; charset=UTF-8"
+msgstr "Content-Type: text/plain; charset=UTF-8\n"
 
 msgid "&"
 msgstr "&"
@@ -237,8 +237,8 @@ msgid ""
 "GNU Wget will use the IP of given network, cURL will use the physical "
 "interface."
 msgstr ""
-"GNU Wget kommer att använda IP-adressen för det angivna nätverket, cURL kommer att använda det fysiska "
-"gränssnittet."
+"GNU Wget kommer att använda IP-adressen för det angivna nätverket, cURL "
+"kommer att använda det fysiska gränssnittet."
 
 msgid "Global Settings"
 msgstr "Globala inställningar"
@@ -277,7 +277,8 @@ msgid "IPv6-Address"
 msgstr "IPv6-adress"
 
 msgid "If both cURL and GNU Wget are installed, Wget is used by default."
-msgstr "Om både cURL och GNU Wget är installerade så används Wget som standard."
+msgstr ""
+"Om både cURL och GNU Wget är installerade så används Wget som standard."
 
 msgid ""
 "If this service section is disabled it could not be started.<br />Neither "
@@ -319,7 +320,9 @@ msgid ""
 msgstr ""
 
 msgid "It is NOT recommended for casual users to change settings on this page."
-msgstr "Det är INTE rekommenderat för vanliga användare att ändra inställningar på den här sidan."
+msgstr ""
+"Det är INTE rekommenderat för vanliga användare att ändra inställningar på "
+"den här sidan."
 
 msgid "Last Update"
 msgstr "Senaste uppdateringen"
@@ -604,7 +607,9 @@ msgid "cURL without Proxy Support"
 msgstr "cURL utan Proxy-stöd"
 
 msgid "can not detect local IP. Please select a different Source combination"
-msgstr "kan inte upptäcka lokal IP-adress. Vänligen välj en annorlunda Käll-kombination"
+msgstr ""
+"kan inte upptäcka lokal IP-adress. Vänligen välj en annorlunda Käll-"
+"kombination"
 
 msgid "can not resolve host:"
 msgstr "kan inte avgöra värd:"
index b567965..c314332 100644 (file)
@@ -29,5 +29,5 @@ msgid ""
 "With this menu you can configure network diagnostics, such as network device "
 "scans and ping tests."
 msgstr ""
-"Med den här menyn så kan du ställa in nätverksdiagnostik så som igenomsökningar och "
-"ping-tester för nätverksenheten."
+"Med den här menyn så kan du ställa in nätverksdiagnostik så som "
+"igenomsökningar och ping-tester för nätverksenheten."
index 500d1bf..7553504 100644 (file)
@@ -21,7 +21,7 @@ nw.init(m.uci)
 
 local zone = fw:get_zone(arg[1])
 if not zone then
-       luci.http.redirect(dsp.build_url("admin/network/firewall/zones"))
+       luci.http.redirect(ds.build_url("admin/network/firewall/zones"))
        return
 else
        m.title = "%s - %s" %{
index 604a4fa..aeabc63 100644 (file)
@@ -111,7 +111,7 @@ mwan_interface = m5:section(TypedSection, "interface", translate("Interfaces"),
        "Interfaces may not share the same name as configured members, policies or rules"))
        mwan_interface.addremove = true
        mwan_interface.dynamic = false
-       mwan_interface.sectionhead = "Interface"
+       mwan_interface.sectionhead = translate("Interface")
        mwan_interface.sortable = false
        mwan_interface.template = "cbi/tblsection"
        mwan_interface.extedit = dsp.build_url("admin", "network", "mwan", "configuration", "interface", "%s")
index 3bccbd9..efbe8f7 100644 (file)
@@ -13,7 +13,7 @@ mwan_member = m5:section(TypedSection, "member", translate("Members"),
        "Members may not share the same name as configured interfaces, policies or rules"))
        mwan_member.addremove = true
        mwan_member.dynamic = false
-       mwan_member.sectionhead = "Member"
+       mwan_member.sectionhead = translate("Member")
        mwan_member.sortable = true
        mwan_member.template = "cbi/tblsection"
        mwan_member.extedit = ds.build_url("admin", "network", "mwan", "configuration", "member", "%s")
index 08c3f69..6640564 100644 (file)
@@ -42,7 +42,7 @@ mwan_policy = m5:section(TypedSection, "policy", translate("Policies"),
        "Policies may not share the same name as configured interfaces, members or rules"))
        mwan_policy.addremove = true
        mwan_policy.dynamic = false
-       mwan_policy.sectionhead = "Policy"
+       mwan_policy.sectionhead = translate("Policy")
        mwan_policy.sortable = true
        mwan_policy.template = "cbi/tblsection"
        mwan_policy.extedit = ds.build_url("admin", "network", "mwan", "configuration", "policy", "%s")
@@ -65,7 +65,6 @@ use_member = mwan_policy:option(DummyValue, "use_member", translate("Members ass
                else
                        return "&#8212;"
                end
-               
        end
 
 last_resort = mwan_policy:option(DummyValue, "last_resort", translate("Last resort"))
@@ -73,11 +72,11 @@ last_resort = mwan_policy:option(DummyValue, "last_resort", translate("Last reso
        function last_resort.cfgvalue(self, s)
                local action = self.map:get(s, "last_resort")
                if action == "blackhole" then
-                       return "blackhole (drop)"
+                       return translate("blackhole (drop)")
                elseif action == "default" then
-                       return "default (use main routing table)"
+                       return translate("default (use main routing table)")
                else
-                       return "unreachable (reject)"
+                       return translate("unreachable (reject)")
                end
        end
 
index 412f369..0f4c595 100644 (file)
@@ -47,7 +47,7 @@ mwan_rule = m5:section(TypedSection, "rule", translate("Traffic Rules"),
        mwan_rule.addremove = true
        mwan_rule.anonymous = false
        mwan_rule.dynamic = false
-       mwan_rule.sectionhead = "Rule"
+       mwan_rule.sectionhead = translate("Rule")
        mwan_rule.sortable = true
        mwan_rule.template = "cbi/tblsection"
        mwan_rule.extedit = dsp.build_url("admin", "network", "mwan", "configuration", "rule", "%s")
@@ -93,10 +93,10 @@ sticky = mwan_rule:option(DummyValue, "sticky", translate("Sticky"))
        function sticky.cfgvalue(self, s)
                if self.map:get(s, "sticky") == "1" then
                        stickied = 1
-                       return "Yes"
+                       return translate("Yes")
                else
                        stickied = nil
-                       return "No"
+                       return translate("No")
                end
        end
 
@@ -133,7 +133,7 @@ errors = mwan_rule:option(DummyValue, "errors", translate("Errors"))
                if not string.find(error_protocol_list, " " .. s .. " ") then
                        return ""
                else
-                       return "<span title=\"No protocol specified\"><img src=\"/luci-static/resources/cbi/reset.gif\" alt=\"error\"></img></span>"
+                       return "<span title=\"" .. translate("No protocol specified") .. "\"><img src=\"/luci-static/resources/cbi/reset.gif\" alt=\"error\"></img></span>"
                end
        end
 
index 6f350cc..4483485 100644 (file)
@@ -24,7 +24,7 @@
 <script type="text/javascript">//<![CDATA[
        var stxhr = new XHR();
 
-       function update_status(tool, task)
+       function update_status(tool, task, task_name)
        {
                var iface = document.getElementById('mwaniface').value;
                var output = document.getElementById('diag_output');
                        {
                                output.innerHTML =
                                        '<img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="padding: 20px; vertical-align: middle;" /> ' +
-                                       "Waiting for MWAN to " + task + "..."
+                                       String.format("<%:Waiting for MWAN to %s...%>", task_name)
                                ;
                        }
                        else
                        {
                                output.innerHTML =
                                        '<img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="padding: 20px; vertical-align: middle;" /> ' +
-                                       "Waiting for diagnostic results..."
+                                       "<%:Waiting for diagnostic results...%>"
                                ;
                        }
 
@@ -56,7 +56,7 @@
                                }
                                else
                                {
-                                       output.innerHTML = '<pre id="diag_output_css"><strong>No diagnostic results returned</strong></pre>';
+                                       output.innerHTML = '<pre id="diag_output_css"><strong><%:No diagnostic results returned%></strong></pre>';
                                }
                        }
                );
                        <% for z in interfaceNames:gmatch("[^ ]+") do -%><option value="<%=z%>"><%=z%></option><%- end %>
                </select>
                <div id="buttoncss">
-                       <input type="button" value="<%:Ping default gateway%>" class="cbi-button cbi-button-apply" onclick="update_status('ping', 'gateway')" />
-                       <input type="button" value="<%:Ping tracking IP%>" class="cbi-button cbi-button-apply" onclick="update_status('ping', 'track_ip')" />
-                       <input type="button" value="<%:Check IP rules%>" class="cbi-button cbi-button-apply" onclick="update_status('rulechk', null)" />
-                       <input type="button" value="<%:Check routing table%>" class="cbi-button cbi-button-apply" onclick="update_status('routechk', null)" />
-                       <input type="button" value="<%:Hotplug ifup%>" class="cbi-button cbi-button-apply" onclick="update_status('hotplug', 'ifup')" />
-                       <input type="button" value="<%:Hotplug ifdown%>" class="cbi-button cbi-button-apply" onclick="update_status('hotplug', 'ifdown')" />
+                       <input type="button" value="<%:Ping default gateway%>" class="cbi-button cbi-button-apply" onclick="update_status('ping', 'gateway', null)" />
+                       <input type="button" value="<%:Ping tracking IP%>" class="cbi-button cbi-button-apply" onclick="update_status('ping', 'track_ip', null)" />
+                       <input type="button" value="<%:Check IP rules%>" class="cbi-button cbi-button-apply" onclick="update_status('rulechk', null, null)" />
+                       <input type="button" value="<%:Check routing table%>" class="cbi-button cbi-button-apply" onclick="update_status('routechk', null, null)" />
+                       <input type="button" value="<%:Hotplug ifup%>" class="cbi-button cbi-button-apply" onclick="update_status('hotplug', 'ifup', null)" />
+                       <input type="button" value="<%:Hotplug ifdown%>" class="cbi-button cbi-button-apply" onclick="update_status('hotplug', 'ifdown', null)" />
                </div>
        </fieldset>
        <fieldset id="diag_select" class="cbi-section">
                <legend><%:MWAN Service Control%></legend>
                <div id="buttoncss">
-                       <input type="button" value="<%:Restart MWAN%>" class="cbi-button cbi-button-apply" onclick="update_status('service', 'restart')" />
-                       <input type="button" value="<%:Stop MWAN%>" class="cbi-button cbi-button-apply" onclick="update_status('service', 'stop')" />
-                       <input type="button" value="<%:Start MWAN%>" class="cbi-button cbi-button-apply" onclick="update_status('service', 'start')" />
+                       <input type="button" value="<%:Restart MWAN%>" class="cbi-button cbi-button-apply" onclick="update_status('service', 'restart', '<%:restart%>')" />
+                       <input type="button" value="<%:Stop MWAN%>" class="cbi-button cbi-button-apply" onclick="update_status('service', 'stop', '<%:stop%>')" />
+                       <input type="button" value="<%:Start MWAN%>" class="cbi-button cbi-button-apply" onclick="update_status('service', 'start', '<%:start%>')" />
                </div>
        </fieldset>
        <fieldset class="cbi-section" style="display:none">
index 21f516b..4174ef4 100644 (file)
@@ -37,7 +37,7 @@
                        }
                        else
                        {
-                               tshoot.innerHTML = '<strong>Error collecting troubleshooting information</strong>';
+                               tshoot.innerHTML = '<strong><%:Error collecting troubleshooting information%></strong>';
                        }
                }
        );
@@ -46,7 +46,7 @@
 <div id="troubleshoot">
        <fieldset class="cbi-section">
                <legend><%:Troubleshooting Data%></legend>
-               <div id="troubleshoot_text"><img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="vertical-align:middle" /> Collecting data...</div>
+               <div id="troubleshoot_text"><img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="vertical-align:middle" /><%:Collecting data...%></div>
        </fieldset>
 </div>
 
index 5077674..bb18d53 100644 (file)
@@ -8,17 +8,7 @@
 </ul>
 
 <style type="text/css">
-  .container { /* container for entire page. fixes bootstrap theme's ridiculously small page width */
-       max-width: none;
-       margin: 0px 0px 0px 30px;
-       padding-right: 30px;
-       width: auto;
-  }
-  .cbi-section-node {
-       margin-top: 20px;
-  }
   .cbi-section {
-       border: 1px dotted #555555;
        padding: 20px;
   }
 </style>
index 84b1245..7cef063 100644 (file)
                                        switch (mArray.wans[i].status)
                                        {
                                                case 'online':
-                                                       stat = 'Online (tracking active)';
+                                                       stat = '<%:Online (tracking active)%>';
                                                        cssc = 'wanon';
                                                        break;
                                                case 'notMonitored':
-                                                       stat = 'Online (tracking off)';
+                                                       stat = '<%:Online (tracking off)%>';
                                                        cssc = 'wanon';
                                                        break;
                                                case 'offline':
-                                                       stat = 'Offline';
+                                                       stat = '<%:Offline%>';
                                                        cssc = 'wanoff';
                                                        break;
                                                case 'notEnabled':
-                                                       stat = 'Disabled';
+                                                       stat = '<%:Disabled%>';
                                                        cssc = 'wanoff';
                                                        break;
                                        }
@@ -38,7 +38,7 @@
                        }
                        else
                        {
-                               status.innerHTML = '<strong>No MWAN interfaces found</strong>';
+                               status.innerHTML = '<strong><%:No MWAN interfaces found%></strong>';
                        }
                }
        );
@@ -46,7 +46,7 @@
 
 <fieldset id="interface_field" class="cbi-section">
        <legend><%:MWAN Interface Live Status%></legend>
-       <div id="mwan_status_text"><img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="vertical-align:middle" /> Collecting data...</div>
+       <div id="mwan_status_text"><img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="vertical-align:middle" /><%:Collecting data...%></div>
 </fieldset>
 
 <style type="text/css">
index bbb617b..6a800c3 100644 (file)
@@ -17,7 +17,7 @@
                        }
                        else
                        {
-                               status.innerHTML = '<strong>No detailed status information available</strong>';
+                               status.innerHTML = '<strong><%:No detailed status information available%></strong>';
                        }
                }
        );
@@ -26,7 +26,7 @@
 <div id="mwan_detail_status">
        <fieldset class="cbi-section">
                <legend><%:MWAN Detailed Status%></legend>
-               <div id="mwan_detail_text"><img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="vertical-align:middle" /> Collecting data...</div>
+               <div id="mwan_detail_text"><img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="vertical-align:middle" /><%:Collecting data...%></div>
        </fieldset>
 </div>
 
index f4c8b12..2929a6d 100644 (file)
                                        switch (mArray.wans[i].status)
                                        {
                                                case 'online':
-                                                       status = 'Online (tracking active)';
+                                                       status = '<%:Online (tracking active)%>';
                                                        css = 'wanon';
                                                        break;
                                                case 'notMonitored':
-                                                       status = 'Online (tracking off)';
+                                                       status = '<%:Online (tracking off)%>';
                                                        css = 'wanon';
                                                        break;
                                                case 'offline':
-                                                       status = 'Offline';
+                                                       status = '<%:Offline%>';
                                                        css = 'wanoff';
                                                        break;
                                                case 'notEnabled':
-                                                       status = 'Disabled';
+                                                       status = '<%:Disabled%>';
                                                        css = 'wanoff';
                                                        break;
                                        }
                        }
                        else
                        {
-                               statusDiv.innerHTML = '<strong>No MWAN interfaces found</strong>';
+                               statusDiv.innerHTML = '<strong><%:No MWAN interfaces found%></strong>';
                        }
 
                        var logs = document.getElementById('mwan_statuslog_text');
                        if (mArray.mwanlog)
                        {
-                               var mwanLog = 'Last 50 MWAN systemlog entries. Newest entries sorted at the top :';
+                               var mwanLog = '<%:Last 50 MWAN systemlog entries. Newest entries sorted at the top :%>';
                                logs.innerHTML = String.format('<pre>%s<br /><br />%s</pre>', mwanLog, mArray.mwanlog[0]);
                        }
                        else
                        {
-                               logs.innerHTML = '<strong>No MWAN systemlog history found</strong>';
+                               logs.innerHTML = '<strong><%:No MWAN systemlog history found%></strong>';
                        }
                }
        );
 <div id="mwan_interface_status">
        <fieldset id="interface_field" class="cbi-section">
                <legend><%:MWAN Interface Live Status%></legend>
-               <div id="mwan_status_text"><img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="vertical-align:middle" /> Collecting data...</div>
+               <div id="mwan_status_text"><img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="vertical-align:middle" /><%:Collecting data...%></div>
        </fieldset>
        <fieldset class="cbi-section">
                <legend><%:MWAN Interface Systemlog%></legend>
-               <div id="mwan_statuslog_text"><img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="vertical-align:middle" /> Collecting data...</div>
+               <div id="mwan_statuslog_text"><img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="vertical-align:middle" /><%:Collecting data...%></div>
        </fieldset>
 </div>
 
index 834a820..f6a70c3 100644 (file)
@@ -7,7 +7,7 @@ msgstr ""
 "Language-Team: \n"
 "MIME-Version: 1.0\n"
 "Content-Transfer-Encoding: 8bit\n"
-"X-Generator: Poedit 2.0.2\n"
+"X-Generator: Poedit 2.0.3\n"
 "Last-Translator: INAGAKI Hiroshi <musashino.open@gmail.com>\n"
 "Plural-Forms: nplurals=1; plural=0;\n"
 "Language: ja\n"
@@ -46,6 +46,9 @@ msgstr "IP ルールのチェック"
 msgid "Check routing table"
 msgstr "ルーティング テーブルのチェック"
 
+msgid "Collecting data..."
+msgstr "データ収集中です..."
+
 msgid "Configuration"
 msgstr "設定"
 
@@ -73,6 +76,9 @@ msgstr "診断結果"
 msgid "Diagnostics"
 msgstr "診断機能"
 
+msgid "Disabled"
+msgstr "無効"
+
 msgid ""
 "Downed interface will be deemed up after this many successful ping tests"
 msgstr ""
@@ -82,6 +88,9 @@ msgstr ""
 msgid "Enabled"
 msgstr "有効"
 
+msgid "Error collecting troubleshooting information"
+msgstr "トラブルシューティング情報の収集エラー"
+
 msgid "Errors"
 msgstr "エラー"
 
@@ -135,6 +144,9 @@ msgstr "インターフェース"
 msgid "Internet Protocol"
 msgstr "インターネット プロトコル"
 
+msgid "Last 50 MWAN systemlog entries. Newest entries sorted at the top :"
+msgstr "直近の MWAN システムログ(50行)です。一番上が最新の行です:"
+
 msgid "Last resort"
 msgstr "最終手段"
 
@@ -209,6 +221,9 @@ msgstr ""
 "単一または複数のポート(例: \"22\" または \"80,443\")、あるいはポートの範囲"
 "(例: \"1024:2048\")を、クオーテーション無しで指定することができます。"
 
+msgid "Member"
+msgstr "メンバー"
+
 msgid "Member used"
 msgstr "使用されるメンバー"
 
@@ -244,6 +259,30 @@ msgstr "ネットワーク設定"
 msgid "No"
 msgstr "いいえ"
 
+msgid "No MWAN interfaces found"
+msgstr "MWAN インターフェースが見つかりません"
+
+msgid "No MWAN systemlog history found"
+msgstr "MWAN システムログの履歴が見つかりません"
+
+msgid "No detailed status information available"
+msgstr "詳細ステータス情報は利用できません"
+
+msgid "No diagnostic results returned"
+msgstr "診断結果がありません"
+
+msgid "No protocol specified"
+msgstr "プロトコルが設定されていません"
+
+msgid "Offline"
+msgstr "オフライン"
+
+msgid "Online (tracking active)"
+msgstr "オンライン(追跡実行中)"
+
+msgid "Online (tracking off)"
+msgstr "オンライン(追跡オフ)"
+
 msgid "Overview"
 msgstr "概要"
 
@@ -292,6 +331,9 @@ msgstr ""
 "ん。また、15文字以内でなければなりません。<br />ポリシーでは、設定済みのイン"
 "ターフェースやメンバー、ルールと同じ名前を使用することはできません。"
 
+msgid "Policy"
+msgstr "ポリシー"
+
 msgid "Policy assigned"
 msgstr "アサイン済みポリシー"
 
@@ -310,6 +352,9 @@ msgstr "デフォルトのホットプラグ スクリプトの復元"
 msgid "Restore..."
 msgstr "復元..."
 
+msgid "Rule"
+msgstr "ルール"
+
 msgid "Rules"
 msgstr "ルール"
 
@@ -362,6 +407,11 @@ msgid "There are currently %d of 250 supported interfaces configured"
 msgstr "現在、250個中 %d 個のサポートされたインターフェースが設定済みです。"
 
 msgid ""
+"This displays the metric assigned to this interface in /etc/config/network"
+msgstr ""
+"/etc/config/network で、このインターフェースに割り当てられたメトリックです。"
+
+msgid ""
 "This hostname or IP address will be pinged to determine if the link is up or "
 "down. Leave blank to assume interface is always online"
 msgstr ""
@@ -369,11 +419,6 @@ msgstr ""
 "に対して Ping の送信が行われます。常にオンラインとする場合、空欄のままにしま"
 "す。"
 
-msgid ""
-"This displays the metric assigned to this interface in /etc/config/network"
-msgstr ""
-"/etc/config/network で、このインターフェースに割り当てられたメトリックです。"
-
 msgid "This section allows you to modify the contents of /etc/config/mwan3"
 msgstr ""
 "このセクションでは、 /etc/config/mwan3 の内容を変更することができます。"
@@ -406,12 +451,12 @@ msgstr ""
 "wan2, その他)<br />$DEVICE - インターフェースにアタッチされたデバイスの名前"
 "(eth0.1, eth1, その他)"
 
-msgid "Tracking hostname or IP address"
-msgstr "追跡ホスト名または IP アドレス"
-
 msgid "Tracking IP"
 msgstr "追跡 IP"
 
+msgid "Tracking hostname or IP address"
+msgstr "追跡ホスト名または IP アドレス"
+
 msgid "Tracking reliability"
 msgstr "追跡の信頼性"
 
@@ -529,6 +574,12 @@ msgstr ""
 "警告: このルールは不適切なプロトコルが指定されているか、または何も指定されて"
 "いません!プロトコルを指定し直してください!"
 
+msgid "Waiting for MWAN to %s..."
+msgstr "MWAN の %s を待っています..."
+
+msgid "Waiting for diagnostic results..."
+msgstr "診断結果を待っています..."
+
 msgid "Weight"
 msgstr "ウエイト"
 
@@ -562,6 +613,15 @@ msgstr "ifup"
 msgid "never"
 msgstr "never"
 
+msgid "restart"
+msgstr "再起動"
+
+msgid "start"
+msgstr "起動"
+
+msgid "stop"
+msgstr "停止"
+
 msgid "unreachable (reject)"
 msgstr "unreachable (reject)"
 
index 3040d26..3d25e84 100644 (file)
@@ -33,6 +33,9 @@ msgstr ""
 msgid "Check routing table"
 msgstr ""
 
+msgid "Collecting data..."
+msgstr ""
+
 msgid "Configuration"
 msgstr ""
 
@@ -60,6 +63,9 @@ msgstr ""
 msgid "Diagnostics"
 msgstr ""
 
+msgid "Disabled"
+msgstr ""
+
 msgid ""
 "Downed interface will be deemed up after this many successful ping tests"
 msgstr ""
@@ -67,6 +73,9 @@ msgstr ""
 msgid "Enabled"
 msgstr ""
 
+msgid "Error collecting troubleshooting information"
+msgstr ""
+
 msgid "Errors"
 msgstr ""
 
@@ -118,6 +127,9 @@ msgstr ""
 msgid "Internet Protocol"
 msgstr ""
 
+msgid "Last 50 MWAN systemlog entries. Newest entries sorted at the top :"
+msgstr ""
+
 msgid "Last resort"
 msgstr ""
 
@@ -183,6 +195,9 @@ msgid ""
 "as a portrange (eg \"1024:2048\") without quotes"
 msgstr ""
 
+msgid "Member"
+msgstr ""
+
 msgid "Member used"
 msgstr ""
 
@@ -212,6 +227,30 @@ msgstr ""
 msgid "No"
 msgstr ""
 
+msgid "No MWAN interfaces found"
+msgstr ""
+
+msgid "No MWAN systemlog history found"
+msgstr ""
+
+msgid "No detailed status information available"
+msgstr ""
+
+msgid "No diagnostic results returned"
+msgstr ""
+
+msgid "No protocol specified"
+msgstr ""
+
+msgid "Offline"
+msgstr ""
+
+msgid "Online (tracking active)"
+msgstr ""
+
+msgid "Online (tracking off)"
+msgstr ""
+
 msgid "Overview"
 msgstr ""
 
@@ -252,6 +291,9 @@ msgid ""
 "configured interfaces, members or rules"
 msgstr ""
 
+msgid "Policy"
+msgstr ""
+
 msgid "Policy assigned"
 msgstr ""
 
@@ -270,6 +312,9 @@ msgstr ""
 msgid "Restore..."
 msgstr ""
 
+msgid "Rule"
+msgstr ""
+
 msgid "Rules"
 msgstr ""
 
@@ -312,12 +357,12 @@ msgid "There are currently %d of 250 supported interfaces configured"
 msgstr ""
 
 msgid ""
-"This hostname or IP address will be pinged to determine if the link is up or "
-"down. Leave blank to assume interface is always online"
+"This displays the metric assigned to this interface in /etc/config/network"
 msgstr ""
 
 msgid ""
-"This displays the metric assigned to this interface in /etc/config/network"
+"This hostname or IP address will be pinged to determine if the link is up or "
+"down. Leave blank to assume interface is always online"
 msgstr ""
 
 msgid "This section allows you to modify the contents of /etc/config/mwan3"
@@ -340,10 +385,10 @@ msgid ""
 "device name attached to the interface (eth0.1, eth1, etc.)"
 msgstr ""
 
-msgid "Tracking hostname or IP address"
+msgid "Tracking IP"
 msgstr ""
 
-msgid "Tracking IP"
+msgid "Tracking hostname or IP address"
 msgstr ""
 
 msgid "Tracking reliability"
@@ -432,6 +477,12 @@ msgid ""
 "specified! Please configure a specific protocol!"
 msgstr ""
 
+msgid "Waiting for MWAN to %s..."
+msgstr ""
+
+msgid "Waiting for diagnostic results..."
+msgstr ""
+
 msgid "Weight"
 msgstr ""
 
@@ -463,5 +514,14 @@ msgstr ""
 msgid "never"
 msgstr ""
 
+msgid "restart"
+msgstr ""
+
+msgid "start"
+msgstr ""
+
+msgid "stop"
+msgstr ""
+
 msgid "unreachable (reject)"
 msgstr ""
index d1066c5..1e0f34f 100644 (file)
@@ -1,17 +1,7 @@
 msgid ""
 msgstr ""
 "Content-Type: text/plain; charset=UTF-8\n"
-"Project-Id-Version: \n"
-"POT-Creation-Date: \n"
-"PO-Revision-Date: \n"
-"Language-Team: \n"
 "Last-Translator: Hsing-Wang Liao <kuoruan@gmail.com>\n"
-"MIME-Version: 1.0\n"
-"Content-Transfer-Encoding: 8bit\n"
-"X-Generator: Poedit 2.0\n"
-"Last-Translator: \n"
-"Plural-Forms: nplurals=1; plural=0;\n"
-"Language: zh_CN\n"
 
 msgid "%d hour"
 msgstr "%d 小时"
@@ -32,20 +22,23 @@ msgid ""
 "Acceptable values: 1-100. This many Tracking IP addresses must respond for "
 "the link to be deemed up"
 msgstr ""
-"接受的值: 1-100。这个设置项指定了当多少个IP地址能够连通时接口会被认为在线"
+"取值范围: 1-100。这个设置项指定了当多少个 IP 地址能够连通时接口会被认为在线"
 
 msgid "Acceptable values: 1-1000. Defaults to 1 if not set"
-msgstr "接受的值: 1-100。如果不填写,默认值为 1"
+msgstr "取值范围: 1-100。如果不填写,默认值为 1"
 
 msgid "Advanced"
 msgstr "高级"
 
 msgid "Check IP rules"
-msgstr "检查IP规则"
+msgstr "检查 IP 规则"
 
 msgid "Check routing table"
 msgstr "检查路由表"
 
+msgid "Collecting data..."
+msgstr "正在收集数据..."
+
 msgid "Configuration"
 msgstr "配置"
 
@@ -73,6 +66,9 @@ msgstr "诊断结果"
 msgid "Diagnostics"
 msgstr "诊断"
 
+msgid "Disabled"
+msgstr "禁用"
+
 msgid ""
 "Downed interface will be deemed up after this many successful ping tests"
 msgstr "当 Ping 成功次数达到这个数值后,已经被认为离线的接口将会重新上线"
@@ -80,6 +76,9 @@ msgstr "当 Ping 成功次数达到这个数值后,已经被认为离线的接
 msgid "Enabled"
 msgstr "启用"
 
+msgid "Error collecting troubleshooting information"
+msgstr "收集故障排除信息时出错"
+
 msgid "Errors"
 msgstr "错误"
 
@@ -131,6 +130,9 @@ msgstr "接口"
 msgid "Internet Protocol"
 msgstr "互联网协议"
 
+msgid "Last 50 MWAN systemlog entries. Newest entries sorted at the top :"
+msgstr "最近 50 条 MWAN 系统日志,最新条目排在顶部:"
+
 msgid "Last resort"
 msgstr "备用成员"
 
@@ -192,15 +194,19 @@ msgid ""
 msgstr ""
 "MWAN 支持最多 250 个物理或逻辑接口。<br />MWAN 要求所有接口必须在 /etc/"
 "config/network 中设定唯一的网关跃点。<br />名称必须与 /etc/config/network 中"
-"的接口名称匹配。(可查看“高级”选项卡)<br />名称允许包括A-Z、a-z、0-9、_ 但是不"
-"能有空格。<br />接口不应该与成员、策略、规则中的任意一个设置项使用相同的名称"
+"的接口名称匹配。(可查看“高级”选项卡)<br />名称允许包括A-Z、a-z、0-9、_ 但是"
+"不能有空格。<br />接口不应该与成员、策略、规则中的任意一个设置项使用相同的名"
+"称"
 
 msgid ""
 "May be entered as a single or multiple port(s) (eg \"22\" or \"80,443\") or "
 "as a portrange (eg \"1024:2048\") without quotes"
 msgstr ""
-"可以输入一个或多个端口 (例如 \"22\" 或者 \"80,443\") 或者是一个端口范围 (例"
-"如 \"1024:2048\") 不含引号"
+"可以输入一个或多个端口(例如 \"22\" 或者 \"80,443\")或者是一个端口范围(例"
+"如 \"1024:2048\")不含引号"
+
+msgid "Member"
+msgstr "成员"
 
 msgid "Member used"
 msgstr "使用的成员"
@@ -213,7 +219,7 @@ msgid ""
 ">Names may contain characters A-Z, a-z, 0-9, _ and no spaces<br />Members "
 "may not share the same name as configured interfaces, policies or rules"
 msgstr ""
-"“成员”用来设置每一个 MWAN 接口的跃点数 (即接口优先级) 和所占比重。<br />名称"
+"“成员”用来设置每一个 MWAN 接口的跃点数(即接口优先级)和所占比重。<br />名称"
 "允许包括 A-Z、 a-、0-9、_ 但是不能有空格。<br />成员不应该与接口、策略、规则"
 "中的任意一个设置项使用相同的名称"
 
@@ -236,6 +242,30 @@ msgstr "网络配置文件"
 msgid "No"
 msgstr "否"
 
+msgid "No MWAN interfaces found"
+msgstr "没有找到 MWAN 接口"
+
+msgid "No MWAN systemlog history found"
+msgstr "没有在系统日志中找到 MWAN 历史信息"
+
+msgid "No detailed status information available"
+msgstr "没有状态详细信息可用"
+
+msgid "No diagnostic results returned"
+msgstr "没有返回诊断结果"
+
+msgid "No protocol specified"
+msgstr "未指定协议"
+
+msgid "Offline"
+msgstr "离线"
+
+msgid "Online (tracking active)"
+msgstr "在线(追踪启用中)"
+
+msgid "Online (tracking off)"
+msgstr "在线(追踪已关闭)"
+
 msgid "Overview"
 msgstr "概况"
 
@@ -281,6 +311,9 @@ msgstr ""
 "包括A-Z、a-z、0-9、_ 但是不能有空格。名称应该在 15 个字符以内<br />策略不应该"
 "与接口、成员、规则中的任意一个设置项使用相同的名称"
 
+msgid "Policy"
+msgstr "策略"
+
 msgid "Policy assigned"
 msgstr "分配的策略"
 
@@ -299,6 +332,9 @@ msgstr "恢复默认的 hotplug 脚本"
 msgid "Restore..."
 msgstr "恢复..."
 
+msgid "Rule"
+msgstr "规则"
+
 msgid "Rules"
 msgstr "规则"
 
@@ -342,20 +378,20 @@ msgid "Stop MWAN"
 msgstr "停止 MWAN"
 
 msgid "Supports CIDR notation (eg \"192.168.100.0/24\") without quotes"
-msgstr "支持 CIDR 记法 (例如: \"192.168.100.0/24\") 不含引号"
+msgstr "支持 CIDR 记法(例如: \"192.168.100.0/24\")不含引号"
 
 msgid "There are currently %d of 250 supported interfaces configured"
-msgstr ""
-
-msgid ""
-"This hostname or IP address will be pinged to determine if the link is up or "
-"down. Leave blank to assume interface is always online"
-msgstr ""
+msgstr "当前已配置 %d 个接口,最大支持 250 个"
 
 msgid ""
 "This displays the metric assigned to this interface in /etc/config/network"
 msgstr "这里显示了这个接口在 /etc/config/network 中配置的跃点数"
 
+msgid ""
+"This hostname or IP address will be pinged to determine if the link is up or "
+"down. Leave blank to assume interface is always online"
+msgstr "通过 ping 此主机或 IP 地址来确定链路是否在线。留空则认为接口始终在线"
+
 msgid "This section allows you to modify the contents of /etc/config/mwan3"
 msgstr "这里允许你修改 /etc/config/mwan3 的内容"
 
@@ -375,21 +411,21 @@ msgid ""
 "$INTERFACE is the interface name (wan1, wan2, etc.)<br />$DEVICE is the "
 "device name attached to the interface (eth0.1, eth1, etc.)"
 msgstr ""
-"这里允许你修改/etc/hotplug.d/iface/16-mwancustom 的内容<br />这可以在接口 "
+"这里允许你修改 /etc/hotplug.d/iface/16-mwancustom 的内容<br />这可以在接口 "
 "ifup 或 ifdown Hotplug 事件时运行系统命令或脚本<br /><br />注意:<br />脚本的"
 "第一行必须是 &#34;#!/bin/sh&#34; 不含引号<br />以#开头的行是注释,不会执行"
-"<br /><br />可用变量:<br />$ACTION 是 Hotplug 事件 (ifup, ifdown)<br />"
-"$INTERFACE 是接口名称 (wan1、wan2 等)<br />$DEVICE 是连接到接口的设备名称 "
-"(eth0.1、eth1 等)"
-
-msgid "Tracking hostname or IP address"
-msgstr ""
+"<br /><br />可用变量:<br />$ACTION 是 Hotplug 事件(ifup, ifdown)<br />"
+"$INTERFACE 是接口名称(wan1、wan2 等)<br />$DEVICE 是连接到接口的设备名称 "
+"(eth0.1、eth1 等)"
 
 msgid "Tracking IP"
-msgstr "跟踪的 IP"
+msgstr "追踪的 IP"
+
+msgid "Tracking hostname or IP address"
+msgstr "追踪的主机或 IP 地址"
 
 msgid "Tracking reliability"
-msgstr "è·\9f踪可靠性"
+msgstr "追踪可靠性"
 
 msgid "Traffic Rules"
 msgstr "流量规则"
@@ -411,70 +447,77 @@ msgid "View the contents of /etc/protocols for protocol descriptions"
 msgstr "请查看 /etc/protocols 获取可选协议详情"
 
 msgid "WARNING: %d interfaces are configured exceeding the maximum of 250!"
-msgstr ""
+msgstr "警告: 已配置 %d 个接口,超过最大值 250!"
 
 msgid ""
 "WARNING: Some policies have names exceeding the maximum of 15 characters!"
-msgstr ""
+msgstr "警告: 某些策略的名称超过了 15 个字符!"
 
 msgid ""
 "WARNING: some interfaces are configured incorrectly or not at all in /etc/"
 "config/network!"
-msgstr ""
+msgstr "警告: 某些接口配置不正确或未配置到 /etc/config/network!"
 
 msgid ""
 "WARNING: some interfaces have a higher reliability requirement than there "
 "are tracking IP addresses!"
-msgstr ""
+msgstr "警告: 某些接口的追踪可靠性要求大于了追踪 IP 地址总数!"
 
 msgid ""
 "WARNING: some interfaces have duplicate metrics configured in /etc/config/"
 "network!"
-msgstr ""
+msgstr "警告: 某些接口在 /etc/config/network 中配置了相同的跃点数!"
 
 msgid ""
 "WARNING: some interfaces have no default route in the main routing table!"
-msgstr ""
+msgstr "警告: 某些接口在主路由表中没有默认路由!"
 
 msgid ""
 "WARNING: some interfaces have no metric configured in /etc/config/network!"
-msgstr ""
+msgstr "警告: 某些接口没有在 /etc/config/network 中配置跃点数!"
 
 msgid ""
 "WARNING: some rules have a port configured with no or improper protocol "
 "specified! Please configure a specific protocol!"
 msgstr ""
+"警告: 某些规则指定了端口却没有配置或配置了不正确的协议,请重新指定协议!"
 
 msgid ""
 "WARNING: this and other interfaces have duplicate metrics configured in /etc/"
 "config/network!"
-msgstr ""
+msgstr "警告: 此接口和其他接口在 /etc/config/network 中配置了相同的跃点数!"
 
 msgid ""
 "WARNING: this interface has a higher reliability requirement than there are "
 "tracking IP addresses!"
-msgstr ""
+msgstr "警告: 此接口的追踪可靠性要求大于了追踪 IP 地址总数!"
 
 msgid "WARNING: this interface has no default route in the main routing table!"
-msgstr ""
+msgstr "警告: 此接口在主路由表中没有默认路由!"
 
 msgid ""
 "WARNING: this interface has no metric configured in /etc/config/network!"
-msgstr ""
+msgstr "警告: 此接口没有在 /etc/config/network 中配置跃点数!"
 
 msgid ""
 "WARNING: this interface is configured incorrectly or not at all in /etc/"
 "config/network!"
-msgstr ""
+msgstr "警告: 此接口配置不正确或未配置到 /etc/config/network!"
 
 msgid ""
 "WARNING: this policy's name is %d characters exceeding the maximum of 15!"
-msgstr ""
+msgstr "警告: 此策略的名称具有 %d 个字符,超过了 15 个字符!"
 
 msgid ""
 "WARNING: this rule is incorrectly configured with no or improper protocol "
 "specified! Please configure a specific protocol!"
-msgstr ""
+msgstr "警告: 此规则没有配置或配置了不正确的协议,请重新指定协议!"
+
+msgid "Waiting for MWAN to %s..."
+msgstr "等待 MWAN %s..."
+
+msgid "Waiting for diagnostic results..."
+msgstr "等待诊断结果..."
 
 msgid "Weight"
 msgstr "比重"
@@ -493,10 +536,10 @@ msgid "always"
 msgstr "总是"
 
 msgid "blackhole (drop)"
-msgstr "黑洞 (丢弃)"
+msgstr "黑洞(丢弃)"
 
 msgid "default (use main routing table)"
-msgstr "默认 (使用主路由表)"
+msgstr "默认(使用主路由表)"
 
 msgid "ifdown"
 msgstr "ifdown"
@@ -507,12 +550,14 @@ msgstr "ifup"
 msgid "never"
 msgstr "从不"
 
+msgid "restart"
+msgstr ""
+
+msgid "start"
+msgstr ""
+
+msgid "stop"
+msgstr ""
+
 msgid "unreachable (reject)"
-msgstr "不可达 (拒绝)"
-
-#~ msgid ""
-#~ "This IP address will be pinged to dermine if the link is up or down. "
-#~ "Leave blank to assume interface is always online"
-#~ msgstr ""
-#~ "MWAN 将会通过 Ping 这些 IP 地址来确定接口是否上线。如果留空,则 MWAN 认为"
-#~ "该接口永远在线"
+msgstr "不可达(拒绝)"
diff --git a/applications/luci-app-nlbwmon/Makefile b/applications/luci-app-nlbwmon/Makefile
new file mode 100644 (file)
index 0000000..a00177f
--- /dev/null
@@ -0,0 +1,8 @@
+include $(TOPDIR)/rules.mk
+
+LUCI_TITLE:=Netlink based bandwidth accounting
+LUCI_DEPENDS:=+nlbwmon
+
+include ../../luci.mk
+
+# call BuildPackage - OpenWrt buildroot signature
diff --git a/applications/luci-app-nlbwmon/htdocs/luci-static/resources/nlbw.chart.min.js b/applications/luci-app-nlbwmon/htdocs/luci-static/resources/nlbw.chart.min.js
new file mode 100644 (file)
index 0000000..34e3026
--- /dev/null
@@ -0,0 +1,68 @@
+(function(){var p=this,l=p.Chart,e=function(a){this.canvas=a.canvas;this.ctx=a;var b=function(a,b){return a["offset"+b]?a["offset"+b]:document.defaultView.getComputedStyle(a).getPropertyValue(b)};this.width=b(a.canvas,"Width")||a.canvas.width;this.height=b(a.canvas,"Height")||a.canvas.height;this.width=a.canvas.width;this.height=a.canvas.height;this.aspectRatio=this.width/this.height;d.retinaScale(this);return this};e.defaults={global:{animation:!0,animationSteps:60,animationEasing:"easeOutQuart",
+showScale:!0,scaleOverride:!1,scaleSteps:null,scaleStepWidth:null,scaleStartValue:null,scaleLineColor:"rgba(0,0,0,.1)",scaleLineWidth:1,scaleShowLabels:!0,scaleLabel:"<%=value%>",scaleIntegersOnly:!0,scaleBeginAtZero:!1,scaleFontFamily:"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",scaleFontSize:12,scaleFontStyle:"normal",scaleFontColor:"#666",responsive:!1,maintainAspectRatio:!0,showTooltips:!0,customTooltips:!1,tooltipEvents:["mousemove","touchstart","touchmove","mouseout"],tooltipFillColor:"rgba(0,0,0,0.8)",
+tooltipFontFamily:"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",tooltipFontSize:14,tooltipFontStyle:"normal",tooltipFontColor:"#fff",tooltipTitleFontFamily:"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",tooltipTitleFontSize:14,tooltipTitleFontStyle:"bold",tooltipTitleFontColor:"#fff",tooltipTitleTemplate:"<%= label%>",tooltipYPadding:6,tooltipXPadding:6,tooltipCaretSize:8,tooltipCornerRadius:6,tooltipXOffset:10,tooltipTemplate:"<%if (label){%><%=label%>: <%}%><%= value %>",multiTooltipTemplate:"<%= value %>",
+multiTooltipKeyBackground:"#fff",segmentColorDefault:"#A6CEE3 #1F78B4 #B2DF8A #33A02C #FB9A99 #E31A1C #FDBF6F #FF7F00 #CAB2D6 #6A3D9A #B4B482 #B15928".split(" "),segmentHighlightColorDefaults:"#CEF6FF #47A0DC #DAFFB2 #5BC854 #FFC2C1 #FF4244 #FFE797 #FFA728 #F2DAFE #9265C2 #DCDCAA #D98150".split(" "),onAnimationProgress:function(){},onAnimationComplete:function(){}}};e.types={};var d=e.helpers={},k=d.each=function(a,b,c){var f=Array.prototype.slice.call(arguments,3);if(a)if(a.length===+a.length){var d;
+for(d=0;d<a.length;d++)b.apply(c,[a[d],d].concat(f))}else for(d in a)b.apply(c,[a[d],d].concat(f))},h=d.clone=function(a){var b={};k(a,function(c,f){a.hasOwnProperty(f)&&(b[f]=c)});return b},r=d.extend=function(a){k(Array.prototype.slice.call(arguments,1),function(b){k(b,function(c,f){b.hasOwnProperty(f)&&(a[f]=c)})});return a},I=d.merge=function(a,b){var c=Array.prototype.slice.call(arguments,0);c.unshift({});return r.apply(null,c)},J=d.indexOf=function(a,b){if(Array.prototype.indexOf)return a.indexOf(b);
+for(var c=0;c<a.length;c++)if(a[c]===b)return c;return-1};d.where=function(a,b){var c=[];d.each(a,function(a){b(a)&&c.push(a)});return c};d.findNextWhere=function(a,b,c){c||(c=-1);for(c+=1;c<a.length;c++){var f=a[c];if(b(f))return f}};d.findPreviousWhere=function(a,b,c){c||(c=a.length);for(--c;0<=c;c--){var f=a[c];if(b(f))return f}};var D=d.inherits=function(a){var b=this,c=a&&a.hasOwnProperty("constructor")?a.constructor:function(){return b.apply(this,arguments)},f=function(){this.constructor=c};
+f.prototype=b.prototype;c.prototype=new f;c.extend=D;a&&r(c.prototype,a);c.__super__=b.prototype;return c},A=d.noop=function(){},K=d.uid=function(){var a=0;return function(){return"chart-"+a++}}(),L=d.warn=function(a){window.console&&"function"===typeof window.console.warn&&console.warn(a)},M=d.amd="function"===typeof define&&define.amd,u=d.isNumber=function(a){return!isNaN(parseFloat(a))&&isFinite(a)},y=d.max=function(a){return Math.max.apply(Math,a)},w=d.min=function(a){return Math.min.apply(Math,
+a)};d.cap=function(a,b,c){if(u(b)){if(a>b)return b}else if(u(c)&&a<c)return c;return a};var E=d.getDecimalPlaces=function(a){if(0!==a%1&&u(a)){a=a.toString();if(0>a.indexOf("e-"))return a.split(".")[1].length;if(0>a.indexOf("."))return parseInt(a.split("e-")[1]);a=a.split(".")[1].split("e-");return a[0].length+parseInt(a[1])}return 0},B=d.radians=function(a){return Math.PI/180*a};d.getAngleFromPoint=function(a,b){var c=b.x-a.x,f=b.y-a.y,d=Math.sqrt(c*c+f*f),m=2*Math.PI+Math.atan2(f,c);0>c&&0>f&&(m+=
+2*Math.PI);return{angle:m,distance:d}};var F=d.aliasPixel=function(a){return 0===a%2?0:.5};d.splineCurve=function(a,b,c,f){var d=Math.sqrt(Math.pow(b.x-a.x,2)+Math.pow(b.y-a.y,2)),m=Math.sqrt(Math.pow(c.x-b.x,2)+Math.pow(c.y-b.y,2)),g=f*d/(d+m);f=f*m/(d+m);return{inner:{x:b.x-g*(c.x-a.x),y:b.y-g*(c.y-a.y)},outer:{x:b.x+f*(c.x-a.x),y:b.y+f*(c.y-a.y)}}};var N=d.calculateOrderOfMagnitude=function(a){return Math.floor(Math.log(a)/Math.LN10)};d.calculateScaleRange=function(a,b,c,f,d){b=Math.floor(b/(1.5*
+c));c=2>=b;var m=[];k(a,function(a){null==a||m.push(a)});var g=w(m),e=y(m);e===g&&(e+=.5,.5<=g&&!f?g-=.5:e+=.5);a=N(Math.abs(e-g));f=f?0:Math.floor(g/(1*Math.pow(10,a)))*Math.pow(10,a);for(var e=Math.ceil(e/(1*Math.pow(10,a)))*Math.pow(10,a)-f,g=Math.pow(10,a),n=Math.round(e/g);(n>b||2*n<b)&&!c;)if(n>b)g*=2,n=Math.round(e/g),0!==n%1&&(c=!0);else if(d&&0<=a)if(0===g/2%1)g/=2,n=Math.round(e/g);else break;else g/=2,n=Math.round(e/g);c&&(n=2,g=e/n);return{steps:n,stepValue:g,min:f,max:f+n*g}};var t=d.template=
+function(a,b){if(a instanceof Function)return a(b);var c={},c=/\W/.test(a)?new Function("obj","var p=[],print=function(){p.push.apply(p,arguments);};with(obj){p.push('"+a.replace(/[\r\t\n]/g," ").split("<%").join("\t").replace(/((^|%>)[^\t]*)'/g,"$1\r").replace(/\t=(.*?)%>/g,"',$1,'").split("\t").join("');").split("%>").join("p.push('").split("\r").join("\\'")+"');}return p.join('');"):c[a]=c[a];return b?c(b):c};d.generateLabels=function(a,b,c,f){var d=Array(b);a&&k(d,function(b,e){d[e]=t(a,{value:c+
+f*(e+1)})});return d};var x=d.easingEffects={linear:function(a){return a},easeInQuad:function(a){return a*a},easeOutQuad:function(a){return-1*a*(a-2)},easeInOutQuad:function(a){return 1>(a/=.5)?.5*a*a:-.5*(--a*(a-2)-1)},easeInCubic:function(a){return a*a*a},easeOutCubic:function(a){return 1*((a=a/1-1)*a*a+1)},easeInOutCubic:function(a){return 1>(a/=.5)?.5*a*a*a:.5*((a-=2)*a*a+2)},easeInQuart:function(a){return a*a*a*a},easeOutQuart:function(a){return-1*((a=a/1-1)*a*a*a-1)},easeInOutQuart:function(a){return 1>
+(a/=.5)?.5*a*a*a*a:-.5*((a-=2)*a*a*a-2)},easeInQuint:function(a){return 1*(a/=1)*a*a*a*a},easeOutQuint:function(a){return 1*((a=a/1-1)*a*a*a*a+1)},easeInOutQuint:function(a){return 1>(a/=.5)?.5*a*a*a*a*a:.5*((a-=2)*a*a*a*a+2)},easeInSine:function(a){return-1*Math.cos(a/1*(Math.PI/2))+1},easeOutSine:function(a){return 1*Math.sin(a/1*(Math.PI/2))},easeInOutSine:function(a){return-.5*(Math.cos(Math.PI*a/1)-1)},easeInExpo:function(a){return 0===a?1:1*Math.pow(2,10*(a/1-1))},easeOutExpo:function(a){return 1===
+a?1:1*(-Math.pow(2,-10*a/1)+1)},easeInOutExpo:function(a){return 0===a?0:1===a?1:1>(a/=.5)?.5*Math.pow(2,10*(a-1)):.5*(-Math.pow(2,-10*--a)+2)},easeInCirc:function(a){return 1<=a?a:-1*(Math.sqrt(1-(a/=1)*a)-1)},easeOutCirc:function(a){return 1*Math.sqrt(1-(a=a/1-1)*a)},easeInOutCirc:function(a){return 1>(a/=.5)?-.5*(Math.sqrt(1-a*a)-1):.5*(Math.sqrt(1-(a-=2)*a)+1)},easeInElastic:function(a){var b=1.70158,c=0,f=1;if(0===a)return 0;if(1==(a/=1))return 1;c||(c=.3);f<Math.abs(1)?(f=1,b=c/4):b=c/(2*Math.PI)*
+Math.asin(1/f);return-(f*Math.pow(2,10*--a)*Math.sin(2*(1*a-b)*Math.PI/c))},easeOutElastic:function(a){var b=1.70158,c=0,f=1;if(0===a)return 0;if(1==(a/=1))return 1;c||(c=.3);f<Math.abs(1)?(f=1,b=c/4):b=c/(2*Math.PI)*Math.asin(1/f);return f*Math.pow(2,-10*a)*Math.sin(2*(1*a-b)*Math.PI/c)+1},easeInOutElastic:function(a){var b=1.70158,c=0,f=1;if(0===a)return 0;if(2==(a/=.5))return 1;c||(c=.3*1.5);f<Math.abs(1)?(f=1,b=c/4):b=c/(2*Math.PI)*Math.asin(1/f);return 1>a?-.5*f*Math.pow(2,10*--a)*Math.sin(2*
+(1*a-b)*Math.PI/c):f*Math.pow(2,-10*--a)*Math.sin(2*(1*a-b)*Math.PI/c)*.5+1},easeInBack:function(a){return 1*(a/=1)*a*(2.70158*a-1.70158)},easeOutBack:function(a){return 1*((a=a/1-1)*a*(2.70158*a+1.70158)+1)},easeInOutBack:function(a){var b=1.70158;return 1>(a/=.5)?.5*a*a*(((b*=1.525)+1)*a-b):.5*((a-=2)*a*(((b*=1.525)+1)*a+b)+2)},easeInBounce:function(a){return 1-x.easeOutBounce(1-a)},easeOutBounce:function(a){return(a/=1)<1/2.75?7.5625*a*a:a<2/2.75?1*(7.5625*(a-=1.5/2.75)*a+.75):a<2.5/2.75?1*(7.5625*
+(a-=2.25/2.75)*a+.9375):1*(7.5625*(a-=2.625/2.75)*a+.984375)},easeInOutBounce:function(a){return.5>a?.5*x.easeInBounce(2*a):.5*x.easeOutBounce(2*a-1)+.5}},G=d.requestAnimFrame=function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(a){return window.setTimeout(a,1E3/60)}}();d.cancelAnimFrame=function(){return window.cancelAnimationFrame||window.webkitCancelAnimationFrame||
+window.mozCancelAnimationFrame||window.oCancelAnimationFrame||window.msCancelAnimationFrame||function(a){return window.clearTimeout(a,1E3/60)}}();d.animationLoop=function(a,b,c,f,d,e){var g=0,k=x[c]||x.linear,n=function(){g++;var c=g/b,h=k(c);a.call(e,h,c,g);f.call(e,h,c);g<b?e.animationFrame=G(n):d.apply(e)};G(n)};d.getRelativePosition=function(a){var b;b=a.originalEvent||a;var c=(a.currentTarget||a.srcElement).getBoundingClientRect();b.touches?(a=b.touches[0].clientX-c.left,b=b.touches[0].clientY-
+c.top):(a=b.clientX-c.left,b=b.clientY-c.top);return{x:a,y:b}};var O=d.addEvent=function(a,b,c){a.addEventListener?a.addEventListener(b,c):a.attachEvent?a.attachEvent("on"+b,c):a["on"+b]=c},P=d.removeEvent=function(a,b,c){a.removeEventListener?a.removeEventListener(b,c,!1):a.detachEvent?a.detachEvent("on"+b,c):a["on"+b]=A};d.bindEvents=function(a,b,c){a.events||(a.events={});k(b,function(b){a.events[b]=function(){c.apply(a,arguments)};O(a.chart.canvas,b,a.events[b])})};var Q=d.unbindEvents=function(a,
+b){k(b,function(b,f){P(a.chart.canvas,f,b)})},R=d.getMaximumWidth=function(a){a=a.parentNode;var b=parseInt(z(a,"padding-left"))+parseInt(z(a,"padding-right"));return a?a.clientWidth-b:0},S=d.getMaximumHeight=function(a){a=a.parentNode;var b=parseInt(z(a,"padding-bottom"))+parseInt(z(a,"padding-top"));return a?a.clientHeight-b:0},z=d.getStyle=function(a,b){return a.currentStyle?a.currentStyle[b]:document.defaultView.getComputedStyle(a,null).getPropertyValue(b)};d.getMaximumSize=d.getMaximumWidth;
+var T=d.retinaScale=function(a){var b=a.ctx,c=a.canvas.width;a=a.canvas.height;window.devicePixelRatio&&(b.canvas.style.width=c+"px",b.canvas.style.height=a+"px",b.canvas.height=a*window.devicePixelRatio,b.canvas.width=c*window.devicePixelRatio,b.scale(window.devicePixelRatio,window.devicePixelRatio))},U=d.clear=function(a){a.ctx.clearRect(0,0,a.width,a.height)},v=d.fontString=function(a,b,c){return b+" "+a+"px "+c},C=d.longestText=function(a,b,c){a.font=b;var f=0;k(c,function(b){b=a.measureText(b).width;
+f=b>f?b:f});return f},H=d.drawRoundedRectangle=function(a,b,c,f,d,e){a.beginPath();a.moveTo(b+e,c);a.lineTo(b+f-e,c);a.quadraticCurveTo(b+f,c,b+f,c+e);a.lineTo(b+f,c+d-e);a.quadraticCurveTo(b+f,c+d,b+f-e,c+d);a.lineTo(b+e,c+d);a.quadraticCurveTo(b,c+d,b,c+d-e);a.lineTo(b,c+e);a.quadraticCurveTo(b,c,b+e,c);a.closePath()};e.instances={};e.Type=function(a,b,c){this.options=b;this.chart=c;this.id=K();e.instances[this.id]=this;b.responsive&&this.resize();this.initialize.call(this,a)};r(e.Type.prototype,
+{initialize:function(){return this},clear:function(){U(this.chart);return this},stop:function(){e.animationService.cancelAnimation(this);return this},resize:function(a){this.stop();var b=this.chart.canvas,c=R(this.chart.canvas),f=this.options.maintainAspectRatio?c/this.chart.aspectRatio:S(this.chart.canvas);b.width=this.chart.width=c;b.height=this.chart.height=f;T(this.chart);"function"===typeof a&&a.apply(this,Array.prototype.slice.call(arguments,1));return this},reflow:A,render:function(a){a&&this.reflow();
+this.options.animation&&!a?(a=new e.Animation,a.numSteps=this.options.animationSteps,a.easing=this.options.animationEasing,a.render=function(a,c){var f=c.currentStep/c.numSteps,e=(0,d.easingEffects[c.easing])(f);a.draw(e,f,c.currentStep)},a.onAnimationProgress=this.options.onAnimationProgress,a.onAnimationComplete=this.options.onAnimationComplete,e.animationService.addAnimation(this,a)):(this.draw(),this.options.onAnimationComplete.call(this));return this},generateLegend:function(){return t(this.options.legendTemplate,
+this)},destroy:function(){this.clear();Q(this,this.events);var a=this.chart.canvas;a.width=this.chart.width;a.height=this.chart.height;a.style.removeProperty?(a.style.removeProperty("width"),a.style.removeProperty("height")):(a.style.removeAttribute("width"),a.style.removeAttribute("height"));delete e.instances[this.id]},showTooltip:function(a,b){"undefined"===typeof this.activeElements&&(this.activeElements=[]);if(function(a){var b=!1;if(a.length!==this.activeElements.length)return b=!0;k(a,function(a,
+c){a!==this.activeElements[c]&&(b=!0)},this);return b}.call(this,a)||b){this.activeElements=a;this.draw();this.options.customTooltips&&this.options.customTooltips(!1);if(0<a.length)if(this.datasets&&1<this.datasets.length){for(var c,f,q=this.datasets.length-1;0<=q&&(c=this.datasets[q].points||this.datasets[q].bars||this.datasets[q].segments,f=J(c,a[0]),-1===f);q--);var m=[],g=[];c=function(a){var b=[],c,e=[],q=[],k,h,l;d.each(this.datasets,function(a){c=a.points||a.bars||a.segments;c[f]&&c[f].hasValue()&&
+b.push(c[f])});d.each(b,function(a){e.push(a.x);q.push(a.y);m.push(d.template(this.options.multiTooltipTemplate,a));g.push({fill:a._saved.fillColor||a.fillColor,stroke:a._saved.strokeColor||a.strokeColor})},this);l=w(q);k=y(q);h=w(e);a=y(e);return{x:h>this.chart.width/2?h:a,y:(l+k)/2}}.call(this,f);(new e.MultiTooltip({x:c.x,y:c.y,xPadding:this.options.tooltipXPadding,yPadding:this.options.tooltipYPadding,xOffset:this.options.tooltipXOffset,fillColor:this.options.tooltipFillColor,textColor:this.options.tooltipFontColor,
+fontFamily:this.options.tooltipFontFamily,fontStyle:this.options.tooltipFontStyle,fontSize:this.options.tooltipFontSize,titleTextColor:this.options.tooltipTitleFontColor,titleFontFamily:this.options.tooltipTitleFontFamily,titleFontStyle:this.options.tooltipTitleFontStyle,titleFontSize:this.options.tooltipTitleFontSize,cornerRadius:this.options.tooltipCornerRadius,labels:m,legendColors:g,legendColorBackground:this.options.multiTooltipKeyBackground,title:t(this.options.tooltipTitleTemplate,a[0]),chart:this.chart,
+ctx:this.chart.ctx,custom:this.options.customTooltips})).draw()}else k(a,function(a){var b=a.tooltipPosition();(new e.Tooltip({x:Math.round(b.x),y:Math.round(b.y),xPadding:this.options.tooltipXPadding,yPadding:this.options.tooltipYPadding,fillColor:this.options.tooltipFillColor,textColor:this.options.tooltipFontColor,fontFamily:this.options.tooltipFontFamily,fontStyle:this.options.tooltipFontStyle,fontSize:this.options.tooltipFontSize,caretHeight:this.options.tooltipCaretSize,cornerRadius:this.options.tooltipCornerRadius,
+text:t(this.options.tooltipTemplate,a),chart:this.chart,custom:this.options.customTooltips})).draw()},this);return this}},toBase64Image:function(){return this.chart.canvas.toDataURL.apply(this.chart.canvas,arguments)}});e.Type.extend=function(a){var b=this,c=function(){return b.apply(this,arguments)};c.prototype=h(b.prototype);r(c.prototype,a);c.extend=e.Type.extend;if(a.name||b.prototype.name){var f=a.name||b.prototype.name,d=e.defaults[b.prototype.name]?h(e.defaults[b.prototype.name]):{};e.defaults[f]=
+r(d,a.defaults);e.types[f]=c;e.prototype[f]=function(a,b){var d=I(e.defaults.global,e.defaults[f],b||{});return new c(a,d,this)}}else L("Name not provided for this chart, so it hasn't been registered");return b};e.Element=function(a){r(this,a);this.initialize.apply(this,arguments);this.save()};r(e.Element.prototype,{initialize:function(){},restore:function(a){a?k(a,function(a){this[a]=this._saved[a]},this):r(this,this._saved);return this},save:function(){this._saved=h(this);delete this._saved._saved;
+return this},update:function(a){k(a,function(a,c){this._saved[c]=this[c];this[c]=a},this);return this},transition:function(a,b){k(a,function(a,f){this[f]=(a-this._saved[f])*b+this._saved[f]},this);return this},tooltipPosition:function(){return{x:this.x,y:this.y}},hasValue:function(){return u(this.value)}});e.Element.extend=D;e.Point=e.Element.extend({display:!0,inRange:function(a,b){return Math.pow(a-this.x,2)+Math.pow(b-this.y,2)<Math.pow(this.hitDetectionRadius+this.radius,2)},draw:function(){if(this.display){var a=
+this.ctx;a.beginPath();a.arc(this.x,this.y,this.radius,0,2*Math.PI);a.closePath();a.strokeStyle=this.strokeColor;a.lineWidth=this.strokeWidth;a.fillStyle=this.fillColor;a.fill();a.stroke()}}});e.Arc=e.Element.extend({inRange:function(a,b){var c=d.getAngleFromPoint(this,{x:a,y:b}),f=c.angle%(2*Math.PI),e=(2*Math.PI+this.startAngle)%(2*Math.PI),m=(2*Math.PI+this.endAngle)%(2*Math.PI)||360,c=c.distance>=this.innerRadius&&c.distance<=this.outerRadius;return(m<e?f<=m||f>=e:f>=e&&f<=m)&&c},tooltipPosition:function(){var a=
+this.startAngle+(this.endAngle-this.startAngle)/2,b=(this.outerRadius-this.innerRadius)/2+this.innerRadius;return{x:this.x+Math.cos(a)*b,y:this.y+Math.sin(a)*b}},draw:function(a){a=this.ctx;a.beginPath();a.arc(this.x,this.y,0>this.outerRadius?0:this.outerRadius,this.startAngle,this.endAngle);a.arc(this.x,this.y,0>this.innerRadius?0:this.innerRadius,this.endAngle,this.startAngle,!0);a.closePath();a.strokeStyle=this.strokeColor;a.lineWidth=this.strokeWidth;a.fillStyle=this.fillColor;a.fill();a.lineJoin=
+"bevel";this.showStroke&&a.stroke()}});e.Rectangle=e.Element.extend({draw:function(){var a=this.ctx,b=this.width/2,c=this.x-b,b=this.x+b,f=this.base-(this.base-this.y),d=this.strokeWidth/2;this.showStroke&&(c+=d,b-=d,f+=d);a.beginPath();a.fillStyle=this.fillColor;a.strokeStyle=this.strokeColor;a.lineWidth=this.strokeWidth;a.moveTo(c,this.base);a.lineTo(c,f);a.lineTo(b,f);a.lineTo(b,this.base);a.fill();this.showStroke&&a.stroke()},height:function(){return this.base-this.y},inRange:function(a,b){return a>=
+this.x-this.width/2&&a<=this.x+this.width/2&&b>=this.y&&b<=this.base}});e.Animation=e.Element.extend({currentStep:null,numSteps:60,easing:"",render:null,onAnimationProgress:null,onAnimationComplete:null});e.Tooltip=e.Element.extend({draw:function(){var a=this.chart.ctx;a.font=v(this.fontSize,this.fontStyle,this.fontFamily);this.xAlign="center";this.yAlign="above";var b=this.caretPadding=2,c=a.measureText(this.text).width+2*this.xPadding,f=this.fontSize+2*this.yPadding,d=f+this.caretHeight+b;this.x+
+c/2>this.chart.width?this.xAlign="left":0>this.x-c/2&&(this.xAlign="right");0>this.y-d&&(this.yAlign="below");var e=this.x-c/2,d=this.y-d;a.fillStyle=this.fillColor;if(this.custom)this.custom(this);else{switch(this.yAlign){case "above":a.beginPath();a.moveTo(this.x,this.y-b);a.lineTo(this.x+this.caretHeight,this.y-(b+this.caretHeight));a.lineTo(this.x-this.caretHeight,this.y-(b+this.caretHeight));a.closePath();a.fill();break;case "below":d=this.y+b+this.caretHeight,a.beginPath(),a.moveTo(this.x,this.y+
+b),a.lineTo(this.x+this.caretHeight,this.y+b+this.caretHeight),a.lineTo(this.x-this.caretHeight,this.y+b+this.caretHeight),a.closePath(),a.fill()}switch(this.xAlign){case "left":e=this.x-c+(this.cornerRadius+this.caretHeight);break;case "right":e=this.x-(this.cornerRadius+this.caretHeight)}H(a,e,d,c,f,this.cornerRadius);a.fill();a.fillStyle=this.textColor;a.textAlign="center";a.textBaseline="middle";a.fillText(this.text,e+c/2,d+f/2)}}});e.MultiTooltip=e.Element.extend({initialize:function(){this.font=
+v(this.fontSize,this.fontStyle,this.fontFamily);this.titleFont=v(this.titleFontSize,this.titleFontStyle,this.titleFontFamily);this.titleHeight=this.title?1.5*this.titleFontSize:0;this.height=this.labels.length*this.fontSize+this.fontSize/2*(this.labels.length-1)+2*this.yPadding+this.titleHeight;this.ctx.font=this.titleFont;var a=this.ctx.measureText(this.title).width,b=C(this.ctx,this.font,this.labels)+this.fontSize+3;this.width=y([b,a])+2*this.xPadding;a=this.height/2;0>this.y-a?this.y=a:this.y+
+a>this.chart.height&&(this.y=this.chart.height-a);this.x=this.x>this.chart.width/2?this.x-(this.xOffset+this.width):this.x+this.xOffset},getLineHeight:function(a){var b=this.y-this.height/2+this.yPadding;return 0===a?b+this.titleHeight/3:b+(1.5*this.fontSize*(a-1)+this.fontSize/2)+this.titleHeight},draw:function(){if(this.custom)this.custom(this);else{H(this.ctx,this.x,this.y-this.height/2,this.width,this.height,this.cornerRadius);var a=this.ctx;a.fillStyle=this.fillColor;a.fill();a.closePath();a.textAlign=
+"left";a.textBaseline="middle";a.fillStyle=this.titleTextColor;a.font=this.titleFont;a.fillText(this.title,this.x+this.xPadding,this.getLineHeight(0));a.font=this.font;d.each(this.labels,function(b,c){a.fillStyle=this.textColor;a.fillText(b,this.x+this.xPadding+this.fontSize+3,this.getLineHeight(c+1));a.fillStyle=this.legendColorBackground;a.fillRect(this.x+this.xPadding,this.getLineHeight(c+1)-this.fontSize/2,this.fontSize,this.fontSize);a.fillStyle=this.legendColors[c].fill;a.fillRect(this.x+this.xPadding,
+this.getLineHeight(c+1)-this.fontSize/2,this.fontSize,this.fontSize)},this)}}});e.Scale=e.Element.extend({initialize:function(){this.fit()},buildYLabels:function(){this.yLabels=[];for(var a=E(this.stepValue),b=0;b<=this.steps;b++)this.yLabels.push(t(this.templateString,{value:(this.min+b*this.stepValue).toFixed(a)}));this.yLabelWidth=this.display&&this.showLabels?C(this.ctx,this.font,this.yLabels)+10:0},addXLabel:function(a){this.xLabels.push(a);this.valuesCount++;this.fit()},removeXLabel:function(){this.xLabels.shift();
+this.valuesCount--;this.fit()},fit:function(){this.startPoint=this.display?this.fontSize:0;this.endPoint=this.display?this.height-1.5*this.fontSize-5:this.height;this.startPoint+=this.padding;var a=this.endPoint-=this.padding,b=this.endPoint-this.startPoint,c;this.calculateYRange(b);this.buildYLabels();for(this.calculateXLabelRotation();b>this.endPoint-this.startPoint;)b=this.endPoint-this.startPoint,c=this.yLabelWidth,this.calculateYRange(b),this.buildYLabels(),c<this.yLabelWidth&&(this.endPoint=
+a,this.calculateXLabelRotation())},calculateXLabelRotation:function(){this.ctx.font=this.font;var a=this.ctx.measureText(this.xLabels[0]).width,b;this.xScalePaddingRight=this.ctx.measureText(this.xLabels[this.xLabels.length-1]).width/2+3;this.xScalePaddingLeft=a/2>this.yLabelWidth?a/2:this.yLabelWidth;this.xLabelRotation=0;if(this.display){var c=C(this.ctx,this.font,this.xLabels),f;this.xLabelWidth=c;for(var d=Math.floor(this.calculateX(1)-this.calculateX(0))-6;this.xLabelWidth>d&&0===this.xLabelRotation||
+this.xLabelWidth>d&&90>=this.xLabelRotation&&0<this.xLabelRotation;)f=Math.cos(B(this.xLabelRotation)),b=f*a,b+this.fontSize/2>this.yLabelWidth&&(this.xScalePaddingLeft=b+this.fontSize/2),this.xScalePaddingRight=this.fontSize/2,this.xLabelRotation++,this.xLabelWidth=f*c;0<this.xLabelRotation&&(this.endPoint-=Math.sin(B(this.xLabelRotation))*c+3)}else this.xLabelWidth=0,this.xScalePaddingLeft=this.xScalePaddingRight=this.padding},calculateYRange:A,drawingArea:function(){return this.startPoint-this.endPoint},
+calculateY:function(a){var b=this.drawingArea()/(this.min-this.max);return this.endPoint-b*(a-this.min)},calculateX:function(a){var b=(this.width-(this.xScalePaddingLeft+this.xScalePaddingRight))/Math.max(this.valuesCount-(this.offsetGridLines?0:1),1);a=b*a+this.xScalePaddingLeft;this.offsetGridLines&&(a+=b/2);return Math.round(a)},update:function(a){d.extend(this,a);this.fit()},draw:function(){var a=this.ctx,b=(this.endPoint-this.startPoint)/this.steps,c=Math.round(this.xScalePaddingLeft);this.display&&
+(a.fillStyle=this.textColor,a.font=this.font,k(this.yLabels,function(f,e){var k=this.endPoint-b*e,g=Math.round(k),h=this.showHorizontalLines;a.textAlign="right";a.textBaseline="middle";this.showLabels&&a.fillText(f,c-10,k);0!==e||h||(h=!0);h&&a.beginPath();0<e?(a.lineWidth=this.gridLineWidth,a.strokeStyle=this.gridLineColor):(a.lineWidth=this.lineWidth,a.strokeStyle=this.lineColor);g+=d.aliasPixel(a.lineWidth);h&&(a.moveTo(c,g),a.lineTo(this.width,g),a.stroke(),a.closePath());a.lineWidth=this.lineWidth;
+a.strokeStyle=this.lineColor;a.beginPath();a.moveTo(c-5,g);a.lineTo(c,g);a.stroke();a.closePath()},this),k(this.xLabels,function(b,c){var d=this.calculateX(c)+F(this.lineWidth),e=this.calculateX(c-(this.offsetGridLines?.5:0))+F(this.lineWidth),k=0<this.xLabelRotation,h=this.showVerticalLines;0!==c||h||(h=!0);h&&a.beginPath();0<c?(a.lineWidth=this.gridLineWidth,a.strokeStyle=this.gridLineColor):(a.lineWidth=this.lineWidth,a.strokeStyle=this.lineColor);h&&(a.moveTo(e,this.endPoint),a.lineTo(e,this.startPoint-
+3),a.stroke(),a.closePath());a.lineWidth=this.lineWidth;a.strokeStyle=this.lineColor;a.beginPath();a.moveTo(e,this.endPoint);a.lineTo(e,this.endPoint+5);a.stroke();a.closePath();a.save();a.translate(d,k?this.endPoint+12:this.endPoint+8);a.rotate(-1*B(this.xLabelRotation));a.font=this.font;a.textAlign=k?"right":"center";a.textBaseline=k?"middle":"top";a.fillText(b,0,0);a.restore()},this))}});e.RadialScale=e.Element.extend({initialize:function(){this.size=w([this.height,this.width]);this.drawingArea=
+this.display?this.size/2-(this.fontSize/2+this.backdropPaddingY):this.size/2},calculateCenterOffset:function(a){return this.drawingArea/(this.max-this.min)*(a-this.min)},update:function(){this.lineArc?this.drawingArea=this.display?this.size/2-(this.fontSize/2+this.backdropPaddingY):this.size/2:this.setScaleSize();this.buildYLabels()},buildYLabels:function(){this.yLabels=[];for(var a=E(this.stepValue),b=0;b<=this.steps;b++)this.yLabels.push(t(this.templateString,{value:(this.min+b*this.stepValue).toFixed(a)}))},
+getCircumference:function(){return 2*Math.PI/this.valuesCount},setScaleSize:function(){var a=w([this.height/2-this.pointLabelFontSize-5,this.width/2]),b,c,d,e=this.width,k,g=0,h;this.ctx.font=v(this.pointLabelFontSize,this.pointLabelFontStyle,this.pointLabelFontFamily);for(c=0;c<this.valuesCount;c++)b=this.getPointPosition(c,a),d=this.ctx.measureText(t(this.templateString,{value:this.labels[c]})).width+5,0===c||c===this.valuesCount/2?(d/=2,b.x+d>e&&(e=b.x+d,k=c),b.x-d<g&&(g=b.x-d,h=c)):c<this.valuesCount/
+2?b.x+d>e&&(e=b.x+d,k=c):c>this.valuesCount/2&&b.x-d<g&&(g=b.x-d,h=c);b=g;e=Math.ceil(e-this.width);k=this.getIndexAngle(k);h=this.getIndexAngle(h);k=e/Math.sin(k+Math.PI/2);h=b/Math.sin(h+Math.PI/2);k=u(k)?k:0;h=u(h)?h:0;this.drawingArea=a-(h+k)/2;this.setCenterPoint(h,k)},setCenterPoint:function(a,b){this.xCenter=(a+this.drawingArea+(this.width-b-this.drawingArea))/2;this.yCenter=this.height/2},getIndexAngle:function(a){return 2*Math.PI/this.valuesCount*a-Math.PI/2},getPointPosition:function(a,
+b){var c=this.getIndexAngle(a);return{x:Math.cos(c)*b+this.xCenter,y:Math.sin(c)*b+this.yCenter}},draw:function(){if(this.display){var a=this.ctx;k(this.yLabels,function(b,c){if(0<c){var d=this.drawingArea/this.steps*c,e=this.yCenter-d;if(0<this.lineWidth){a.strokeStyle=this.lineColor;a.lineWidth=this.lineWidth;if(this.lineArc)a.beginPath(),a.arc(this.xCenter,this.yCenter,d,0,2*Math.PI);else{a.beginPath();for(var f=0;f<this.valuesCount;f++)d=this.getPointPosition(f,this.calculateCenterOffset(this.min+
+c*this.stepValue)),0===f?a.moveTo(d.x,d.y):a.lineTo(d.x,d.y)}a.closePath();a.stroke()}this.showLabels&&(a.font=v(this.fontSize,this.fontStyle,this.fontFamily),this.showLabelBackdrop&&(d=a.measureText(b).width,a.fillStyle=this.backdropColor,a.fillRect(this.xCenter-d/2-this.backdropPaddingX,e-this.fontSize/2-this.backdropPaddingY,d+2*this.backdropPaddingX,this.fontSize+2*this.backdropPaddingY)),a.textAlign="center",a.textBaseline="middle",a.fillStyle=this.fontColor,a.fillText(b,this.xCenter,e))}},this);
+if(!this.lineArc){a.lineWidth=this.angleLineWidth;a.strokeStyle=this.angleLineColor;for(var b=this.valuesCount-1;0<=b;b--){var c=null,d=null;0<this.angleLineWidth&&(c=this.calculateCenterOffset(this.max),d=this.getPointPosition(b,c),a.beginPath(),a.moveTo(this.xCenter,this.yCenter),a.lineTo(d.x,d.y),a.stroke(),a.closePath());if(this.backgroundColors&&this.backgroundColors.length==this.valuesCount){null==c&&(c=this.calculateCenterOffset(this.max));null==d&&(d=this.getPointPosition(b,c));var e=this.getPointPosition(0===
+b?this.valuesCount-1:b-1,c),h=this.getPointPosition(b===this.valuesCount-1?0:b+1,c),c=(e.x+d.x)/2,e=(e.y+d.y)/2,g=(d.x+h.x)/2,h=(d.y+h.y)/2;a.beginPath();a.moveTo(this.xCenter,this.yCenter);a.lineTo(c,e);a.lineTo(d.x,d.y);a.lineTo(g,h);a.fillStyle=this.backgroundColors[b];a.fill();a.closePath()}d=this.getPointPosition(b,this.calculateCenterOffset(this.max)+5);a.font=v(this.pointLabelFontSize,this.pointLabelFontStyle,this.pointLabelFontFamily);a.fillStyle=this.pointLabelFontColor;e=this.labels.length;
+c=this.labels.length/2;g=c/2;h=b<g||b>e-g;e=b===g||b===e-g;a.textAlign=0===b?"center":b===c?"center":b<c?"left":"right";a.textBaseline=e?"middle":h?"bottom":"top";a.fillText(this.labels[b],d.x,d.y)}}}}});e.animationService={frameDuration:17,animations:[],dropFrames:0,addAnimation:function(a,b){for(var c=0;c<this.animations.length;++c)if(this.animations[c].chartInstance===a){this.animations[c].animationObject=b;return}this.animations.push({chartInstance:a,animationObject:b});1==this.animations.length&&
+d.requestAnimFrame.call(window,this.digestWrapper)},cancelAnimation:function(a){var b=d.findNextWhere(this.animations,function(b){return b.chartInstance===a});b&&this.animations.splice(b,1)},digestWrapper:function(){e.animationService.startDigest.call(e.animationService)},startDigest:function(){var a=Date.now(),b=0;1<this.dropFrames&&(b=Math.floor(this.dropFrames),this.dropFrames-=b);for(var c=0;c<this.animations.length;c++)null===this.animations[c].animationObject.currentStep&&(this.animations[c].animationObject.currentStep=
+0),this.animations[c].animationObject.currentStep+=1+b,this.animations[c].animationObject.currentStep>this.animations[c].animationObject.numSteps&&(this.animations[c].animationObject.currentStep=this.animations[c].animationObject.numSteps),this.animations[c].animationObject.render(this.animations[c].chartInstance,this.animations[c].animationObject),this.animations[c].animationObject.currentStep==this.animations[c].animationObject.numSteps&&(this.animations[c].animationObject.onAnimationComplete.call(this.animations[c].chartInstance),
+this.animations.splice(c,1),c--);a=(Date.now()-a-this.frameDuration)/this.frameDuration;1<a&&(this.dropFrames+=a);0<this.animations.length&&d.requestAnimFrame.call(window,this.digestWrapper)}};d.addEvent(window,"resize",function(){var a;return function(){clearTimeout(a);a=setTimeout(function(){k(e.instances,function(a){a.options.responsive&&a.resize(a.render,!0)})},50)}}());M?define(function(){return e}):"object"===typeof module&&module.exports&&(module.exports=e);p.Chart=e;e.noConflict=function(){p.Chart=
+l;return e}}).call(this);
+(function(){var p=this.Chart,l=p.helpers,e={segmentShowStroke:!0,segmentStrokeColor:"#fff",segmentStrokeWidth:2,percentageInnerCutout:50,animationSteps:100,animationEasing:"easeOutBounce",animateRotate:!0,animateScale:!1,legendTemplate:'<ul class="<%=name.toLowerCase()%>-legend"><% for (var i=0; i<segments.length; i++){%><li><span style="background-color:<%=segments[i].fillColor%>"><%if(segments[i].label){%><%=segments[i].label%><%}%></span></li><%}%></ul>'};p.Type.extend({name:"Doughnut",defaults:e,
+initialize:function(d){this.segments=[];this.outerRadius=(l.min([this.chart.width,this.chart.height])-this.options.segmentStrokeWidth/2)/2;this.SegmentArc=p.Arc.extend({ctx:this.chart.ctx,x:this.chart.width/2,y:this.chart.height/2});this.options.showTooltips&&l.bindEvents(this,this.options.tooltipEvents,function(d){d="mouseout"!==d.type?this.getSegmentsAtEvent(d):[];l.each(this.segments,function(d){d.restore(["fillColor"])});l.each(d,function(d){d.fillColor=d.highlightColor});this.showTooltip(d)});
+this.calculateTotal(d);l.each(d,function(e,h){e.color||(e.color="hsl("+360*h/d.length+", 100%, 50%)");this.addData(e,h,!0)},this);this.render()},getSegmentsAtEvent:function(d){var e=[],h=l.getRelativePosition(d);l.each(this.segments,function(d){d.inRange(h.x,h.y)&&e.push(d)},this);return e},addData:function(d,e,h){e=void 0!==e?e:this.segments.length;"undefined"===typeof d.color&&(d.color=p.defaults.global.segmentColorDefault[e%p.defaults.global.segmentColorDefault.length],d.highlight=p.defaults.global.segmentHighlightColorDefaults[e%
+p.defaults.global.segmentHighlightColorDefaults.length]);this.segments.splice(e,0,new this.SegmentArc({value:d.value,outerRadius:this.options.animateScale?0:this.outerRadius,innerRadius:this.options.animateScale?0:this.outerRadius/100*this.options.percentageInnerCutout,fillColor:d.color,highlightColor:d.highlight||d.color,showStroke:this.options.segmentShowStroke,strokeWidth:this.options.segmentStrokeWidth,strokeColor:this.options.segmentStrokeColor,startAngle:1.5*Math.PI,circumference:this.options.animateRotate?
+0:this.calculateCircumference(d.value),label:d.label}));h||(this.reflow(),this.update())},calculateCircumference:function(d){return 0<this.total?d/this.total*Math.PI*2:0},calculateTotal:function(d){this.total=0;l.each(d,function(d){this.total+=Math.abs(d.value)},this)},update:function(){this.calculateTotal(this.segments);l.each(this.activeElements,function(d){d.restore(["fillColor"])});l.each(this.segments,function(d){d.save()});this.render()},removeData:function(d){d=l.isNumber(d)?d:this.segments.length-
+1;this.segments.splice(d,1);this.reflow();this.update()},reflow:function(){l.extend(this.SegmentArc.prototype,{x:this.chart.width/2,y:this.chart.height/2});this.outerRadius=(l.min([this.chart.width,this.chart.height])-this.options.segmentStrokeWidth/2)/2;l.each(this.segments,function(d){d.update({outerRadius:this.outerRadius,innerRadius:this.outerRadius/100*this.options.percentageInnerCutout})},this)},draw:function(d){var e=d?d:1;this.clear();l.each(this.segments,function(d,l){d.transition({circumference:this.calculateCircumference(d.value),
+outerRadius:this.outerRadius,innerRadius:this.outerRadius/100*this.options.percentageInnerCutout},e);d.endAngle=d.startAngle+d.circumference;d.draw();0===l&&(d.startAngle=1.5*Math.PI);l<this.segments.length-1&&(this.segments[l+1].startAngle=d.endAngle)},this)}});p.types.Doughnut.extend({name:"Pie",defaults:l.merge(e,{percentageInnerCutout:0})})}).call(this);
diff --git a/applications/luci-app-nlbwmon/luasrc/controller/nlbw.lua b/applications/luci-app-nlbwmon/luasrc/controller/nlbw.lua
new file mode 100644 (file)
index 0000000..bb56bc6
--- /dev/null
@@ -0,0 +1,225 @@
+-- Copyright 2017 Jo-Philipp Wich <jo@mein.io>
+-- Licensed to the public under the Apache License 2.0.
+
+module("luci.controller.nlbw", package.seeall)
+
+function index()
+       entry({"admin", "nlbw"}, firstchild(), _("Bandwidth Monitor"), 80)
+       entry({"admin", "nlbw", "display"}, template("nlbw/display"), _("Display"), 1)
+       entry({"admin", "nlbw", "config"}, cbi("nlbw/config"), _("Configuration"), 2)
+       entry({"admin", "nlbw", "backup"}, template("nlbw/backup"), _("Backup"), 3)
+       entry({"admin", "nlbw", "data"}, call("action_data"), nil, 4)
+       entry({"admin", "nlbw", "list"}, call("action_list"), nil, 5)
+       entry({"admin", "nlbw", "ptr"}, call("action_ptr"), nil, 6).leaf = true
+       entry({"admin", "nlbw", "download"}, call("action_download"), nil, 7)
+       entry({"admin", "nlbw", "restore"}, post("action_restore"), nil, 8)
+       entry({"admin", "nlbw", "commit"}, call("action_commit"), nil, 9)
+end
+
+local function exec(cmd, args, writer)
+       local os = require "os"
+       local nixio = require "nixio"
+
+       local fdi, fdo = nixio.pipe()
+       local pid = nixio.fork()
+
+       if pid > 0 then
+               fdo:close()
+
+               while true do
+                       local buffer = fdi:read(2048)
+                       local wpid, stat, code = nixio.waitpid(pid, "nohang")
+
+                       if writer and buffer and #buffer > 0 then
+                               writer(buffer)
+                       end
+
+                       if wpid and stat == "exited" then
+                               break
+                       end
+               end
+       elseif pid == 0 then
+               nixio.dup(fdo, nixio.stdout)
+               fdi:close()
+               fdo:close()
+               nixio.exece(cmd, args, nil)
+               nixio.stdout:close()
+               os.exit(1)
+       end
+end
+
+function action_data()
+       local http = require "luci.http"
+
+       local types = {
+               csv = "text/csv",
+               json = "application/json"
+       }
+
+       local args = { }
+       local mtype = http.formvalue("type") or "json"
+       local delim = http.formvalue("delim") or ";"
+       local period = http.formvalue("period")
+       local group_by = http.formvalue("group_by")
+       local order_by = http.formvalue("order_by")
+
+       if types[mtype] then
+               args[#args+1] = "-c"
+               args[#args+1] = mtype
+       else
+               http.status(400, "Unsupported type")
+               return
+       end
+
+       if delim and #delim > 0 then
+               args[#args+1] = "-s%s" % delim
+       end
+
+       if period and #period > 0 then
+               args[#args+1] = "-t"
+               args[#args+1] = period
+       end
+
+       if group_by and #group_by > 0 then
+               args[#args+1] = "-g"
+               args[#args+1] = group_by
+       end
+
+       if order_by and #order_by > 0 then
+               args[#args+1] = "-o"
+               args[#args+1] = order_by
+       end
+
+       http.prepare_content(types[mtype])
+       exec("/usr/sbin/nlbw", args, http.write)
+end
+
+function action_list()
+       local http = require "luci.http"
+
+       local fd = io.popen("/usr/sbin/nlbw -c list")
+       local periods = { }
+
+       if fd then
+               while true do
+                       local period = fd:read("*l")
+
+                       if not period then
+                               break
+                       end
+
+                       periods[#periods+1] = period
+               end
+
+               fd:close()
+       end
+
+       http.prepare_content("application/json")
+       http.write_json(periods)
+end
+
+function action_ptr(...)
+       local http = require "luci.http"
+       local util = require "luci.util"
+
+       http.prepare_content("application/json")
+       http.write_json(util.ubus("network.rrdns", "lookup", {
+               addrs = {...}, timeout = 3000
+       }))
+end
+
+function action_download()
+       local nixio = require "nixio"
+       local http = require "luci.http"
+       local sys = require "luci.sys"
+       local uci = require "luci.model.uci".cursor()
+
+       local dir = uci:get_first("nlbwmon", "nlbwmon", "database_directory")
+               or "/var/lib/nlbwmon"
+
+       if dir and nixio.fs.stat(dir, "type") == "dir" then
+               local n = "nlbwmon-backup-%s-%s.tar.gz"
+                       %{ sys.hostname(), os.date("%Y-%m-%d") }
+
+               http.prepare_content("application/octet-stream")
+               http.header("Content-Disposition", "attachment; filename=\"%s\"" % n)
+               exec("/bin/tar", { "-C", dir, "-c", "-z", ".", "-f", "-" }, http.write)
+       else
+               http.status(500, "Unable to find database directory")
+       end
+end
+
+function action_restore()
+       local nixio = require "nixio"
+       local http = require "luci.http"
+       local i18n = require "luci.i18n"
+       local tpl = require "luci.template"
+       local uci = require "luci.model.uci".cursor()
+
+       local tmp = "/tmp/nlbw-restore.tar.gz"
+       local dir = uci:get_first("nlbwmon", "nlbwmon", "database_directory")
+               or "/var/lib/nlbwmon"
+
+       local fp
+       http.setfilehandler(
+               function(meta, chunk, eof)
+                       if not fp and meta and meta.name == "archive" then
+                               fp = io.open(tmp, "w")
+                       end
+                       if fp and chunk then
+                               fp:write(chunk)
+                       end
+                       if fp and eof then
+                               fp:close()
+                       end
+               end)
+
+       local files = { }
+       local tar = io.popen("/bin/tar -tzf %s" % tmp, "r")
+       if tar then
+               while true do
+                       local file = tar:read("*l")
+                       if not file then
+                               break
+                       elseif file:match("^%d%d%d%d%d%d%d%d%.db%.gz$") or
+                              file:match("^%./%d%d%d%d%d%d%d%d%.db%.gz$") then
+                               files[#files+1] = file
+                       end
+               end
+               tar:close()
+       end
+
+       if #files == 0 then
+               http.status(500, "Internal Server Error")
+               tpl.render("nlbw/backup", {
+                       message = i18n.translate("Invalid or empty backup archive")
+               })
+               return
+       end
+
+
+       local output = { }
+
+       exec("/etc/init.d/nlbwmon", { "stop" })
+       exec("/bin/mkdir", { "-p", dir })
+
+       exec("/bin/tar", { "-C", dir, "-vxzf", tmp, unpack(files) },
+               function(chunk) output[#output+1] = chunk:match("%S+") end)
+
+       exec("/bin/rm", { "-f", tmp })
+       exec("/etc/init.d/nlbwmon", { "start" })
+
+       tpl.render("nlbw/backup", {
+               message = i18n.translatef(
+                       "The following database files have been restored: %s",
+                       table.concat(output, ", "))
+       })
+end
+
+function action_commit()
+       local http = require "luci.http"
+       local disp = require "luci.dispatcher"
+
+       http.redirect(disp.build_url("admin/nlbw/display"))
+       exec("/usr/sbin/nlbw", { "-c", "commit" })
+end
diff --git a/applications/luci-app-nlbwmon/luasrc/model/cbi/nlbw/config.lua b/applications/luci-app-nlbwmon/luasrc/model/cbi/nlbw/config.lua
new file mode 100644 (file)
index 0000000..71e096c
--- /dev/null
@@ -0,0 +1,215 @@
+-- Copyright 2017 Jo-Philipp Wich <jo@mein.io>
+-- Licensed to the public under the Apache License 2.0.
+
+local utl = require "luci.util"
+local sys = require "luci.sys"
+local fs  = require "nixio.fs"
+local ip  = require "luci.ip"
+local nw  = require "luci.model.network"
+
+local s, m, period, warning, date, days, interval, ifaces, subnets, limit, prealloc, compress, generations, commit, refresh, directory, protocols
+
+m = Map("nlbwmon", translate("Netlink Bandwidth Monitor - Configuration"),
+       translate("The Netlink Bandwidth Monitor (nlbwmon) is a lightweight, efficient traffic accounting program keeping track of bandwidth usage per host and protocol."))
+
+nw.init(luci.model.uci.cursor_state())
+
+s = m:section(TypedSection, "nlbwmon")
+s.anonymous = true
+s.addremove = false
+s:tab("general", translate("General Settings"))
+s:tab("advanced", translate("Advanced Settings"))
+s:tab("protocol", translate("Protocol Mapping"),
+       translate("Protocol mappings to distinguish traffic types per host, one mapping per line. The first value specifies the IP protocol, the second value the port number and the third column is the name of the mapped protocol."))
+
+period = s:taboption("general", ListValue, "_period", translate("Accounting period"),
+       translate("Choose \"Day of month\" to restart the accounting period monthly on a specific date, e.g. every 3rd. Choose \"Fixed interval\" to restart the accounting period exactly every N days, beginning at a given date."))
+
+period:value("relative", translate("Day of month"))
+period:value("absolute", translate("Fixed interval"))
+
+period.write = function(self, cfg, val)
+       if period:formvalue(cfg) == "relative" then
+               m:set(cfg, "database_interval", interval:formvalue(cfg))
+       else
+               m:set(cfg, "database_interval", "%s/%s" %{
+                       date:formvalue(cfg),
+                       days:formvalue(cfg)
+               })
+       end
+end
+
+period.cfgvalue = function(self, cfg)
+       local val = m:get(cfg, "database_interval") or ""
+       if val:match("^%d%d%d%d%-%d%d%-%d%d/%d+$") then
+               return "absolute"
+       end
+       return "relative"
+end
+
+
+warning = s:taboption("general", DummyValue, "_warning", translate("Warning"))
+warning.default = translatef("Changing the accounting interval type will invalidate existing databases!<br /><strong><a href=\"%s\">Download backup</a></strong>.", luci.dispatcher.build_url("admin/nlbw/backup"))
+warning.rawhtml = true
+
+if (m.uci:get_first("nlbwmon", "nlbwmon", "database_interval") or ""):match("^%d%d%d%d-%d%d-%d%d/%d+$") then
+       warning:depends("_period", "relative")
+else
+       warning:depends("_period", "absolute")
+end
+
+
+interval = s:taboption("general", Value, "_interval", translate("Due date"),
+       translate("Day of month to restart the accounting period. Use negative values to count towards the end of month, e.g. \"-5\" to specify the 27th of July or the 24th of Februrary."))
+
+interval.datatype = "or(range(1,31),range(-31,-1))"
+interval.placeholder = "1"
+interval:value("1", translate("1 - Restart every 1st of month"))
+interval:value("-1", translate("-1 - Restart every last day of month"))
+interval:value("-7", translate("-7 - Restart a week before end of month"))
+interval.rmempty = false
+interval:depends("_period", "relative")
+interval.write = period.write
+
+interval.cfgvalue = function(self, cfg)
+       local val = m:get(cfg, "database_interval")
+       return val and tonumber(val)
+end
+
+
+date = s:taboption("general", Value, "_date", translate("Start date"),
+       translate("Start date of the first accounting period, e.g. begin of ISP contract."))
+
+date.datatype = "dateyyyymmdd"
+date.placeholder = "2016-03-15"
+date.rmempty = false
+date:depends("_period", "absolute")
+date.write = period.write
+
+date.cfgvalue = function(self, cfg)
+       local val = m:get(cfg, "database_interval") or ""
+       return (val:match("^(%d%d%d%d%-%d%d%-%d%d)/%d+$"))
+end
+
+
+days = s:taboption("general", Value, "_days", translate("Interval"),
+       translate("Length of accounting interval in days."))
+
+days.datatype = "min(1)"
+days.placeholder = "30"
+days.rmempty = false
+days:depends("_period", "absolute")
+days.write = period.write
+
+days.cfgvalue = function(self, cfg)
+       local val = m:get(cfg, "database_interval") or ""
+       return (val:match("^%d%d%d%d%-%d%d%-%d%d/(%d+)$"))
+end
+
+
+ifaces = s:taboption("general", Value, "_ifaces", translate("Local interfaces"),
+       translate("Only conntrack streams from or to any of these networks are counted."))
+
+ifaces.template = "cbi/network_netlist"
+ifaces.widget = "checkbox"
+ifaces.nocreate = true
+
+ifaces.cfgvalue = function(self, cfg)
+       return m:get(cfg, "local_network")
+end
+
+ifaces.write = function(self, cfg)
+       local item
+       local items = {}
+       for item in utl.imatch(subnets:formvalue(cfg)) do
+               items[#items+1] = item
+       end
+       for item in utl.imatch(ifaces:formvalue(cfg)) do
+               items[#items+1] = item
+       end
+       m:set(cfg, "local_network", items)
+end
+
+
+subnets = s:taboption("general", DynamicList, "_subnets", translate("Local subnets"),
+       translate("Only conntrack streams from or to any of these subnets are counted."))
+
+subnets.datatype = "ipaddr"
+
+subnets.cfgvalue = function(self, cfg)
+       local subnet
+       local subnets = {}
+       for subnet in utl.imatch(m:get(cfg, "local_network")) do
+               subnet = ip.new(subnet)
+               subnets[#subnets+1] = subnet and subnet:string()
+       end
+       return subnets
+end
+
+subnets.write = ifaces.write
+
+
+limit = s:taboption("advanced", Value, "database_limit", translate("Maximum entries"),
+       translate("The maximum amount of entries that should be put into the database, setting the limit to 0 will allow databases to grow indefinitely."))
+
+limit.datatype = "uinteger"
+limit.placeholder = "10000"
+
+prealloc = s:taboption("advanced", Flag, "database_prealloc", translate("Preallocate database"),
+       translate("Whether to preallocate the maximum possible database size in memory. This is mainly useful for memory constrained systems which might not be able to satisfy memory allocation after longer uptime periods."))
+
+prealloc:depends({["database_limit"] = "0", ["!reverse"] = true })
+
+
+compress = s:taboption("advanced", Flag, "database_compress", translate("Compress database"),
+       translate("Whether to gzip compress archive databases. Compressing the database files makes accessing old data slightly slower but helps to reduce storage requirements."))
+
+compress.default = compress.enabled
+
+
+generations = s:taboption("advanced", Value, "database_generations", translate("Stored periods"),
+       translate("Maximum number of accounting periods to keep, use zero to keep databases forever."))
+
+generations.datatype = "uinteger"
+generations.placeholder = "10"
+
+
+commit = s:taboption("advanced", Value, "commit_interval", translate("Commit interval"),
+       translate("Interval at which the temporary in-memory database is committed to the persistent database directory."))
+
+commit.placeholder = "24h"
+commit:value("24h", translate("24h - least flash wear at the expense of data loss risk"))
+commit:value("12h", translate("12h - compromise between risk of data loss and flash wear"))
+commit:value("10m", translate("10m - frequent commits at the expense of flash wear"))
+commit:value("60s", translate("60s - commit minutely, useful for non-flash storage"))
+
+
+refresh = s:taboption("advanced", Value, "refresh_interval", translate("Refresh interval"),
+       translate("Interval at which traffic counters of still established connections are refreshed from netlink information."))
+
+refresh.placeholder = "30s"
+refresh:value("30s", translate("30s - refresh twice per minute for reasonably current stats"))
+refresh:value("5m", translate("5m - rarely refresh to avoid frequently clearing conntrack counters"))
+
+
+directory = s:taboption("advanced", Value, "database_directory", translate("Database directory"),
+       translate("Database storage directory. One file per accounting period will be placed into this directory."))
+
+directory.placeholder = "/var/lib/nlbwmon"
+
+
+protocols = s:taboption("protocol", TextValue, "_protocols")
+protocols.rows = 50
+
+protocols.cfgvalue = function(self, cfg)
+       return fs.readfile("/usr/share/nlbwmon/protocols")
+end
+
+protocols.write = function(self, cfg, value)
+       fs.writefile("/usr/share/nlbwmon/protocols", (value or ""):gsub("\r\n", "\n"))
+end
+
+protocols.remove = protocols.write
+
+
+return m
diff --git a/applications/luci-app-nlbwmon/luasrc/view/nlbw/backup.htm b/applications/luci-app-nlbwmon/luasrc/view/nlbw/backup.htm
new file mode 100644 (file)
index 0000000..ea2e0f0
--- /dev/null
@@ -0,0 +1,34 @@
+<%#
+ Copyright 2017 Jo-Philipp Wich <jo@mein.io>
+ Licensed to the public under the Apache License 2.0.
+-%>
+
+<%+header%>
+
+<script type="text/javascript" src="<%=resource%>/cbi.js"></script>
+
+<h2 name="content"><%:Netlink Bandwidth Monitor - Backup / Restore %></h2>
+
+<fieldset class="cbi-section">
+       <legend><%:Restore Database Backup%></legend>
+       <p>
+               <form method="POST" action="<%=url("admin/nlbw/restore")%>" enctype="multipart/form-data">
+                       <input type="hidden" name="token" value="<%=token%>" />
+                       <input type="file" name="archive" accept="application/gzip,.gz" />
+                       <input type="submit" value="<%:Restore%>" class="cbi-button cbi-button-apply" />
+               </form>
+
+               <% if message then %>
+                       <div class="alert-message"><%=message%></div>
+               <% end %>
+       </p>
+
+       <legend><%:Download Database Backup%></legend>
+       <p>
+               <form method="GET" action="<%=url("admin/nlbw/download")%>">
+                       <input type="submit" value="<%:Generate Backup%>" class="cbi-button cbi-button-link" />
+               </form>
+       </p>
+</fieldset>
+
+<%+footer%>
diff --git a/applications/luci-app-nlbwmon/luasrc/view/nlbw/display.htm b/applications/luci-app-nlbwmon/luasrc/view/nlbw/display.htm
new file mode 100644 (file)
index 0000000..7e85ace
--- /dev/null
@@ -0,0 +1,1052 @@
+<%#
+ Copyright 2017 Jo-Philipp Wich <jo@mein.io>
+ Licensed to the public under the Apache License 2.0.
+-%>
+
+<% css = [[
+
+       #chartjs-tooltip {
+               opacity: 0;
+               position: absolute;
+               background: rgba(0, 0, 0, .7);
+               color: white;
+               padding: 3px;
+               border-radius: 3px;
+               -webkit-transition: all .1s ease;
+               transition: all .1s ease;
+               pointer-events: none;
+               -webkit-transform: translate(-50%, 0);
+               transform: translate(-50%, 0);
+               z-index: 200;
+       }
+
+       #chartjs-tooltip.above {
+               -webkit-transform: translate(-50%, -100%);
+               transform: translate(-50%, -100%);
+       }
+
+       #chartjs-tooltip.above:before {
+               border: solid;
+               border-color: #111 transparent;
+               border-color: rgba(0, 0, 0, .8) transparent;
+               border-width: 8px 8px 0 8px;
+               bottom: 1em;
+               content: "";
+               display: block;
+               left: 50%;
+               top: 100%;
+               position: absolute;
+               z-index: 99;
+               -webkit-transform: translate(-50%, 0);
+               transform: translate(-50%, 0);
+       }
+
+       table {
+               border: 1px solid #999;
+               border-collapse: collapse;
+               margin: 0 0 2px !important;
+       }
+
+       th, td, table table td {
+               border: 1px solid #999;
+               text-align: right;
+               padding: 1px 3px !important;
+               white-space: nowrap;
+       }
+
+       tbody td {
+               border-bottom-color: #ccc;
+       }
+
+       tbody td[rowspan] {
+               border-bottom-color: #999;
+       }
+
+       tbody tr:last-child td {
+               border-bottom-color: #999;
+       }
+
+
+       .pie {
+               width: 200px;
+               display: inline-block;
+               margin: 20px;
+       }
+
+       .pie label {
+               font-weight: bold;
+               font-size: 14px;
+               display: block;
+               margin-bottom: 10px;
+               text-align: center;
+       }
+
+       .kpi {
+               display: inline-block;
+               margin: 80px 20px 20px;
+               vertical-align: top;
+       }
+
+       .kpi ul {
+               list-style: none;
+       }
+
+       .kpi li {
+               margin: 10px;
+               display: none;
+       }
+
+       .kpi big {
+               font-weight: bold;
+       }
+
+       #detail-bubble {
+               position: absolute;
+               opacity: 0;
+               visibility: hidden;
+       }
+
+       #detail-bubble.in {
+               opacity: 1;
+               visibility: visible;
+               transition: opacity 0.5s;
+       }
+
+       #detail-bubble > div {
+               border: 1px solid #ccc;
+               border-radius: 2px;
+               padding: 5px;
+               background: #fcfcfc;
+       }
+
+       #detail-bubble .head {
+               text-align: center;
+               white-space: nowrap;
+               position: relative;
+       }
+
+       #detail-bubble .head .dismiss {
+               top: 0;
+               right: 0;
+               width: 20px;
+               line-height: 20px;
+               text-align: center;
+               text-decoration: none;
+               font-weight: bold;
+               color: #000;
+               position: absolute;
+               font-size: 20px;
+       }
+
+       #detail-bubble .pie {
+               width: 100px;
+               margin: 5px;
+       }
+
+       #detail-bubble .kpi {
+               margin: 40px 5px 5px;
+               font-size: smaller;
+               text-align: left;
+       }
+
+       #detail-bubble .kpi ul {
+               margin: 0;
+       }
+
+       #bubble-arrow {
+               border: 1px solid #ccc;
+               border-width: 1px 0 0 1px;
+               background: #fcfcfc;
+               width: 15px;
+               height: 15px;
+               position: absolute;
+               left: 0;
+               top: -8px;
+               transform: rotate(45deg);
+               margin: 0 0 0 -8px;
+       }
+
+       tr.active > td {
+               border-bottom: 2px solid red;
+       }
+
+       tr.active > td.active {
+               border: 2px solid red;
+               border-bottom: none;
+       }
+
+       td.detail {
+               border: 2px solid red;
+               border-top: none;
+               opacity: 0;
+               transition: opacity 0.5s;
+       }
+
+       td.detail.in {
+               opacity: 1;
+       }
+
+       th.hostname,
+       td.hostname {
+               text-align: left;
+       }
+
+]] -%>
+
+<%+header%>
+
+<script type="text/javascript" src="<%=resource%>/cbi.js"></script>
+<script type="text/javascript" src="<%=resource%>/nlbw.chart.min.js"></script>
+<script type="text/javascript">//<![CDATA[
+
+var chartRegistry = {},
+       trafficPeriods = [],
+       trafficData = { columns: [], data: [] },
+       hostNames = {},
+       hostInfo = <%=luci.util.serialize_json(luci.sys.net.host_hints())%>,
+       ouiData = [];
+
+
+function off(elem)
+{
+       var val = [0, 0];
+       do {
+               if (!isNaN(elem.offsetLeft) && !isNaN(elem.offsetTop)) {
+                       val[0] += elem.offsetLeft;
+                       val[1] += elem.offsetTop;
+               }
+       }
+       while ((elem = elem.offsetParent) != null);
+       return val;
+}
+
+Chart.defaults.global.customTooltips = function(tooltip) {
+       var tooltipEl = document.getElementById('chartjs-tooltip');
+
+       if (!tooltipEl) {
+               tooltipEl = document.createElement('div');
+               tooltipEl.setAttribute('id', 'chartjs-tooltip');
+               document.body.appendChild(tooltipEl);
+       }
+
+       if (!tooltip) {
+               if (tooltipEl.row)
+                       tooltipEl.row.style.backgroundColor = '';
+
+               tooltipEl.style.opacity = 0;
+               return;
+       }
+
+       var pos = off(tooltip.chart.canvas);
+
+       tooltipEl.className = tooltip.yAlign;
+       tooltipEl.innerHTML = tooltip.text[0];
+
+       tooltipEl.style.opacity = 1;
+       tooltipEl.style.left = pos[0] + tooltip.x + 'px';
+       tooltipEl.style.top = pos[1] + tooltip.y - tooltip.caretHeight - tooltip.caretPadding + 'px';
+
+       var row = tooltip.text[1],
+           hue = tooltip.text[2];
+
+       if (row && !isNaN(hue)) {
+               row.style.backgroundColor = 'hsl(%u, 100%%, 80%%)'.format(hue);
+               tooltipEl.row = row;
+       }
+};
+
+Chart.defaults.global.tooltipFontSize = 10;
+Chart.defaults.global.tooltipTemplate = function(tip) {
+       tip.label[0] = tip.label[0].format(tip.value);
+       return tip.label;
+};
+
+function kpi(id, val1, val2, val3)
+{
+       var e = document.getElementById(id);
+
+       if (val1 && val2 && val3)
+               e.innerHTML = '<%:%s, %s and %s%>'.format(val1, val2, val3);
+       else if (val1 && val2)
+               e.innerHTML = '<%:%s and %s%>'.format(val1, val2);
+       else if (val1)
+               e.innerHTML = val1;
+
+       e.parentNode.style.display = val1 ? 'list-item' : '';
+}
+
+function pie(id, data)
+{
+       data.sort(function(a, b) { return b.value - a.value });
+
+       if (data.length === 0 || (data.length === 1 && data[0].value === 0))
+               data[0] = {
+                       value: 1,
+                       color: '#cccccc',
+                       label: [ '<%:no traffic%>' ]
+               };
+
+       for (var i = 0; i < data.length; i++) {
+               if (!data[i].color) {
+                       var hue = 120 / (data.length-1) * i;
+                       data[i].color = 'hsl(%u, 80%%, 50%%)'.format(hue);
+                       data[i].label.push(hue);
+               }
+       }
+
+       var ctx = document.getElementById(id).getContext('2d');
+
+       if (chartRegistry.hasOwnProperty(id))
+               chartRegistry[id].destroy();
+
+       chartRegistry[id] = new Chart(ctx).Doughnut(data, {
+               segmentStrokeWidth: 1,
+               percentageInnerCutout: 30
+       });
+
+       return chartRegistry[id];
+}
+
+function query(filter, group, order)
+{
+       var keys = [], columns = {}, records = {}, result = [];
+
+       if (typeof(group) !== 'function' && typeof(group) !== 'object')
+               group = ['mac'];
+
+       for (var i = 0; i < trafficData.columns.length; i++)
+               columns[trafficData.columns[i]] = i;
+
+       for (var i = 0; i < trafficData.data.length; i++) {
+               var record = trafficData.data[i];
+
+               if (typeof(filter) === 'function' && filter(columns, record) !== true)
+                       continue;
+
+               var key;
+
+               if (typeof(group) === 'function') {
+                       key = group(columns, record);
+               }
+               else {
+                       key = [];
+
+                       for (var j = 0; j < group.length; j++)
+                               if (columns.hasOwnProperty(group[j]))
+                                       key.push(record[columns[group[j]]]);
+
+                       key = key.join(',');
+               }
+
+               if (!records.hasOwnProperty(key)) {
+                       var rec = {};
+
+                       for (var col in columns)
+                               rec[col] = record[columns[col]];
+
+                       records[key] = rec;
+                       result.push(rec);
+               }
+               else {
+                       records[key].conns    += record[columns.conns];
+                       records[key].rx_bytes += record[columns.rx_bytes];
+                       records[key].rx_pkts  += record[columns.rx_pkts];
+                       records[key].tx_bytes += record[columns.tx_bytes];
+                       records[key].tx_pkts  += record[columns.tx_pkts];
+               }
+       }
+
+       if (typeof(order) === 'function')
+               result.sort(order);
+
+       return result;
+}
+
+function oui(mac) {
+       var m, l = 0, r = ouiData.length / 3 - 1;
+       var mac1 = parseInt(mac.replace(/[^a-fA-F0-9]/g, ''), 16);
+
+       while (l <= r) {
+               m = l + Math.floor((r - l) / 2);
+
+               var mask = (0xffffffffffff -
+                                       (Math.pow(2, 48 - ouiData[m * 3 + 1]) - 1));
+
+               var mac1_hi = ((mac1 / 0x10000) & (mask / 0x10000)) >>> 0;
+               var mac1_lo = ((mac1 &  0xffff) & (mask &  0xffff)) >>> 0;
+
+               var mac2 = parseInt(ouiData[m * 3], 16);
+               var mac2_hi = (mac2 / 0x10000) >>> 0;
+               var mac2_lo = (mac2 &  0xffff) >>> 0;
+
+               if (mac1_hi === mac2_hi && mac1_lo === mac2_lo)
+                       return ouiData[m * 3 + 2];
+
+               if (mac2_hi > mac1_hi ||
+                       (mac2_hi === mac1_hi && mac2_lo > mac1_lo))
+                       r = m - 1;
+               else
+                       l = m + 1;
+       }
+
+       return null;
+}
+
+
+function fetchData(period)
+{
+       XHR.get('<%=url("admin/nlbw/data")%>', { period: period, group_by: 'family,mac,ip,layer7', order_by: '-rx_bytes,-tx_bytes' }, function(xhr, res) {
+               if (res !== null && typeof(res) === 'object' && typeof(res.columns) === 'object' && typeof(res.data) === 'object')
+                       trafficData = res;
+
+               var addrs = query(null, ['ip'], null);
+               var ipAddrs = [];
+
+               for (var i = 0; i < addrs.length; i++)
+                       if (ipAddrs.indexOf(addrs[i].ip) < 0)
+                               ipAddrs.push(addrs[i].ip);
+
+               renderHostData();
+               renderLayer7Data();
+               renderIPv6Data();
+
+               XHR.get('<%=url("admin/nlbw/ptr")%>/' + ipAddrs.join('/'), null, function(xhr, res) {
+                       if (res !== null && typeof(res) === 'object')
+                               hostNames = res;
+               });
+       });
+}
+
+function switchTab(tab)
+{
+       bubbleDismiss();
+
+       return cbi_t_switch('nlbw', tab);
+}
+
+function renderPeriods()
+{
+       var sel = document.getElementById('nlbw.period');
+
+       for (var e, i = trafficPeriods.length - 1; e = trafficPeriods[i]; i--) {
+               var d1 = new Date(e);
+               var d2, pd;
+
+               if (i) {
+                       d2 = new Date(trafficPeriods[i - 1]);
+                       d2.setDate(d2.getDate() - 1);
+                       pd = '%04d-%02d-%02d'.format(d1.getFullYear(), d1.getMonth() + 1, d1.getDate());
+               }
+               else {
+                       d2 = new Date();
+                       pd = '';
+               }
+
+               var opt = document.createElement('option');
+                   opt.setAttribute('data-duration', (d2.getTime() - d1.getTime()) / 1000);
+                   opt.value = pd;
+                   opt.text = '%04d-%02d-%02d - %04d-%02d-%02d'.format(
+                               d1.getFullYear(), d1.getMonth() + 1, d1.getDate(),
+                               d2.getFullYear(), d2.getMonth() + 1, d2.getDate());
+
+               sel.appendChild(opt);
+       }
+
+       sel.selectedIndex = sel.childNodes.length - 1;
+       sel.style.display = '';
+
+       sel.onchange = function() {
+               bubbleDismiss();
+               fetchData(sel.options[sel.selectedIndex].value);
+       }
+}
+
+function renderHostDetail()
+{
+       var key = this.getAttribute('href').substr(1),
+           col = this.getAttribute('data-col'),
+           label = this.getAttribute('data-label'),
+           bubble = document.getElementById('detail-bubble'),
+           arrow = document.getElementById('bubble-arrow'),
+           table = document.getElementById('bubble-table');
+
+       bubbleDismiss();
+
+       var detailData = query(
+               function(c, r) {
+                       return ((r[c.mac] === key || r[c.ip] === key) &&
+                               (r[c.rx_bytes] > 0 || r[c.tx_bytes] > 0));
+               },
+               [col],
+               function(r1, r2) {
+                       return ((r2.rx_bytes + r2.tx_bytes) - (r1.rx_bytes + r1.tx_bytes));
+               }
+       );
+
+       var rxData = [], txData = [];
+
+       table.innerHTML = '<tr>' +
+               '<th>%s</th>'.format(label || col) +
+               '<th><%:Conn.%></th>' +
+               '<th colspan="2"><%:Down. (Bytes / Pkts.)%></th>' +
+               '<th colspan="2"><%:Up. (Bytes / Pkts.)%></th>' +
+       '</tr>';
+
+       for (var i = 0; i < detailData.length; i++) {
+               var rec = detailData[i],
+                   row = table.insertRow(-1);
+
+               row.insertCell(-1).innerHTML = rec[col] || '<%:other%>';
+               row.insertCell(-1).innerHTML = "%1000.2m".format(rec.conns);
+               row.insertCell(-1).innerHTML = "%1024.2mB".format(rec.rx_bytes);
+               row.insertCell(-1).innerHTML = "%1000.2mP".format(rec.rx_pkts);
+               row.insertCell(-1).innerHTML = "%1024.2mB".format(rec.tx_bytes);
+               row.insertCell(-1).innerHTML = "%1000.2mP".format(rec.tx_pkts);
+
+               rxData.push({
+                       label: ['%s: %%1024.2mB'.format(rec[col] || '<%:other%>'), row],
+                       value: rec.rx_bytes
+               });
+
+               txData.push({
+                       label: ['%s: %%1024.2mB'.format(rec[col] || '<%:other%>'), row],
+                       value: rec.tx_bytes
+               });
+       }
+
+       pie('bubble-pie1', rxData);
+       pie('bubble-pie2', txData);
+
+       var mac = key.toUpperCase();
+       var name = hostInfo.hasOwnProperty(mac) ? hostInfo[mac].name : null;
+
+       if (!name)
+               for (var i = 0; i < detailData.length; i++)
+                       if ((name = hostNames[detailData[i].ip]) !== undefined)
+                               break;
+
+       if (mac !== '00:00:00:00:00:00') {
+               kpi('bubble-hostname', name);
+               kpi('bubble-vendor', oui(mac));
+       }
+       else {
+               kpi('bubble-hostname');
+               kpi('bubble-vendor');
+       }
+
+       var tr = this.parentNode.parentNode,
+           xy = off(tr),
+           xy2 = off(this);
+
+       bubble.style.width = tr.offsetWidth + 'px';
+       bubble.style.left = xy[0] + 'px';
+       bubble.style.top = (xy[1] + tr.offsetHeight) + 'px';
+       arrow.style.left = Math.floor(xy2[0] + this.offsetWidth / 2 - xy[0]) + 'px';
+
+       bubble.className = 'in';
+
+       return false;
+}
+
+function formatHostname(dns)
+{
+       if (dns === undefined || dns === null || dns === '')
+               return '-';
+
+       dns = dns.split('.')[0];
+
+       if (dns.length > 12)
+               return '<span title="%q">%h…</span>'.format(dns, dns.substr(0, 12));
+
+       return '%h'.format(dns);
+}
+
+function renderHostData()
+{
+       var trafData = [], connData = [];
+       var rx_total = 0, tx_total = 0, conn_total = 0;
+       var table = document.getElementById('host-data');
+
+       var hostData = query(
+               function(c, r) {
+                       return (r[c.rx_bytes] > 0 || r[c.tx_bytes] > 0);
+               },
+               ['mac'],
+               //function(c, r) {
+               //      return (r[c.mac] !== '00:00:00:00:00:00') ? r[c.mac] : r[c.ip];
+               //},
+               function(r1, r2) {
+                       return ((r2.rx_bytes + r2.tx_bytes) - (r1.rx_bytes + r1.tx_bytes));
+               }
+       );
+
+       while (table.rows.length > 1)
+               table.deleteRow(1);
+
+       for (var i = 0; i < hostData.length; i++) {
+               var row = table.insertRow(-1),
+                   cell = row.insertCell(-1),
+                   rec = hostData[i],
+                   mac = rec.mac.toUpperCase(),
+                   key = (mac !== '00:00:00:00:00:00') ? mac : rec.ip,
+                   dns = hostInfo[mac] ? hostInfo[mac].name : null;
+
+               var link1 = document.createElement('a');
+                   link1.onclick = renderHostDetail;
+                   link1.href = '#' + rec.mac;
+                   link1.setAttribute('data-col', 'ip');
+                   link1.setAttribute('data-label', '<%:Source IP%>');
+                   link1.innerHTML = (mac !== '00:00:00:00:00:00') ? mac : '<%:other%>';
+
+               var link2 = document.createElement('a');
+                   link2.onclick = renderHostDetail;
+                   link2.href = '#' + rec.mac;
+                   link2.setAttribute('data-col', 'layer7');
+                   link2.setAttribute('data-label', '<%:Protocol%>');
+                   link2.innerHTML = "%1000.2m".format(rec.conns);
+
+               cell.innerHTML = formatHostname(dns);
+               cell.className = 'hostname';
+
+               row.insertCell(-1).appendChild(link1);
+               row.insertCell(-1).appendChild(link2);
+               row.insertCell(-1).innerHTML = "%1024.2mB".format(rec.rx_bytes);
+               row.insertCell(-1).innerHTML = "%1000.2mP".format(rec.rx_pkts);
+               row.insertCell(-1).innerHTML = "%1024.2mB".format(rec.tx_bytes);
+               row.insertCell(-1).innerHTML = "%1000.2mP".format(rec.tx_pkts);
+
+               trafData.push({
+                       value: rec.rx_bytes + rec.tx_bytes,
+                       label: ["%s: %%.2mB".format(key), row]
+               });
+
+               connData.push({
+                       value: rec.conns,
+                       label: ["%s: %%.2m".format(key), row]
+               });
+
+               rx_total += rec.rx_bytes;
+               tx_total += rec.tx_bytes;
+               conn_total += rec.conns;
+       }
+
+       if (table.rows.length === 1) {
+               var cell = table.insertRow(-1).insertCell(-1);
+
+               cell.setAttribute('colspan', 6);
+               cell.innerHTML = '<em><%:No data recorded yet.%> <a href="<%=url("admin/nlbw/commit")%>"><%:Force reload…%></a></em>';
+       }
+
+       pie('traf-pie', trafData);
+       pie('conn-pie', connData);
+
+       kpi('rx-total', '%1024.2mB'.format(rx_total));
+       kpi('tx-total', '%1024.2mB'.format(tx_total));
+       kpi('conn-total', '%1000m'.format(conn_total));
+       kpi('host-total', '%u'.format(hostData.length));
+}
+
+function renderLayer7Data()
+{
+       var rxData = [], txData = [];
+       var topConn = [[0],[0],[0]], topRx = [[0],[0],[0]], topTx = [[0],[0],[0]];
+       var table = document.getElementById('layer7-data');
+
+       var layer7Data = query(
+               null, ['layer7'],
+               function(r1, r2) {
+                       return ((r2.rx_bytes + r2.tx_bytes) - (r1.rx_bytes + r1.tx_bytes));
+               }
+       );
+
+       while (table.rows.length > 1)
+               table.deleteRow(1);
+
+       for (var i = 0, c = 0; i < layer7Data.length; i++) {
+               var rec = layer7Data[i],
+                   row = table.insertRow(-1);
+
+               rxData.push({
+                       value: rec.rx_bytes,
+                       label: ["%s: %%.2mB".format(rec.layer7 || '<%:other%>'), row]
+               });
+
+               txData.push({
+                       value: rec.tx_bytes,
+                       label: ["%s: %%.2mB".format(rec.layer7 || '<%:other%>'), row]
+               });
+
+               row.insertCell(-1).innerHTML = rec.layer7 || '<%:other%>';
+               row.insertCell(-1).innerHTML = "%1000m".format(rec.conns);
+               row.insertCell(-1).innerHTML = "%1024.2mB".format(rec.rx_bytes);
+               row.insertCell(-1).innerHTML = "%1000.2mP".format(rec.rx_pkts);
+               row.insertCell(-1).innerHTML = "%1024.2mB".format(rec.tx_bytes);
+               row.insertCell(-1).innerHTML = "%1000.2mP".format(rec.tx_pkts);
+
+               if (rec.layer7) {
+                       topRx.push([rec.rx_bytes, rec.layer7]);
+                       topTx.push([rec.tx_bytes, rec.layer7]);
+                       topConn.push([rec.conns, rec.layer7]);
+               }
+       }
+
+       if (table.rows.length === 1) {
+               var cell = table.insertRow(-1).insertCell(-1);
+
+               cell.setAttribute('colspan', 6);
+               cell.innerHTML = '<em><%:No data recorded yet.%> <a href="<%=url("admin/nlbw/commit")%>"><%:Force reload…%></a></em>';
+       }
+
+       pie('layer7-rx-pie', rxData);
+       pie('layer7-tx-pie', txData);
+
+       topRx.sort(function(a, b) { return b[0] - a[0] });
+       topTx.sort(function(a, b) { return b[0] - a[0] });
+       topConn.sort(function(a, b) { return b[0] - a[0] });
+
+       kpi('layer7-total', layer7Data.length);
+       kpi('layer7-most-rx', topRx[0][1], topRx[1][1], topRx[2][1]);
+       kpi('layer7-most-tx', topTx[0][1], topTx[1][1], topTx[2][1]);
+       kpi('layer7-most-conn', topConn[0][1], topConn[1][1], topConn[2][1]);
+}
+
+function renderIPv6Data()
+{
+       var table     = document.getElementById('ipv6-data'),
+           col       = { },
+           rx4_total = 0,
+           tx4_total = 0,
+           rx6_total = 0,
+           tx6_total = 0,
+           v4_total  = 0,
+           v6_total  = 0,
+           ds_total  = 0,
+           families  = { },
+           records   = { };
+
+       ipv6Data = query(
+               null, ['family', 'mac'],
+               function(r1, r2) {
+                       return ((r2.rx_bytes + r2.tx_bytes) - (r1.rx_bytes + r1.tx_bytes));
+               }
+       );
+
+       for (var i = 0, c = 0; i < ipv6Data.length; i++) {
+               var rec = ipv6Data[i],
+                   mac = rec.mac.toUpperCase(),
+                   ip  = rec.ip,
+                   fam = families[mac] || 0,
+                   recs = records[mac] || {};
+
+               if (rec.family == 4) {
+                       rx4_total += rec.rx_bytes;
+                       tx4_total += rec.tx_bytes;
+                       fam |= 1;
+               }
+               else {
+                       rx6_total += rec.rx_bytes;
+                       tx6_total += rec.tx_bytes;
+                       fam |= 2;
+               }
+
+               recs[rec.family] = rec;
+               records[mac] = recs;
+
+               families[mac] = fam;
+       }
+
+       for (var mac in families) {
+               switch (families[mac])
+               {
+               case 3:
+                       ds_total++;
+                       break;
+
+               case 2:
+                       v6_total++;
+                       break;
+
+               case 1:
+                       v4_total++;
+                       break;
+               }
+       }
+
+       while (table.rows.length > 1)
+               table.deleteRow(1);
+
+       for (var mac in records) {
+               if (mac === '00:00:00:00:00:00')
+                       continue;
+
+               var row = table.insertRow(-1),
+                   cell1 = row.insertCell(-1),
+                   cell2 = row.insertCell(-1),
+                   dns = hostInfo[mac] ? hostInfo[mac].name : null,
+                   rec4 = records[mac][4],
+                   rec6 = records[mac][6];
+
+               cell1.setAttribute('rowspan', 2);
+               cell1.innerHTML = formatHostname(dns);
+               cell1.className = 'hostname';
+
+               cell2.setAttribute('rowspan', 2);
+               cell2.innerHTML = mac;
+
+               row.insertCell(-1).innerHTML = 'IPv4';
+               row.insertCell(-1).innerHTML = rec4 ? "%1024.2mB".format(rec4.rx_bytes) : '-';
+               row.insertCell(-1).innerHTML = rec4 ? "%1000.2mP".format(rec4.rx_pkts)  : '-';
+               row.insertCell(-1).innerHTML = rec4 ? "%1024.2mB".format(rec4.tx_bytes) : '-';
+               row.insertCell(-1).innerHTML = rec4 ? "%1000.2mP".format(rec4.tx_pkts)  : '-';
+
+               row = table.insertRow(-1);
+
+               row.insertCell(-1).innerHTML = 'IPv6';
+               row.insertCell(-1).innerHTML = rec6 ? "%1024.2mB".format(rec6.rx_bytes) : '-';
+               row.insertCell(-1).innerHTML = rec6 ? "%1000.2mP".format(rec6.rx_pkts)  : '-';
+               row.insertCell(-1).innerHTML = rec6 ? "%1024.2mB".format(rec6.tx_bytes) : '-';
+               row.insertCell(-1).innerHTML = rec6 ? "%1000.2mP".format(rec6.tx_pkts)  : '-';
+       }
+
+       if (table.rows.length === 1) {
+               var cell = table.insertRow(-1).insertCell(-1);
+
+               cell.setAttribute('colspan', 7);
+               cell.innerHTML = '<em><%:No data recorded yet.%> <a href="<%=url("admin/nlbw/commit")%>"><%:Force reload…%></a></em>';
+       }
+
+       var shareData = [], hostsData = [];
+
+       if (rx4_total > 0 || tx4_total > 0)
+               shareData.push({
+                       value: rx4_total + tx4_total,
+                       label: ["IPv4: %.2mB"],
+                       color: 'hsl(140, 100%, 50%)'
+               });
+
+       if (rx6_total > 0 || tx6_total > 0)
+               shareData.push({
+                       value: rx6_total + tx6_total,
+                       label: ["IPv6: %.2mB"],
+                       color: 'hsl(180, 100%, 50%)'
+               });
+
+       if (v4_total > 0)
+               hostsData.push({
+                       value: v4_total,
+                       label: ["<%:%d IPv4-only hosts%>"],
+                       color: 'hsl(140, 100%, 50%)'
+               });
+
+       if (v6_total > 0)
+               hostsData.push({
+                       value: v6_total,
+                       label: ["<%:%d IPv6-only hosts%>"],
+                       color: 'hsl(180, 100%, 50%)'
+               });
+
+       if (ds_total > 0)
+               hostsData.push({
+                       value: ds_total,
+                       label: ["<%:%d dual-stack hosts%>"],
+                       color: 'hsl(50, 100%, 50%)'
+               });
+
+       pie('ipv6-share-pie', shareData);
+       pie('ipv6-hosts-pie', hostsData);
+
+       kpi('ipv6-hosts', '%.2f%%'.format(100 / (ds_total + v4_total + v6_total) * (ds_total + v6_total)));
+       kpi('ipv6-share', '%.2f%%'.format(100 / (rx4_total + rx6_total + tx4_total + tx6_total) * (rx6_total + tx6_total)));
+       kpi('ipv6-rx', '%1024.2mB'.format(rx6_total));
+       kpi('ipv6-tx', '%1024.2mB'.format(tx6_total));
+}
+
+function bubbleDismiss()
+{
+       var bubble = document.getElementById('detail-bubble');
+
+       bubble.className = '';
+       document.body.appendChild(bubble);
+
+       return false;
+}
+
+
+//]]></script>
+
+<h2 name="content"><%:Netlink Bandwidth Monitor%></h2>
+
+<div id="detail-bubble">
+       <span id="bubble-arrow"></span>
+       <div>
+               <div class="head">
+                       <a class="dismiss" href="#" onclick="this.blur(); return bubbleDismiss()">×</a>
+                       <div class="pie">
+                               <label>Download</label>
+                               <canvas id="bubble-pie1" width="100" height="100"></canvas>
+                       </div>
+                       <div class="pie">
+                               <label>Upload</label>
+                               <canvas id="bubble-pie2" width="100" height="100"></canvas>
+                       </div>
+                       <div class="kpi">
+                               <ul>
+                                       <li><%_Hostname: <big id="bubble-hostname">example.org</big>%></li>
+                                       <li><%_Vendor: <big id="bubble-vendor">Example Corp.</big>%></li>
+                               </ul>
+                       </div>
+               </div>
+               <table id="bubble-table"></table>
+       </div>
+</div>
+
+<hr>
+
+<p>
+       <%:Select accounting period:%>
+       <select id="nlbw.period" style="display:none"></select>
+</p>
+
+<hr>
+
+<ul class="cbi-tabmenu">
+       <li id="tab.nlbw.traffic" class="cbi-tab"><a href="#" onclick="return switchTab('traffic')"><%:Traffic Distribution%></a></li>
+       <li id="tab.nlbw.layer7" class="cbi-tab-disabled"><a href="#" onclick="return switchTab('layer7')"><%:Application Protocols%></a></li>
+       <li id="tab.nlbw.ipv6" class="cbi-tab-disabled"><a href="#" onclick="return switchTab('ipv6')"><%:IPv6%></a></li>
+       <li id="tab.nlbw.export" class="cbi-tab-disabled"><a href="#" onclick="return switchTab('export')"><%:Export%></a></li>
+</ul>
+
+<div class="cbi-section" id="container.nlbw.traffic">
+       <div>
+               <div class="pie">
+                       <label><%:Traffic / Host%></label>
+                       <canvas id="traf-pie" width="200" height="200"></canvas>
+               </div>
+
+               <div class="pie">
+                       <label><%:Connections / Host%></label>
+                       <canvas id="conn-pie" width="200" height="200"></canvas>
+               </div>
+
+               <div class="kpi">
+                       <ul>
+                               <li><%_<big id="host-total">0</big> hosts%></li>
+                               <li><%_<big id="rx-total">0</big> download%></li>
+                               <li><%_<big id="tx-total">0</big> upload%></li>
+                               <li><%_<big id="conn-total">0</big> connections%></li>
+                       </ul>
+               </div>
+       </div>
+       <table id="host-data">
+               <tr>
+                       <th width="10%" class="hostname"><%:Host%></th>
+                       <th width="5%"><%:MAC%></th>
+                       <th width="5%"><%:Connections%></th>
+                       <th width="30%" colspan="2"><%:Download (Bytes / Packets)%></th>
+                       <th width="30%" colspan="2"><%:Upload (Bytes / Packets)%></th>
+               </tr>
+       </table>
+</div>
+
+<div class="cbi-section" id="container.nlbw.layer7" style="display:none">
+       <div>
+               <div class="pie">
+                       <label><%:Download / Application%></label>
+                       <canvas id="layer7-rx-pie" width="200" height="200"></canvas>
+               </div>
+
+               <div class="pie">
+                       <label><%:Upload / Application%></label>
+                       <canvas id="layer7-tx-pie" width="200" height="200"></canvas>
+               </div>
+
+               <div class="kpi">
+                       <ul>
+                               <li><%_<big id="layer7-total">0</big> different application protocols%></li>
+                               <li><%_<big id="layer7-most-rx">0</big> cause the most download%></li>
+                               <li><%_<big id="layer7-most-tx">0</big> cause the most upload%></li>
+                               <li><%_<big id="layer7-most-conn">0</big> cause the most connections%></li>
+                       </ul>
+               </div>
+       </div>
+       <table id="layer7-data">
+               <tr>
+                       <th width="20%"><%:Application%></th>
+                       <th width="10%"><%:Connections%></th>
+                       <th width="30%" colspan="2"><%:Download (Bytes / Packets)%></th>
+                       <th width="30%" colspan="2"><%:Upload (Bytes / Packets)%></th>
+               </tr>
+       </table>
+</div>
+
+<div class="cbi-section" id="container.nlbw.ipv6" style="display:none">
+       <div>
+               <div class="pie">
+                       <label><%:IPv4 vs. IPv6%></label>
+                       <canvas id="ipv6-share-pie" width="200" height="200"></canvas>
+               </div>
+
+               <div class="pie">
+                       <label><%:Dualstack enabled hosts%></label>
+                       <canvas id="ipv6-hosts-pie" width="200" height="200"></canvas>
+               </div>
+
+               <div class="kpi">
+                       <ul>
+                               <li><%_<big id="ipv6-hosts">0%</big> IPv6 support rate among hosts%></li>
+                               <li><%_<big id="ipv6-share">0%</big> of the total traffic is IPv6%></li>
+                               <li><%_<big id="ipv6-rx">0B</big> total IPv6 download%></li>
+                               <li><%_<big id="ipv6-tx">0B</big> total IPv6 upload%></li>
+                       </ul>
+               </div>
+       </div>
+       <table id="ipv6-data">
+               <tr>
+                       <th width="10%" class="hostname"><%:Host%></th>
+                       <th width="5%"><%:MAC%></th>
+                       <th width="5%"><%:Family%></th>
+                       <th width="40%" colspan="2"><%:Download (Bytes / Packets)%></th>
+                       <th width="40%" colspan="2"><%:Upload (Bytes / Packets)%></th>
+               </tr>
+       </table>
+</div>
+
+<div class="cbi-section" id="container.nlbw.export" style="display:none">
+       <ul>
+               <li><a href="<%=url('admin/nlbw/data')%>?type=csv&#38;group_by=mac&#38;order_by=-rx,-tx"><%:CSV, grouped by MAC%></a></li>
+               <li><a href="<%=url('admin/nlbw/data')%>?type=csv&#38;group_by=ip&#38;order_by=-rx,-tx"><%:CSV, grouped by IP%></a></li>
+               <li><a href="<%=url('admin/nlbw/data')%>?type=csv&#38;group_by=layer7&#38;order_by=-rx,-tx"><%:CSV, grouped by protocol%></a></li>
+               <li><a href="<%=url('admin/nlbw/data')%>?type=json"><%:JSON dump%></a></li>
+       </ul>
+</div>
+
+<script type="text/javascript">//<![CDATA[
+       cbi_t_add('nlbw', 'traffic');
+       cbi_t_add('nlbw', 'layer7');
+       cbi_t_add('nlbw', 'ipv6');
+       cbi_t_add('nlbw', 'export');
+
+       XHR.get('<%=url("admin/nlbw/list")%>', null, function(xhr, res) {
+
+               if (res !== null && typeof(res) === 'object' && res.length > 0) {
+                       trafficPeriods = res;
+                       renderPeriods();
+               }
+
+               xhr.open('GET', 'https://raw.githubusercontent.com/jow-/oui-database/master/oui.json', true);
+               xhr.onreadystatechange = function() {
+                       if (xhr.readyState === 4) {
+                               try { res = JSON.parse(xhr.responseText); }
+                               catch(e) { res = null; }
+
+                               if (res !== null && typeof(res) === 'object' && (res.length % 3) === 0)
+                                       ouiData = res;
+
+                               fetchData('');
+                       }
+               };
+               xhr.send(null);
+       });
+//]]></script>
+
+<%+footer%>
diff --git a/applications/luci-app-nlbwmon/po/ja/nlbwmon.po b/applications/luci-app-nlbwmon/po/ja/nlbwmon.po
new file mode 100644 (file)
index 0000000..b5931e0
--- /dev/null
@@ -0,0 +1,387 @@
+msgid ""
+msgstr ""
+"Content-Type: text/plain; charset=UTF-8\n"
+"Project-Id-Version: \n"
+"POT-Creation-Date: \n"
+"PO-Revision-Date: \n"
+"Last-Translator: INAGAKI Hiroshi <musashino.open@gmail.com>\n"
+"Language-Team: \n"
+"MIME-Version: 1.0\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: ja\n"
+"X-Generator: Poedit 2.0.3\n"
+
+msgid "%d IPv4-only hosts"
+msgstr "%d IPv4 限定ホスト"
+
+msgid "%d IPv6-only hosts"
+msgstr "%d IPv6 限定ホスト"
+
+msgid "%d dual-stack hosts"
+msgstr "%d デュアルスタック ホスト"
+
+msgid "%s and %s"
+msgstr "%s, %s"
+
+msgid "%s, %s and %s"
+msgstr "%s, %s, %s"
+
+msgid "-1 - Restart every last day of month"
+msgstr "-1 - 月の最終日"
+
+msgid "-7 - Restart a week before end of month"
+msgstr "-7 - 月の最終日の一週間前"
+
+msgid "1 - Restart every 1st of month"
+msgstr "1 - 毎月1日"
+
+msgid "10m - frequent commits at the expense of flash wear"
+msgstr "10m - フラッシュ媒体への負荷が高い頻繁なコミット(10分)"
+
+msgid "12h - compromise between risk of data loss and flash wear"
+msgstr "12h - データ消失リスクとフラッシュ媒体への負荷の妥協点(12時間)"
+
+msgid "24h - least flash wear at the expense of data loss risk"
+msgstr "24h - データ消失リスクは高いがフラッシュ媒体への負荷は最小(24時間)"
+
+msgid "30s - refresh twice per minute for reasonably current stats"
+msgstr "30s - 現在の状態の把握に適切な1分間に2回のリフレッシュ(30秒)"
+
+msgid "5m - rarely refresh to avoid frequently clearing conntrack counters"
+msgstr ""
+
+msgid "60s - commit minutely, useful for non-flash storage"
+msgstr "60秒 - 1分毎のコミット、非フラッシュ ストレージに有用"
+
+msgid "<big id=\"conn-total\">0</big> connections"
+msgstr "<big id=\"conn-total\">0</big> 接続数"
+
+msgid "<big id=\"host-total\">0</big> hosts"
+msgstr "<big id=\"host-total\">0</big> ホスト数"
+
+msgid "<big id=\"ipv6-hosts\">0%</big> IPv6 support rate among hosts"
+msgstr "<big id=\"ipv6-hosts\">0%</big> 全ホスト中の IPv6 サポート比率"
+
+msgid "<big id=\"ipv6-rx\">0B</big> total IPv6 download"
+msgstr "<big id=\"ipv6-rx\">0B</big> IPv6 総ダウンロード"
+
+msgid "<big id=\"ipv6-share\">0%</big> of the total traffic is IPv6"
+msgstr "<big id=\"ipv6-share\">0%</big> 全トラフィック中の IPv6 の割合"
+
+msgid "<big id=\"ipv6-tx\">0B</big> total IPv6 upload"
+msgstr "<big id=\"ipv6-tx\">0B</big> IPv6 総アップロード"
+
+msgid "<big id=\"layer7-most-conn\">0</big> cause the most connections"
+msgstr "<big id=\"layer7-most-conn\">0</big> 接続数上位"
+
+msgid "<big id=\"layer7-most-rx\">0</big> cause the most download"
+msgstr "<big id=\"layer7-most-rx\">0</big> ダウンロード上位"
+
+msgid "<big id=\"layer7-most-tx\">0</big> cause the most upload"
+msgstr "<big id=\"layer7-most-tx\">0</big> アップロード上位"
+
+msgid "<big id=\"layer7-total\">0</big> different application protocols"
+msgstr "<big id=\"layer7-total\">0</big> アプリケーション プロトコル数"
+
+msgid "<big id=\"rx-total\">0</big> download"
+msgstr "<big id=\"rx-total\">0</big> ダウンロード"
+
+msgid "<big id=\"tx-total\">0</big> upload"
+msgstr "<big id=\"tx-total\">0</big> アップロード"
+
+msgid "Accounting period"
+msgstr "収集期間"
+
+msgid "Advanced Settings"
+msgstr "拡張設定"
+
+msgid "Application"
+msgstr "アプリケーション"
+
+msgid "Application Protocols"
+msgstr "アプリケーション プロトコル"
+
+msgid "Backup"
+msgstr "バックアップ"
+
+msgid "Bandwidth Monitor"
+msgstr "帯域幅モニター"
+
+msgid "CSV, grouped by IP"
+msgstr "CSV(IP によるグループ化)"
+
+msgid "CSV, grouped by MAC"
+msgstr "CSV(MAC によるグループ化)"
+
+msgid "CSV, grouped by protocol"
+msgstr "CSV(プロトコルによるグループ化)"
+
+msgid ""
+"Changing the accounting interval type will invalidate existing databases!"
+"<br /><strong><a href=\"%s\">Download backup</a></strong>."
+msgstr ""
+"既存のデータベースと互換性の無い収集期間の形式が選択されました。<br /"
+"><strong><a href=\"%s\">バックアップのダウンロード</a></strong>"
+
+msgid ""
+"Choose \"Day of month\" to restart the accounting period monthly on a "
+"specific date, e.g. every 3rd. Choose \"Fixed interval\" to restart the "
+"accounting period exactly every N days, beginning at a given date."
+msgstr ""
+"月毎で設定した日付からのデータの計測を行うには、 \"月間\" を選択します(例: "
+"毎月3日)。設定した日数毎にデータの収集を行うには、\"特定の間隔\" を選択しま"
+"す。後者の場合、指定された日付から開始されます。"
+
+msgid "Commit interval"
+msgstr "コミット間隔"
+
+msgid "Compress database"
+msgstr "データベースの圧縮"
+
+msgid "Configuration"
+msgstr "設定"
+
+msgid "Conn."
+msgstr "接続数"
+
+msgid "Connections"
+msgstr "接続数"
+
+msgid "Connections / Host"
+msgstr "ホスト毎の接続数"
+
+msgid "Database directory"
+msgstr "データベース ディレクトリ"
+
+msgid ""
+"Database storage directory. One file per accounting period will be placed "
+"into this directory."
+msgstr ""
+"データベースの保存先ディレクトリです。計測期間あたり 1 つのファイルがこのディ"
+"レクトリに配置されます。"
+
+msgid "Day of month"
+msgstr "月間"
+
+msgid ""
+"Day of month to restart the accounting period. Use negative values to count "
+"towards the end of month, e.g. \"-5\" to specify the 27th of July or the "
+"24th of Februrary."
+msgstr ""
+"月の中で新たな収集期間を開始する日です。月の最終日からの日数をマイナス値で指"
+"定することができます(例: 7月27日または2月24日は \"-5\")。"
+
+msgid "Display"
+msgstr "表示"
+
+msgid "Down. (Bytes / Pkts.)"
+msgstr "ダウンロード(Bytes / Pkts.)"
+
+msgid "Download (Bytes / Packets)"
+msgstr "ダウンロード(Bytes / Packets)"
+
+msgid "Download / Application"
+msgstr "ダウンロード / アプリケーション"
+
+msgid "Download Database Backup"
+msgstr "データベース バックアップのダウンロード"
+
+msgid "Dualstack enabled hosts"
+msgstr "デュアルスタック ホスト"
+
+msgid "Due date"
+msgstr "期日"
+
+msgid "Export"
+msgstr "エクスポート"
+
+msgid "Family"
+msgstr "IP 種別"
+
+msgid "Fixed interval"
+msgstr "特定の間隔"
+
+msgid "Force reload…"
+msgstr "強制リロード..."
+
+msgid "General Settings"
+msgstr "全般設定"
+
+msgid "Generate Backup"
+msgstr "バックアップの作成"
+
+msgid "Host"
+msgstr "ホスト"
+
+msgid "Hostname: <big id=\"bubble-hostname\">example.org</big>"
+msgstr "ホスト名: <big id=\"bubble-hostname\">example.org</big>"
+
+msgid "IPv4 vs. IPv6"
+msgstr "IPv4 及び IPv6"
+
+msgid "IPv6"
+msgstr "IPv6"
+
+msgid "Interval"
+msgstr "間隔"
+
+msgid ""
+"Interval at which the temporary in-memory database is committed to the "
+"persistent database directory."
+msgstr ""
+"メモリー上の一時的なデータベースから、永続的なデータベース ディレクトリへのコ"
+"ミットを実行する間隔です。"
+
+msgid ""
+"Interval at which traffic counters of still established connections are "
+"refreshed from netlink information."
+msgstr ""
+
+msgid "Invalid or empty backup archive"
+msgstr "無効または空のバックアップ アーカイブです。"
+
+msgid "JSON dump"
+msgstr "JSON ダンプ"
+
+msgid "Length of accounting interval in days."
+msgstr "収集期間の日数です。"
+
+msgid "Local interfaces"
+msgstr "ローカル インターフェース"
+
+msgid "Local subnets"
+msgstr "ローカル サブネット"
+
+msgid "MAC"
+msgstr "MAC"
+
+msgid "Maximum entries"
+msgstr "最大件数"
+
+msgid ""
+"Maximum number of accounting periods to keep, use zero to keep databases "
+"forever."
+msgstr ""
+"計測データを保持する、収集期間の最大個数です。 '0' を設定した場合、全データを"
+"保持します。"
+
+msgid "Netlink Bandwidth Monitor"
+msgstr "Netlink Bandwidth Monitor"
+
+msgid "Netlink Bandwidth Monitor - Backup / Restore"
+msgstr "Netlink Bandwidth Monitor - バックアップ / 復元"
+
+msgid "Netlink Bandwidth Monitor - Configuration"
+msgstr "Netlink Bandwidth Monitor - 設定"
+
+msgid "No data recorded yet."
+msgstr "まだデータがありません。"
+
+msgid "Only conntrack streams from or to any of these networks are counted."
+msgstr ""
+"選択されたネットワークにおける conntrack ストリームのみが計測されます。"
+
+msgid "Only conntrack streams from or to any of these subnets are counted."
+msgstr "設定されたサブネットにおける conntrack ストリームのみが計測されます。"
+
+msgid "Preallocate database"
+msgstr "データベースの事前割当"
+
+msgid "Protocol"
+msgstr "プロトコル"
+
+msgid "Protocol Mapping"
+msgstr "プロトコル マッピング"
+
+msgid ""
+"Protocol mappings to distinguish traffic types per host, one mapping per "
+"line. The first value specifies the IP protocol, the second value the port "
+"number and the third column is the name of the mapped protocol."
+msgstr ""
+"ホスト毎のトラフィック形式を区別するためのプロトコル マッピングで、一行あたり"
+"一つのマッピングを追加します。各エントリーの一つ目の値は IP プロトコルを、2つ"
+"目の値はポート番号、3つ目はマッピングされたプロトコルの名前をそれぞれ表しま"
+"す。"
+
+msgid "Refresh interval"
+msgstr "リフレッシュ間隔"
+
+msgid "Restore"
+msgstr "復元"
+
+msgid "Restore Database Backup"
+msgstr "データベースの復元"
+
+msgid "Select accounting period:"
+msgstr "収集期間を選択:"
+
+msgid "Source IP"
+msgstr "アクセス元 IP"
+
+msgid "Start date"
+msgstr "開始日"
+
+msgid "Start date of the first accounting period, e.g. begin of ISP contract."
+msgstr "初回のデータ収集の開始日です(例: ISP 契約の開始日)。"
+
+msgid "Stored periods"
+msgstr "保存期間"
+
+msgid ""
+"The Netlink Bandwidth Monitor (nlbwmon) is a lightweight, efficient traffic "
+"accounting program keeping track of bandwidth usage per host and protocol."
+msgstr ""
+"Netlink Bandwidth Monitor (nlbwmon) は、軽量かつ、ホストやプロトコル毎に帯域"
+"幅使用量の追跡を行う効率的なトラフィック計測プログラムです。"
+
+msgid "The following database files have been restored: %s"
+msgstr "次のデータベース ファイルが復元されました: %s"
+
+msgid ""
+"The maximum amount of entries that should be put into the database, setting "
+"the limit to 0 will allow databases to grow indefinitely."
+msgstr ""
+"データベースに保管される最大件数です。 '0' を設定した場合、制限無しのデータ"
+"ベースの増大を許可します。"
+
+msgid "Traffic / Host"
+msgstr "トラフィック / ホスト"
+
+msgid "Traffic Distribution"
+msgstr "トラフィック内訳"
+
+msgid "Up. (Bytes / Pkts.)"
+msgstr "アップロード(Bytes / Pkts.)"
+
+msgid "Upload (Bytes / Packets)"
+msgstr "アップロード(Bytes / Packets)"
+
+msgid "Upload / Application"
+msgstr "アップロード / アプリケーション"
+
+msgid "Vendor: <big id=\"bubble-vendor\">Example Corp.</big>"
+msgstr "ベンダ: <big id=\"bubble-vendor\">Example Corp.</big>"
+
+msgid "Warning"
+msgstr "警告"
+
+msgid ""
+"Whether to gzip compress archive databases. Compressing the database files "
+"makes accessing old data slightly slower but helps to reduce storage "
+"requirements."
+msgstr ""
+"データベースの gzip 圧縮アーカイブ化です。データベース ファイルを圧縮すると古"
+"いデータへのアクセスが多少遅くなりますが、ストレージ使用量の低減に役立ちま"
+"す。"
+
+msgid ""
+"Whether to preallocate the maximum possible database size in memory. This is "
+"mainly useful for memory constrained systems which might not be able to "
+"satisfy memory allocation after longer uptime periods."
+msgstr ""
+
+msgid "no traffic"
+msgstr "トラフィック無し"
+
+msgid "other"
+msgstr "その他"
diff --git a/applications/luci-app-nlbwmon/po/templates/nlbwmon.pot b/applications/luci-app-nlbwmon/po/templates/nlbwmon.pot
new file mode 100644 (file)
index 0000000..61d2230
--- /dev/null
@@ -0,0 +1,352 @@
+msgid ""
+msgstr "Content-Type: text/plain; charset=UTF-8"
+
+msgid "%d IPv4-only hosts"
+msgstr ""
+
+msgid "%d IPv6-only hosts"
+msgstr ""
+
+msgid "%d dual-stack hosts"
+msgstr ""
+
+msgid "%s and %s"
+msgstr ""
+
+msgid "%s, %s and %s"
+msgstr ""
+
+msgid "-1 - Restart every last day of month"
+msgstr ""
+
+msgid "-7 - Restart a week before end of month"
+msgstr ""
+
+msgid "1 - Restart every 1st of month"
+msgstr ""
+
+msgid "10m - frequent commits at the expense of flash wear"
+msgstr ""
+
+msgid "12h - compromise between risk of data loss and flash wear"
+msgstr ""
+
+msgid "24h - least flash wear at the expense of data loss risk"
+msgstr ""
+
+msgid "30s - refresh twice per minute for reasonably current stats"
+msgstr ""
+
+msgid "5m - rarely refresh to avoid frequently clearing conntrack counters"
+msgstr ""
+
+msgid "60s - commit minutely, useful for non-flash storage"
+msgstr ""
+
+msgid "<big id=\"conn-total\">0</big> connections"
+msgstr ""
+
+msgid "<big id=\"host-total\">0</big> hosts"
+msgstr ""
+
+msgid "<big id=\"ipv6-hosts\">0%</big> IPv6 support rate among hosts"
+msgstr ""
+
+msgid "<big id=\"ipv6-rx\">0B</big> total IPv6 download"
+msgstr ""
+
+msgid "<big id=\"ipv6-share\">0%</big> of the total traffic is IPv6"
+msgstr ""
+
+msgid "<big id=\"ipv6-tx\">0B</big> total IPv6 upload"
+msgstr ""
+
+msgid "<big id=\"layer7-most-conn\">0</big> cause the most connections"
+msgstr ""
+
+msgid "<big id=\"layer7-most-rx\">0</big> cause the most download"
+msgstr ""
+
+msgid "<big id=\"layer7-most-tx\">0</big> cause the most upload"
+msgstr ""
+
+msgid "<big id=\"layer7-total\">0</big> different application protocols"
+msgstr ""
+
+msgid "<big id=\"rx-total\">0</big> download"
+msgstr ""
+
+msgid "<big id=\"tx-total\">0</big> upload"
+msgstr ""
+
+msgid "Accounting period"
+msgstr ""
+
+msgid "Advanced Settings"
+msgstr ""
+
+msgid "Application"
+msgstr ""
+
+msgid "Application Protocols"
+msgstr ""
+
+msgid "Backup"
+msgstr ""
+
+msgid "Bandwidth Monitor"
+msgstr ""
+
+msgid "CSV, grouped by IP"
+msgstr ""
+
+msgid "CSV, grouped by MAC"
+msgstr ""
+
+msgid "CSV, grouped by protocol"
+msgstr ""
+
+msgid ""
+"Changing the accounting interval type will invalidate existing databases!"
+"<br /><strong><a href=\"%s\">Download backup</a></strong>."
+msgstr ""
+
+msgid ""
+"Choose \"Day of month\" to restart the accounting period monthly on a "
+"specific date, e.g. every 3rd. Choose \"Fixed interval\" to restart the "
+"accounting period exactly every N days, beginning at a given date."
+msgstr ""
+
+msgid "Commit interval"
+msgstr ""
+
+msgid "Compress database"
+msgstr ""
+
+msgid "Configuration"
+msgstr ""
+
+msgid "Conn."
+msgstr ""
+
+msgid "Connections"
+msgstr ""
+
+msgid "Connections / Host"
+msgstr ""
+
+msgid "Database directory"
+msgstr ""
+
+msgid ""
+"Database storage directory. One file per accounting period will be placed "
+"into this directory."
+msgstr ""
+
+msgid "Day of month"
+msgstr ""
+
+msgid ""
+"Day of month to restart the accounting period. Use negative values to count "
+"towards the end of month, e.g. \"-5\" to specify the 27th of July or the "
+"24th of Februrary."
+msgstr ""
+
+msgid "Display"
+msgstr ""
+
+msgid "Down. (Bytes / Pkts.)"
+msgstr ""
+
+msgid "Download (Bytes / Packets)"
+msgstr ""
+
+msgid "Download / Application"
+msgstr ""
+
+msgid "Download Database Backup"
+msgstr ""
+
+msgid "Dualstack enabled hosts"
+msgstr ""
+
+msgid "Due date"
+msgstr ""
+
+msgid "Export"
+msgstr ""
+
+msgid "Family"
+msgstr ""
+
+msgid "Fixed interval"
+msgstr ""
+
+msgid "Force reload…"
+msgstr ""
+
+msgid "General Settings"
+msgstr ""
+
+msgid "Generate Backup"
+msgstr ""
+
+msgid "Host"
+msgstr ""
+
+msgid "Hostname: <big id=\"bubble-hostname\">example.org</big>"
+msgstr ""
+
+msgid "IPv4 vs. IPv6"
+msgstr ""
+
+msgid "IPv6"
+msgstr ""
+
+msgid "Interval"
+msgstr ""
+
+msgid ""
+"Interval at which the temporary in-memory database is committed to the "
+"persistent database directory."
+msgstr ""
+
+msgid ""
+"Interval at which traffic counters of still established connections are "
+"refreshed from netlink information."
+msgstr ""
+
+msgid "Invalid or empty backup archive"
+msgstr ""
+
+msgid "JSON dump"
+msgstr ""
+
+msgid "Length of accounting interval in days."
+msgstr ""
+
+msgid "Local interfaces"
+msgstr ""
+
+msgid "Local subnets"
+msgstr ""
+
+msgid "MAC"
+msgstr ""
+
+msgid "Maximum entries"
+msgstr ""
+
+msgid ""
+"Maximum number of accounting periods to keep, use zero to keep databases "
+"forever."
+msgstr ""
+
+msgid "Netlink Bandwidth Monitor"
+msgstr ""
+
+msgid "Netlink Bandwidth Monitor - Backup / Restore"
+msgstr ""
+
+msgid "Netlink Bandwidth Monitor - Configuration"
+msgstr ""
+
+msgid "No data recorded yet."
+msgstr ""
+
+msgid "Only conntrack streams from or to any of these networks are counted."
+msgstr ""
+
+msgid "Only conntrack streams from or to any of these subnets are counted."
+msgstr ""
+
+msgid "Preallocate database"
+msgstr ""
+
+msgid "Protocol"
+msgstr ""
+
+msgid "Protocol Mapping"
+msgstr ""
+
+msgid ""
+"Protocol mappings to distinguish traffic types per host, one mapping per "
+"line. The first value specifies the IP protocol, the second value the port "
+"number and the third column is the name of the mapped protocol."
+msgstr ""
+
+msgid "Refresh interval"
+msgstr ""
+
+msgid "Restore"
+msgstr ""
+
+msgid "Restore Database Backup"
+msgstr ""
+
+msgid "Select accounting period:"
+msgstr ""
+
+msgid "Source IP"
+msgstr ""
+
+msgid "Start date"
+msgstr ""
+
+msgid "Start date of the first accounting period, e.g. begin of ISP contract."
+msgstr ""
+
+msgid "Stored periods"
+msgstr ""
+
+msgid ""
+"The Netlink Bandwidth Monitor (nlbwmon) is a lightweight, efficient traffic "
+"accounting program keeping track of bandwidth usage per host and protocol."
+msgstr ""
+
+msgid "The following database files have been restored: %s"
+msgstr ""
+
+msgid ""
+"The maximum amount of entries that should be put into the database, setting "
+"the limit to 0 will allow databases to grow indefinitely."
+msgstr ""
+
+msgid "Traffic / Host"
+msgstr ""
+
+msgid "Traffic Distribution"
+msgstr ""
+
+msgid "Up. (Bytes / Pkts.)"
+msgstr ""
+
+msgid "Upload (Bytes / Packets)"
+msgstr ""
+
+msgid "Upload / Application"
+msgstr ""
+
+msgid "Vendor: <big id=\"bubble-vendor\">Example Corp.</big>"
+msgstr ""
+
+msgid "Warning"
+msgstr ""
+
+msgid ""
+"Whether to gzip compress archive databases. Compressing the database files "
+"makes accessing old data slightly slower but helps to reduce storage "
+"requirements."
+msgstr ""
+
+msgid ""
+"Whether to preallocate the maximum possible database size in memory. This is "
+"mainly useful for memory constrained systems which might not be able to "
+"satisfy memory allocation after longer uptime periods."
+msgstr ""
+
+msgid "no traffic"
+msgstr ""
+
+msgid "other"
+msgstr ""
diff --git a/applications/luci-app-nlbwmon/root/etc/uci-defaults/40_luci-nlbwmon b/applications/luci-app-nlbwmon/root/etc/uci-defaults/40_luci-nlbwmon
new file mode 100644 (file)
index 0000000..c977177
--- /dev/null
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+uci -q batch <<-EOF >/dev/null
+       delete ucitrack.@nlbwmon[-1]
+       add ucitrack nlbwmon
+       set ucitrack.@nlbwmon[-1].init=nlbwmon
+       commit ucitrack
+EOF
+
+rm -f /tmp/luci-indexcache
+exit 0
index 848a5c8..d0923e0 100644 (file)
@@ -1,14 +1,16 @@
 #
-# Copyright (C) 2008-2014 The LuCI Team <luci@lists.subsignal.org>
+# Copyright (C) 2017 Yousong Zhou <yszhou4tech@gmail.com>
 #
 # This is free software, licensed under the Apache License, Version 2.0 .
 #
 
 include $(TOPDIR)/rules.mk
 
-LUCI_TITLE:=LuCI Support for Shadowsocks-libev
+LUCI_TITLE:=LuCI Support for shadowsocks-libev
 LUCI_DEPENDS:=
 
+PKG_LICENSE:=Apache-2.0
+
 include ../../luci.mk
 
 # call BuildPackage - OpenWrt buildroot signature
index ae96816..05d12e3 100644 (file)
@@ -1,12 +1,33 @@
--- Copyright 2015 Jian Chang <aa65535@live.com>
+-- Copyright 2017 Yousong Zhou <yszhou4tech@gmail.com>
 -- Licensed to the public under the Apache License 2.0.
-
+--
 module("luci.controller.shadowsocks-libev", package.seeall)
 
 function index()
-       if not nixio.fs.access("/etc/config/shadowsocks-libev") then
-               return
-       end
+       entry({"admin", "services", "shadowsocks-libev"},
+               alias("admin", "services", "shadowsocks-libev", "instances"),
+               _("Shadowsocks-libev"), 59)
+
+       entry({"admin", "services", "shadowsocks-libev", "instances"},
+               arcombine(cbi("shadowsocks-libev/instances"), cbi("shadowsocks-libev/instance-details")),
+               _("Local Instances"), 10).leaf = true
+
+       entry({"admin", "services", "shadowsocks-libev", "servers"},
+               cbi("shadowsocks-libev/servers"),
+               _("Remote Servers"), 20).leaf = true
+
+       entry({"admin", "services", "shadowsocks-libev", "rules"},
+               cbi("shadowsocks-libev/rules"),
+               _("Redir Rules"), 30).leaf = true
+
+       entry({"admin", "services", "shadowsocks-libev", "status"}, call("ss_status"), nil).leaf = true
+
+end
+
+function ss_status()
+       local ut = require "luci.util"
+       local rv = ut.ubus("service", "list", {name = "shadowsocks-libev"})["shadowsocks-libev"] or {_=0}
 
-       entry({"admin", "services", "shadowsocks-libev"}, cbi("shadowsocks-libev"), _("ShadowSocks-libev"), 74).dependent = true
+       luci.http.prepare_content("application/json")
+       luci.http.write_json(rv)
 end
diff --git a/applications/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev.lua b/applications/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev.lua
deleted file mode 100644 (file)
index 97ce83f..0000000
+++ /dev/null
@@ -1,157 +0,0 @@
--- Copyright 2015 Jian Chang <aa65535@live.com>
--- Licensed to the public under the Apache License 2.0.
-
-local m, s, o, e, a
-
-if luci.sys.call("pidof ss-redir >/dev/null") == 0 then
-       m = Map("shadowsocks-libev", translate("ShadowSocks-libev"), translate("ShadowSocks-libev is running"))
-else
-       m = Map("shadowsocks-libev", translate("ShadowSocks-libev"), translate("ShadowSocks-libev is not running"))
-end
-
-e = {
-       "table",
-       "rc4",
-       "rc4-md5",
-       "aes-128-cfb",
-       "aes-192-cfb",
-       "aes-256-cfb",
-       "bf-cfb",
-       "camellia-128-cfb",
-       "camellia-192-cfb",
-       "camellia-256-cfb",
-       "cast5-cfb",
-       "des-cfb",
-       "idea-cfb",
-       "rc2-cfb",
-       "seed-cfb",
-       "salsa20",
-       "chacha20",
-}
-
--- Global Setting
-s = m:section(TypedSection, "shadowsocks-libev", translate("Global Setting"))
-s.anonymous = true
-
-o = s:option(Flag, "enable", translate("Enable"))
-o.default = 1
-o.rmempty = false
-
-o = s:option(Value, "server", translate("Server Address"))
-o.datatype = "ipaddr"
-o.rmempty = false
-
-o = s:option(Value, "server_port", translate("Server Port"))
-o.datatype = "port"
-o.rmempty = false
-
-o = s:option(Value, "local_port", translate("Local Port"))
-o.datatype = "port"
-o.default = 1080
-o.rmempty = false
-
-o = s:option(Value, "timeout", translate("Connection Timeout"))
-o.datatype = "uinteger"
-o.default = 60
-o.rmempty = false
-
-o = s:option(Value, "password", translate("Password"))
-o.password = true
-o.rmempty = false
-
-o = s:option(ListValue, "encrypt_method", translate("Encrypt Method"))
-for i,v in ipairs(e) do
-       o:value(v)
-end
-o.rmempty = false
-
-o = s:option(Value, "ignore_list", translate("Ignore List"))
-o:value("/dev/null", translate("Disabled"))
-o.default = "/dev/null"
-o.rmempty = false
-
--- UDP Relay
-s = m:section(TypedSection, "shadowsocks-libev", translate("UDP Relay"))
-s.anonymous = true
-
-o = s:option(ListValue, "udp_mode", translate("Relay Mode"))
-o:value("0", translate("Disabled"))
-o:value("1", translate("Enabled"))
-o:value("2", translate("Custom"))
-o.default = 0
-o.rmempty = false
-
-o = s:option(Value, "udp_server", translate("Server Address"))
-o.datatype = "ipaddr"
-o:depends("udp_mode", 2)
-
-o = s:option(Value, "udp_server_port", translate("Server Port"))
-o.datatype = "port"
-o:depends("udp_mode", 2)
-
-o = s:option(Value, "udp_local_port", translate("Local Port"))
-o.datatype = "port"
-o.default = 1081
-o:depends("udp_mode", 2)
-
-o = s:option(Value, "udp_timeout", translate("Connection Timeout"))
-o.datatype = "uinteger"
-o.default = 60
-o:depends("udp_mode", 2)
-
-o = s:option(Value, "udp_password", translate("Password"))
-o.password = true
-o:depends("udp_mode", 2)
-
-o = s:option(ListValue, "udp_encrypt_method", translate("Encrypt Method"))
-for i,v in ipairs(e) do
-       o:value(v)
-end
-o:depends("udp_mode", 2)
-
--- UDP Forward
-s = m:section(TypedSection, "shadowsocks-libev", translate("UDP Forward"))
-s.anonymous = true
-
-o = s:option(Flag, "tunnel_enable", translate("Enable"))
-o.default = 1
-o.rmempty = false
-
-o = s:option(Value, "tunnel_port", translate("UDP Local Port"))
-o.datatype = "port"
-o.default = 5300
-
-o = s:option(Value, "tunnel_forward", translate("Forwarding Tunnel"))
-o.default = "8.8.4.4:53"
-
--- Access Control
-s = m:section(TypedSection, "shadowsocks-libev", translate("Access Control"))
-s.anonymous = true
-
-s:tab("lan_ac", translate("LAN"))
-
-o = s:taboption("lan_ac", ListValue, "lan_ac_mode", translate("Access Control"))
-o:value("0", translate("Disabled"))
-o:value("1", translate("Allow listed only"))
-o:value("2", translate("Allow all except listed"))
-o.default = 0
-o.rmempty = false
-
-o = s:taboption("lan_ac", DynamicList, "lan_ac_ip", translate("LAN IP List"))
-o.datatype = "ipaddr"
-
-luci.ip.neighbors({ family = 4 }, function(entry)
-       if entry.reachable then
-               o:value(entry.dest:string())
-       end
-end)
-
-s:tab("wan_ac", translate("WAN"))
-
-o = s:taboption("wan_ac", DynamicList, "wan_bp_ip", translate("Bypassed IP"))
-o.datatype = "ip4addr"
-
-o = s:taboption("wan_ac", DynamicList, "wan_fw_ip", translate("Forwarded IP"))
-o.datatype = "ip4addr"
-
-return m
diff --git a/applications/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev/instance-details.lua b/applications/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev/instance-details.lua
new file mode 100644 (file)
index 0000000..d9a61d0
--- /dev/null
@@ -0,0 +1,49 @@
+-- Copyright 2017 Yousong Zhou <yszhou4tech@gmail.com>
+-- Licensed to the public under the Apache License 2.0.
+
+local ds = require "luci.dispatcher"
+local ss = require "luci.model.shadowsocks-libev"
+
+local sname = arg[1]
+local redirect_url = ds.build_url("admin/services/shadowsocks-libev/instances")
+local s, o
+
+local m = Map("shadowsocks-libev")
+local sdata = m:get(sname)
+if not sdata then
+       luci.http.redirect(redirect_url)
+       return
+end
+local stype = sdata[".type"]
+m.redirect = redirect_url
+m.title = "shadowsocks-libev - %s - %s" % {stype, sname}
+
+
+s = m:section(NamedSection, sname, stype)
+s:tab("general", translate("General Settings"))
+s:tab("advanced", translate("Advanced Settings"))
+s:taboption("general", Flag, "disabled", translate("Disable"))
+ss.option_install_package(s, "general")
+
+if stype == "ss_server" then
+       ss.options_server(s, "general")
+       o = s:taboption("general", Value, "bind_address",
+               translate("Bind address"),
+               translate("The address ss-server will initiate connection from"))
+       o.datatype = "ipaddr"
+       o.placeholder = "0.0.0.0"
+       ss.values_ipaddr(o)
+       o = s:taboption("general", Value, "manager_address", translate("Manager address"))
+       o.datatype = "hostport"
+else
+       ss.options_client(s, "general")
+       if stype == "ss_tunnel" then
+               o = s:taboption("general", Value, "tunnel_address",
+                       translate("Tunnel address"),
+                       translate("The address ss-tunnel will forward traffic to"))
+               o.datatype = "hostport"
+       end
+end
+ss.options_common(s, "advanced")
+
+return m
diff --git a/applications/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev/instances.lua b/applications/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev/instances.lua
new file mode 100644 (file)
index 0000000..62a90fb
--- /dev/null
@@ -0,0 +1,104 @@
+-- Copyright 2017 Yousong Zhou <yszhou4tech@gmail.com>
+-- Licensed to the public under the Apache License 2.0.
+
+local ds = require "luci.dispatcher"
+local ss = require "luci.model.shadowsocks-libev"
+local ut = require "luci.util"
+local m, s, o
+
+m = Map("shadowsocks-libev",
+       translate("Local Instances"),
+       translate("Instances of shadowsocks-libev components, e.g. ss-local, \
+                          ss-redir, ss-tunnel, ss-server, etc.  To enable an instance it \
+                          is required to enable both the instance itself and the remote \
+                          server it refers to."))
+
+local instances = {}
+local cfgtypes = { "ss_local", "ss_redir", "ss_server", "ss_tunnel" }
+
+for sname, sdata in pairs(m:get()) do
+       local key, value = ss.cfgvalue_overview(sdata)
+       if key ~= nil then
+               instances[key] = value
+       end
+end
+
+s = m:section(Table, instances)
+s.addremove = true
+s.template_addremove = "shadowsocks-libev/add_instance"
+s.extedit = function(self, section)
+       local value = instances[section]
+       if type(value) == "table" then
+               return ds.build_url(unpack(ds.context.requestpath),
+                                       "services/shadowsocks-libev/instances",
+                                       value[".name"])
+       end
+end
+s.parse = function(self, ...)
+       Table.parse(self, ...)
+
+       local crval = REMOVE_PREFIX .. self.config
+       local name = self.map:formvaluetable(crval)
+       for k,v in pairs(name) do
+               local value = instances[k]
+               local sname = value[".name"]
+               if type(value) == "table" then
+                       m:del(sname)
+                       instances[k] = nil
+                       for _, oname in ipairs({"redir_tcp", "redir_udp"}) do
+                               local ovalue = m:get("ss_rules", oname)
+                               if ovalue == sname then
+                                       m:del("ss_rules", oname)
+                               end
+                       end
+               end
+       end
+
+       local stype = m:formvalue("_newinst.type")
+       local sname = m:formvalue("_newinst.name")
+       if ut.contains(cfgtypes, stype) then
+               local created
+               if sname and #sname > 0 then
+                       created = m:set(sname, nil, stype)
+               else
+                       created = m:add(stype)
+                       sname = created
+               end
+               if created then
+                       m.uci:save("shadowsocks-libev")
+                       luci.http.redirect(ds.build_url(
+                               "admin/services/shadowsocks-libev/instances", sname
+                       ))
+               end
+       end
+end
+
+o = s:option(DummyValue, "name", translate("Name"))
+o.rawhtml = true
+o = s:option(DummyValue, "overview", translate("Overview"))
+o.rawhtml = true
+
+s:option(DummyValue, "running", translate("Running"))
+
+o = s:option(Button, "disabled", translate("Enable/Disable"))
+o.render = function(self, section, scope)
+       if instances[section].disabled then
+               self.title = translate("Disabled")
+               self.inputstyle = "reset"
+       else
+               self.title = translate("Enabled")
+               self.inputstyle = "save"
+       end
+       Button.render(self, section, scope)
+end
+o.write = function(self, section)
+       local sdata = instances[section]
+       if type(sdata) == "table" then
+               local sname = sdata[".name"]
+               local disabled = not sdata["disabled"]
+               sdata["disabled"] = disabled
+               m:set(sname, "disabled", tostring(disabled))
+       end
+end
+
+return m
diff --git a/applications/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev/rules.lua b/applications/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev/rules.lua
new file mode 100644 (file)
index 0000000..fe5f9c3
--- /dev/null
@@ -0,0 +1,73 @@
+-- Copyright 2017 Yousong Zhou <yszhou4tech@gmail.com>
+-- Licensed to the public under the Apache License 2.0.
+
+local ss = require("luci.model.shadowsocks-libev")
+
+local m, s, o
+
+m = Map("shadowsocks-libev",
+       translate("Redir Rules"),
+       translate("On this page you can configure how traffics are to be \
+               forwarded to ss-redir instances. \
+               If enabled, packets will first have their source ip addresses checked \
+               against <em>Src ip bypass</em>, <em>Src ip forward</em>, \
+               <em>Src ip checkdst</em> and if none matches <em>Src default</em> \
+               will give the default action to be taken. \
+               If the prior check results in action <em>checkdst</em>, packets will continue \
+               to have their destination addresses checked."))
+
+
+s = m:section(NamedSection, "ss_rules", "ss-rules")
+s:tab("general", translate("General Settings"))
+s:tab("srcip", translate("Source Settings"))
+s:tab("dstip", translate("Destination Settings"))
+
+s:taboption('general', Flag, "disabled", translate("Disable"))
+ss.option_install_package(s, 'general')
+
+o = s:taboption('general', ListValue, "redir_tcp",
+       translate("ss-redir for TCP"))
+ss.values_redir(o, 'tcp')
+o = s:taboption('general', ListValue, "redir_udp",
+       translate("ss-redir for UDP"))
+ss.values_redir(o, 'udp')
+
+o = s:taboption('general', ListValue, "local_default",
+       translate("Local-out default"),
+       translate("Default action for locally generated packets"))
+ss.values_actions(o)
+s:taboption('general', Value, "ipt_args",
+       translate("Extra arguments"),
+       translate("Passes additional arguments to iptables. Use with care!"))
+
+s:taboption('srcip', DynamicList, "src_ips_bypass",
+       translate("Src ip bypass"),
+       translate("Bypass redir action for packets with source addresses in this list"))
+s:taboption('srcip', DynamicList, "src_ips_forward",
+       translate("Src ip forward"),
+       translate("Go through redir action for packets with source addresses in this list"))
+s:taboption('srcip', DynamicList, "src_ips_checkdst",
+       translate("Src ip checkdst"),
+       translate("Continue to have dst address checked for packets with source addresses in this list"))
+o = s:taboption('srcip', ListValue, "src_default",
+       translate("Src default"),
+       translate("Default action for packets whose source addresses do not match any of the source ip list"))
+ss.values_actions(o)
+
+s:taboption('dstip', DynamicList, "dst_ips_bypass",
+       translate("Dst ip bypass"),
+       translate("Bypass redir action for packets with destination addresses in this list"))
+s:taboption('dstip', DynamicList, "dst_ips_forward",
+       translate("Dst ip forward"),
+       translate("Go through redir action for packets with destination addresses in this list"))
+
+o = s:taboption('dstip', FileBrowser, "dst_ips_bypass_file",
+       translate("Dst ip bypass file"),
+       translate("File containing ip addresses for the purposes as with <em>Dst ip bypass</em>"))
+o.datatype = "file"
+s:taboption('dstip', FileBrowser, "dst_ips_forward_file",
+       translate("Dst ip forward file"),
+       translate("File containing ip addresses for the purposes as with <em>Dst ip forward</em>"))
+o.datatype = "file"
+
+return m
diff --git a/applications/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev/servers.lua b/applications/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev/servers.lua
new file mode 100644 (file)
index 0000000..71c6656
--- /dev/null
@@ -0,0 +1,31 @@
+-- Copyright 2017 Yousong Zhou <yszhou4tech@gmail.com>
+-- Licensed to the public under the Apache License 2.0.
+
+local ds = require "luci.dispatcher"
+local ss = require("luci.model.shadowsocks-libev")
+
+local m, s
+
+m = Map("shadowsocks-libev",
+       translate("Remote Servers"),
+       translate("Definition of remote shadowsocks servers.  \
+                       Disable any of them will also disable instances refering to it."))
+
+local sname = arg[1]
+if sname then
+       if not m:get(sname) then
+               luci.http.redirect(ds.build_url("admin/services/shadowsocks-libev/servers"))
+               return
+       end
+       s = m:section(NamedSection, sname, "server")
+       m.title = m.title .. ' - ' .. sname
+else
+       s = m:section(TypedSection, "server")
+       s.template = 'cbi/tblsection'
+       s.addremove = true
+end
+
+s:option(Flag, "disabled", translate("Disable"))
+ss.options_server(s)
+
+return m
diff --git a/applications/luci-app-shadowsocks-libev/luasrc/model/shadowsocks-libev.lua b/applications/luci-app-shadowsocks-libev/luasrc/model/shadowsocks-libev.lua
new file mode 100644 (file)
index 0000000..b11890f
--- /dev/null
@@ -0,0 +1,252 @@
+-- Copyright 2017 Yousong Zhou <yszhou4tech@gmail.com>
+-- Licensed to the public under the Apache License 2.0.
+
+local _up = getfenv(3)
+local ut = require("luci.util")
+local ds = require("luci.dispatcher")
+local nw = require("luci.model.network")
+nw.init()
+module("luci.model.shadowsocks-libev", function(m)
+       setmetatable(m, {__index=function (self, k)
+               local tb = _up
+               return rawget(self, k) or _up[k]
+       end})
+end)
+
+function values_actions(o)
+       for _, a in ipairs(actions) do
+               o:value(a)
+       end
+end
+
+function values_redir(o, xmode)
+       o.map.uci.foreach("shadowsocks-libev", "ss_redir", function(sdata)
+               local sname = sdata[".name"]
+               local mode = sdata["mode"]
+               if mode and mode:find(xmode) then
+                       local desc = "%s - %s" % {sname, mode}
+                       o:value(sname, desc)
+               end
+       end)
+end
+
+function values_serverlist(o)
+       o.map.uci.foreach("shadowsocks-libev", "server", function(sdata)
+               local sname = sdata[".name"]
+               local server = sdata["server"]
+               local server_port = sdata["server_port"]
+               if server and server_port then
+                       local desc = "%s - %s:%s" % {sname, sdata["server"], sdata["server_port"]}
+                       o:value(sname, desc)
+               end
+       end)
+end
+
+function values_ipaddr(o)
+       local keys, vals = {}, {}
+       for _, v in ipairs(nw:get_interfaces()) do
+               for _, a in ipairs(v:ipaddrs()) do
+                       o:value(a:host():string(), '%s (%s)' %{ a:host(), v:shortname() })
+               end
+       end
+end
+
+function options_client(s, tab)
+       local o
+
+       o = s:taboption(tab, ListValue, "server", translate("Remote server"))
+       values_serverlist(o)
+       o = s:taboption(tab, Value, "local_address", translate("Local address"))
+       o.datatype = "ipaddr"
+       o.placeholder = "0.0.0.0"
+       values_ipaddr(o)
+       o = s:taboption(tab, Value, "local_port", translate("Local port"))
+       o.datatype = "port"
+end
+
+function options_server(s, tab)
+       local o
+       local optfunc
+
+       if tab == nil then
+               optfunc = function(...) return s:option(...) end
+       else
+               optfunc = function(...) return s:taboption(tab, ...) end
+       end
+
+       o = optfunc(Value, "server", translate("Server"))
+       o.datatype = "host"
+       o.size = 16
+       o = optfunc(Value, "server_port", translate("Server port"))
+       o.datatype = "port"
+       o.size = 5
+       o = optfunc(ListValue, "method", translate("Method"))
+       for _, m in ipairs(methods) do
+               o:value(m)
+       end
+       o = optfunc(Value, "key", translate("Key (base64 encoding)"))
+       o.datatype = "base64"
+       o.password = true
+       o.size = 12
+       o = optfunc(Value, "password", translate("Password"))
+       o.password = true
+       o.size = 12
+end
+
+function options_common(s, tab)
+       local o
+
+       o = s:taboption(tab, ListValue, "mode", translate("Mode of operation"))
+       for _, m in ipairs(modes) do
+               o:value(m)
+       end
+       o.default = "tcp_and_udp"
+       o = s:taboption(tab, Value, "mtu", translate("MTU"))
+       o.datatype = "uinteger"
+       o = s:taboption(tab, Value, "timeout", translate("Timeout (sec)"))
+       o.datatype = "uinteger"
+       s:taboption(tab, Value, "user", translate("Run as"))
+
+       s:taboption(tab, Flag, "verbose", translate("Verbose"))
+       s:taboption(tab, Flag, "ipv6_first", translate("IPv6 First"), translate("Prefer IPv6 addresses when resolving names"))
+       s:taboption(tab, Flag, "fast_open", translate("Enable TCP Fast Open"))
+       s:taboption(tab, Flag, "reuse_port", translate("Enable SO_REUSEPORT"))
+end
+
+function ucival_to_bool(val)
+       return val == "true" or val == "1" or val == "yes" or val == "on"
+end
+
+function cfgvalue_overview(sdata)
+       local stype = sdata[".type"]
+       local lines  = {}
+
+       if stype == "ss_server" then
+               cfgvalue_overview_(sdata, lines, names_options_server)
+               cfgvalue_overview_(sdata, lines, names_options_common)
+               cfgvalue_overview_(sdata, lines, {
+                       "bind_address",
+                       "manager_address",
+               })
+       elseif stype == "ss_local" or stype == "ss_redir" or stype == "ss_tunnel" then
+               cfgvalue_overview_(sdata, lines, names_options_client)
+               if stype == "ss_tunnel" then
+                       cfgvalue_overview_(sdata, lines, {"tunnel_address"})
+               end
+               cfgvalue_overview_(sdata, lines, names_options_common)
+       else
+               return nil, nil
+       end
+       local sname = sdata[".name"]
+       local key = "%s.%s" % {stype, sname}
+       local value = {
+               [".name"] = sname,
+               name = '%s.<var>%s</var>' % {stype, sname},
+               overview = table.concat(lines, "</br>"),
+               disabled = ucival_to_bool(sdata["disabled"]),
+       }
+       return key, value
+end
+
+function cfgvalue_overview_(sdata, lines, names)
+       local line
+
+       for _, n in ipairs(names) do
+               local v = sdata[n]
+               if v ~= nil then
+                       local fv = "<var>%s</var>" % ut.pcdata(v)
+                       if sdata[".type"] ~= "ss_server" and n == "server" then
+                               fv = '<a class="label" href="%s">%s</a>' % {
+                                       ds.build_url("admin/services/shadowsocks-libev/servers", v), fv}
+                       end
+                       line = n .. ": " .. fv
+                       table.insert(lines, line)
+               end
+       end
+end
+
+function option_install_package(s, tab)
+       local bin = s.sectiontype:gsub("_", "-", 1)
+       local installed = nixio.fs.access("/usr/bin/" .. bin)
+       if installed then
+               return
+       end
+       local opkg_package = "shadowsocks-libev-" .. bin
+       local p_install
+       if tab then
+               p_install = s:taboption(tab, Button, "_install")
+       else
+               p_install = s:option(Button, "_install")
+       end
+       p_install.title      = translate("Package is not installed")
+       p_install.inputtitle = translate("Install package %q" % opkg_package)
+       p_install.inputstyle = "apply"
+
+       function p_install.write()
+               return luci.http.redirect(
+                       luci.dispatcher.build_url("admin/system/packages") ..
+                       "?submit=1&install=%s" % opkg_package
+               )
+       end
+end
+
+names_options_server = {
+       "server",
+       "server_port",
+       "method",
+       "key",
+       "password",
+}
+
+names_options_client = {
+       "server",
+       "local_address",
+       "local_port",
+}
+
+names_options_common = {
+       "verbose",
+       "ipv6_first",
+       "fast_open",
+       "reuse_port",
+       "mode",
+       "mtu",
+       "timeout",
+       "user",
+}
+
+modes = {
+       "tcp_only",
+       "tcp_and_udp",
+       "udp_only",
+}
+
+actions = {
+       "bypass",
+       "forward",
+       "checkdst",
+}
+
+methods = {
+       -- aead
+       "aes-128-gcm",
+       "aes-192-gcm",
+       "aes-256-gcm",
+       -- stream
+       "table",
+       "rc4",
+       "rc4-md5",
+       "aes-128-cfb",
+       "aes-192-cfb",
+       "aes-256-cfb",
+       "aes-128-ctr",
+       "aes-192-ctr",
+       "aes-256-ctr",
+       "bf-cfb",
+       "camellia-128-cfb",
+       "camellia-192-cfb",
+       "camellia-256-cfb",
+       "salsa20",
+       "chacha20",
+       "chacha20-ietf",
+}
diff --git a/applications/luci-app-shadowsocks-libev/luasrc/view/shadowsocks-libev/add_instance.htm b/applications/luci-app-shadowsocks-libev/luasrc/view/shadowsocks-libev/add_instance.htm
new file mode 100644 (file)
index 0000000..219d89b
--- /dev/null
@@ -0,0 +1,45 @@
+<div class="cbi-section-create cbi-tblsection-create">
+       <br />
+       <table class="cbi-section-table">
+               <tr class="cbi-section-table-row">
+                       <td class="cbi-section-table-cell" style="width:140px">
+                               <select class="cbi-input-select" id="_newinst.type" name="_newinst.type">
+                                       <option value="_dummy">-- instance type --</option>
+                                       <option value="ss_local">ss-local</option>
+                                       <option value="ss_tunnel">ss-tunnel</option>
+                                       <option value="ss_redir">ss-redir</option>
+                                       <option value="ss_server">ss-server</option>
+                               </select>
+                       </td>
+                       <td class="cbi-section-table-cell" style="width:110px">
+                               <input type="text" class="cbi-input-text" id="_newinst.name" name="_newinst.name" placeholder="<%:Name%>"/>
+                       </td>
+                       <td class="cbi-section-table-cell left">
+                               <input type="submit" class="cbi-button cbi-button-add" name="cbi.cts.<%=self.config%>" value="<%:Add%>" />
+                       </td>
+               </tr>
+       </table>
+</div>
+<script type="text/javascript">//<![CDATA[
+       XHR.poll(5, '<%=url('admin/services/shadowsocks-libev/status')%>', null,
+               function(x, st)
+               {
+                       var names = [
+                               <%-
+                                       for _, name in ipairs(self:cfgsections()) do
+                                               write("%q," % name)
+                                       end
+                               -%>
+                       ];
+                       var instances = st["instances"] || {};
+                       for (var i = 0, len = names.length; i < len; i++) {
+                               var name = names[i];
+                               var el = document.getElementById('cbi-table-' + name + '-running');
+                               if (el) {
+                                       var running = instances.hasOwnProperty(name)? instances[name].running : false;
+                                       el.innerText = running ? 'yes' : 'no';
+                               }
+                       }
+               }
+       );
+//]]></script>
diff --git a/applications/luci-app-shadowsocks-libev/po/pt-br/shadowsocks-libev.po b/applications/luci-app-shadowsocks-libev/po/pt-br/shadowsocks-libev.po
deleted file mode 100644 (file)
index f2b18e3..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-msgid ""
-msgstr ""
-"Content-Type: text/plain; charset=UTF-8\n"
-"Project-Id-Version: \n"
-"POT-Creation-Date: \n"
-"PO-Revision-Date: \n"
-"Language-Team: \n"
-"MIME-Version: 1.0\n"
-"Content-Transfer-Encoding: 8bit\n"
-"X-Generator: Poedit 1.8.11\n"
-"Last-Translator: Luiz Angelo Daros de Luca <luizluca@gmail.com>\n"
-"Plural-Forms: nplurals=2; plural=(n > 1);\n"
-"Language: pt_BR\n"
-
-msgid "Access Control"
-msgstr "Controle de Acesso"
-
-msgid "Allow all except listed"
-msgstr "Permitir todos, exceto os listados"
-
-msgid "Allow listed only"
-msgstr "Permitir somente os listados"
-
-msgid "Bypassed IP"
-msgstr "Endereços IP Ignorados"
-
-msgid "Connection Timeout"
-msgstr "Tempo limite de conexão"
-
-msgid "Custom"
-msgstr "Personalizado"
-
-msgid "Disabled"
-msgstr "Desabilitado"
-
-msgid "Enable"
-msgstr "Ativar"
-
-msgid "Enabled"
-msgstr "Ativado"
-
-msgid "Encrypt Method"
-msgstr "Método de Cifragem"
-
-msgid "Forwarded IP"
-msgstr "Endereço IP Encaminhado"
-
-msgid "Forwarding Tunnel"
-msgstr "Tunel para Encaminhamento"
-
-msgid "Global Setting"
-msgstr "Opções Globais"
-
-msgid "Ignore List"
-msgstr "Lista de Ignorados"
-
-msgid "LAN"
-msgstr "LAN"
-
-msgid "LAN IP List"
-msgstr "Lista de endereços IP da LAN"
-
-msgid "Local Port"
-msgstr "Porta Local"
-
-msgid "Password"
-msgstr "Senha"
-
-msgid "Relay Mode"
-msgstr "Modo de Retransmissor"
-
-msgid "Server Address"
-msgstr "Endereço do Servidor"
-
-msgid "Server Port"
-msgstr "Porta do servidor"
-
-msgid "ShadowSocks-libev"
-msgstr "ShadowSocks-libev"
-
-msgid "ShadowSocks-libev is not running"
-msgstr "O serviço ShadowSocks-libev está parado"
-
-msgid "ShadowSocks-libev is running"
-msgstr "O serviço ShadowSocks-libev está em execução."
-
-msgid "UDP Forward"
-msgstr "Encaminhamento UDP"
-
-msgid "UDP Local Port"
-msgstr "Porta Local UDP"
-
-msgid "UDP Relay"
-msgstr "Retransmissão UDP"
-
-msgid "WAN"
-msgstr "WAN"
diff --git a/applications/luci-app-shadowsocks-libev/po/sv/shadowsocks-libev.po b/applications/luci-app-shadowsocks-libev/po/sv/shadowsocks-libev.po
deleted file mode 100644 (file)
index b0cf6d3..0000000
+++ /dev/null
@@ -1,136 +0,0 @@
-msgid ""
-msgstr ""
-"Content-Type: text/plain; charset=UTF-8\n"
-"Project-Id-Version: PACKAGE VERSION\n"
-"Last-Translator: Automatically generated\n"
-"Language-Team: none\n"
-"Language: sv\n"
-"MIME-Version: 1.0\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-msgid "Access Control"
-msgstr ""
-
-msgid "Allow all except listed"
-msgstr ""
-
-msgid "Allow listed only"
-msgstr ""
-
-msgid "Bypassed IP"
-msgstr ""
-
-msgid "Connection Timeout"
-msgstr ""
-
-msgid "Custom"
-msgstr ""
-
-msgid "Disabled"
-msgstr ""
-
-msgid "Enable"
-msgstr ""
-
-msgid "Enabled"
-msgstr ""
-
-msgid "Encrypt Method"
-msgstr ""
-
-msgid "Forwarded IP"
-msgstr ""
-
-msgid "Forwarding Tunnel"
-msgstr ""
-
-msgid "Global Setting"
-msgstr ""
-
-msgid "Ignore List"
-msgstr ""
-
-msgid "LAN"
-msgstr ""
-
-msgid "LAN IP List"
-msgstr ""
-
-msgid "Local Port"
-msgstr ""
-
-msgid "Password"
-msgstr ""
-
-msgid "Relay Mode"
-msgstr ""
-
-msgid "Server Address"
-msgstr ""
-
-msgid "Server Port"
-msgstr ""
-
-msgid "ShadowSocks-libev"
-msgstr ""
-
-msgid "ShadowSocks-libev is not running"
-msgstr ""
-
-msgid "ShadowSocks-libev is running"
-msgstr ""
-
-msgid "UDP Forward"
-msgstr ""
-
-msgid "UDP Local Port"
-msgstr ""
-
-msgid "UDP Relay"
-msgstr ""
-
-msgid "WAN"
-msgstr ""
-
-#~ msgid "Broadcast on all interfaces"
-#~ msgstr "Sänd i alla gränssnitt"
-
-#~ msgid "Choose the host to wake up or enter a custom MAC address to use"
-#~ msgstr ""
-#~ "Välj värden som ska väckas upp eller fyll i en anpassad MAC-adress att "
-#~ "använda"
-
-#~ msgid "Host to wake up"
-#~ msgstr "Värd som ska väckas upp"
-
-#~ msgid "Network interface to use"
-#~ msgstr "Nätverksgränssnitt som ska användas"
-
-#~ msgid ""
-#~ "Sometimes only one of the two tools works. If one fails, try the other one"
-#~ msgstr ""
-#~ "Ibland så fungerar bara en av de två verktygen. Prova med den andra om "
-#~ "den första misslyckades"
-
-#~ msgid "Specifies the interface the WoL packet is sent on"
-#~ msgstr "Anger gränssnittet som fjärrstartspaketet skickas med"
-
-#~ msgid "Starting WoL utility:"
-#~ msgstr "Startar hjälpprogrammet för fjärrstyrning av uppstart:"
-
-#~ msgid "Wake on LAN"
-#~ msgstr "Fjärrstyrning av uppstart"
-
-#~ msgid ""
-#~ "Wake on LAN is a mechanism to remotely boot computers in the local "
-#~ "network."
-#~ msgstr ""
-#~ "Fjärrstyrning av uppstart är en mekanism för att starta upp datorer via "
-#~ "fjärrstyrning i det lokala nätverket."
-
-#~ msgid "Wake up host"
-#~ msgstr "Väck upp värden"
-
-#~ msgid "WoL program"
-#~ msgstr "Program för fjärrstart"
diff --git a/applications/luci-app-shadowsocks-libev/po/templates/shadowsocks-libev.pot b/applications/luci-app-shadowsocks-libev/po/templates/shadowsocks-libev.pot
deleted file mode 100644 (file)
index 81bbcb7..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-msgid ""
-msgstr "Content-Type: text/plain; charset=UTF-8"
-
-msgid "Access Control"
-msgstr ""
-
-msgid "Allow all except listed"
-msgstr ""
-
-msgid "Allow listed only"
-msgstr ""
-
-msgid "Bypassed IP"
-msgstr ""
-
-msgid "Connection Timeout"
-msgstr ""
-
-msgid "Custom"
-msgstr ""
-
-msgid "Disabled"
-msgstr ""
-
-msgid "Enable"
-msgstr ""
-
-msgid "Enabled"
-msgstr ""
-
-msgid "Encrypt Method"
-msgstr ""
-
-msgid "Forwarded IP"
-msgstr ""
-
-msgid "Forwarding Tunnel"
-msgstr ""
-
-msgid "Global Setting"
-msgstr ""
-
-msgid "Ignore List"
-msgstr ""
-
-msgid "LAN"
-msgstr ""
-
-msgid "LAN IP List"
-msgstr ""
-
-msgid "Local Port"
-msgstr ""
-
-msgid "Password"
-msgstr ""
-
-msgid "Relay Mode"
-msgstr ""
-
-msgid "Server Address"
-msgstr ""
-
-msgid "Server Port"
-msgstr ""
-
-msgid "ShadowSocks-libev"
-msgstr ""
-
-msgid "ShadowSocks-libev is not running"
-msgstr ""
-
-msgid "ShadowSocks-libev is running"
-msgstr ""
-
-msgid "UDP Forward"
-msgstr ""
-
-msgid "UDP Local Port"
-msgstr ""
-
-msgid "UDP Relay"
-msgstr ""
-
-msgid "WAN"
-msgstr ""
diff --git a/applications/luci-app-shadowsocks-libev/po/zh-cn/shadowsocks-libev.po b/applications/luci-app-shadowsocks-libev/po/zh-cn/shadowsocks-libev.po
deleted file mode 100644 (file)
index f86eee7..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-msgid ""
-msgstr ""
-"Project-Id-Version: PACKAGE VERSION\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-11-12 14:12+0800\n"
-"PO-Revision-Date: 2015-07-02 14:26+0800\n"
-"Last-Translator: Jian Chang <aa65535@live.com>\n"
-"Language: zh_CN\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=1; plural=0;\n"
-"X-Generator: Pootle 2.0.6\n"
-
-msgid "Access Control"
-msgstr "访问控制"
-
-msgid "Allow all except listed"
-msgstr "仅允许列表外"
-
-msgid "Allow listed only"
-msgstr "仅允许列表内"
-
-msgid "Bypassed IP"
-msgstr "被忽略的IP"
-
-msgid "Connection Timeout"
-msgstr "连接超时"
-
-msgid "Custom"
-msgstr "自定义"
-
-msgid "Disabled"
-msgstr "已禁用"
-
-msgid "Enable"
-msgstr "启用"
-
-msgid "Enabled"
-msgstr "已启用"
-
-msgid "Encrypt Method"
-msgstr "加密方式"
-
-msgid "Forwarded IP"
-msgstr "走代理的IP"
-
-msgid "Forwarding Tunnel"
-msgstr "UDP转发地址"
-
-msgid "Global Setting"
-msgstr "全局设置"
-
-msgid "Ignore List"
-msgstr "忽略列表"
-
-msgid "LAN"
-msgstr ""
-
-msgid "LAN IP List"
-msgstr "内网IP列表"
-
-msgid "Local Port"
-msgstr "本地端口"
-
-msgid "Password"
-msgstr "密码"
-
-msgid "Relay Mode"
-msgstr "中继模式"
-
-msgid "Server Address"
-msgstr "服务器地址"
-
-msgid "Server Port"
-msgstr "服务器端口"
-
-msgid "ShadowSocks-libev"
-msgstr "ShadowSocks-libev"
-
-msgid "ShadowSocks-libev is not running"
-msgstr "ShadowSocks-libev 未运行"
-
-msgid "ShadowSocks-libev is running"
-msgstr "ShadowSocks-libev 运行中"
-
-msgid "UDP Forward"
-msgstr "UDP转发"
-
-msgid "UDP Local Port"
-msgstr "UDP本地端口"
-
-msgid "UDP Relay"
-msgstr "UDP中继"
-
-msgid "WAN"
-msgstr ""
index 04eee93..2a8acee 100644 (file)
@@ -5,18 +5,34 @@ module("luci.statistics.rrdtool.definitions.apcups",package.seeall)
 
 function rrdargs( graph, plugin, plugin_instance, dtype )
 
+       local voltagesdc = {
+               title = "%H: Voltages on APC UPS - Battery",
+               vlabel = "Volts DC",
+    alt_autoscale = true,
+               number_format = "%5.1lfV",
+               data = {
+                       instances = {
+                               voltage = { "battery" }
+                       },
+
+                       options = { 
+                               voltage = { title = "Battery voltage", noarea=true }
+                       }
+               }
+       }
+       
        local voltages = {
-               title = "%H: Voltages on APC UPS ",
-               vlabel = "V",
+               title = "%H: Voltages on APC UPS - AC",
+               vlabel = "Volts AC",
+               alt_autoscale = true,
                number_format = "%5.1lfV",
                data = {
                        instances = {
-                               voltage = { "battery", "input", "output" }
+                               voltage = {  "input", "output" }
                        },
 
                        options = {
                                voltage_output  = { color = "00e000", title = "Output voltage", noarea=true, overlay=true },
-                               voltage_battery = { color = "0000ff", title = "Battery voltage", noarea=true, overlay=true },
                                voltage_input   = { color = "ffb000", title = "Input voltage", noarea=true, overlay=true }
                        }
                }
@@ -97,5 +113,5 @@ function rrdargs( graph, plugin, plugin_instance, dtype )
                }
        }
 
-       return { voltages, percentload, charge_percent, temperature, timeleft, frequency }
+       return { voltages, voltagesdc, percentload, charge_percent, temperature, timeleft, frequency }
 end
index 33d5051..738af55 100644 (file)
@@ -15,6 +15,12 @@ msgstr ""
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 "X-Generator: Pootle 2.0.6\n"
 
+msgid "APC UPS"
+msgstr ""
+
+msgid "APCUPS Plugin Configuration"
+msgstr ""
+
 msgid "Action (target)"
 msgstr "Acció (objectiu)"
 
@@ -295,6 +301,9 @@ msgstr "Monitoritza els discs i les particions"
 msgid "Monitor filesystem types"
 msgstr "Monitoritza els tipus de sistema de fitxers"
 
+msgid "Monitor host"
+msgstr ""
+
 msgid "Monitor hosts"
 msgstr "Monitoritza màquines"
 
@@ -381,6 +390,9 @@ msgstr "Configuració del connector ping"
 msgid "Port"
 msgstr "Port"
 
+msgid "Port for apcupsd communication"
+msgstr ""
+
 msgid "Processes"
 msgstr "Processos"
 
@@ -506,6 +518,9 @@ msgstr "TTL per paquets ping"
 msgid "Table"
 msgstr "Taula"
 
+msgid "The APCUPS plugin collects statistics about the APC UPS."
+msgstr ""
+
 msgid "The NUT plugin reads information about Uninterruptible Power Supplies."
 msgstr ""
 
index 8498316..fc3f513 100644 (file)
@@ -11,6 +11,12 @@ msgstr ""
 "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
 "X-Generator: Pootle 2.0.6\n"
 
+msgid "APC UPS"
+msgstr ""
+
+msgid "APCUPS Plugin Configuration"
+msgstr ""
+
 msgid "Action (target)"
 msgstr "Akce (cíl)"
 
@@ -290,6 +296,9 @@ msgstr "Sledovat disky a oddíly"
 msgid "Monitor filesystem types"
 msgstr "Sledovat typy souborových systémů"
 
+msgid "Monitor host"
+msgstr ""
+
 msgid "Monitor hosts"
 msgstr "Sledovat hostitele"
 
@@ -376,6 +385,9 @@ msgstr "Nastavení pluginu Ping"
 msgid "Port"
 msgstr "Port"
 
+msgid "Port for apcupsd communication"
+msgstr ""
+
 msgid "Processes"
 msgstr "Procesy"
 
@@ -500,6 +512,9 @@ msgstr "TTL pro pakety pingu"
 msgid "Table"
 msgstr ""
 
+msgid "The APCUPS plugin collects statistics about the APC UPS."
+msgstr ""
+
 msgid "The NUT plugin reads information about Uninterruptible Power Supplies."
 msgstr ""
 
index 196433e..45ba020 100644 (file)
@@ -13,6 +13,12 @@ msgstr ""
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 "X-Generator: Pootle 2.0.6\n"
 
+msgid "APC UPS"
+msgstr ""
+
+msgid "APCUPS Plugin Configuration"
+msgstr ""
+
 msgid "Action (target)"
 msgstr "Aktion (Ziel)"
 
@@ -297,6 +303,9 @@ msgstr "Geräte und Partitionen überwachen"
 msgid "Monitor filesystem types"
 msgstr "Datesystemtypen überwachen"
 
+msgid "Monitor host"
+msgstr ""
+
 msgid "Monitor hosts"
 msgstr "Hosts überwachen"
 
@@ -383,6 +392,9 @@ msgstr "Ping Plugin Konfiguration"
 msgid "Port"
 msgstr "Port"
 
+msgid "Port for apcupsd communication"
+msgstr ""
+
 msgid "Processes"
 msgstr "Prozesse"
 
@@ -506,6 +518,9 @@ msgstr "TTL für Ping Pakete"
 msgid "Table"
 msgstr "Tabelle"
 
+msgid "The APCUPS plugin collects statistics about the APC UPS."
+msgstr ""
+
 msgid "The NUT plugin reads information about Uninterruptible Power Supplies."
 msgstr ""
 "Das NUT-Plugin liest Informationen über Unterbrechungsfreie Stromversorgungen"
index da54cda..4062868 100644 (file)
@@ -13,6 +13,12 @@ msgstr ""
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 "X-Generator: Pootle 2.0.4\n"
 
+msgid "APC UPS"
+msgstr ""
+
+msgid "APCUPS Plugin Configuration"
+msgstr ""
+
 msgid "Action (target)"
 msgstr ""
 
@@ -288,6 +294,9 @@ msgstr ""
 msgid "Monitor filesystem types"
 msgstr ""
 
+msgid "Monitor host"
+msgstr ""
+
 msgid "Monitor hosts"
 msgstr ""
 
@@ -374,6 +383,9 @@ msgstr ""
 msgid "Port"
 msgstr ""
 
+msgid "Port for apcupsd communication"
+msgstr ""
+
 msgid "Processes"
 msgstr "Διεργασίες"
 
@@ -497,6 +509,9 @@ msgstr ""
 msgid "Table"
 msgstr "Πίνακας"
 
+msgid "The APCUPS plugin collects statistics about the APC UPS."
+msgstr ""
+
 msgid "The NUT plugin reads information about Uninterruptible Power Supplies."
 msgstr ""
 
index d9ab59c..f7ebfe0 100644 (file)
@@ -13,6 +13,12 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "X-Generator: Translate Toolkit 1.1.1\n"
 
+msgid "APC UPS"
+msgstr ""
+
+msgid "APCUPS Plugin Configuration"
+msgstr ""
+
 msgid "Action (target)"
 msgstr "Action (target)"
 
@@ -293,6 +299,9 @@ msgstr "Monitor disks and partitions"
 msgid "Monitor filesystem types"
 msgstr "Monitor filesystem types"
 
+msgid "Monitor host"
+msgstr ""
+
 msgid "Monitor hosts"
 msgstr "Monitor hosts"
 
@@ -379,6 +388,9 @@ msgstr "Ping Plugin Configuration"
 msgid "Port"
 msgstr ""
 
+msgid "Port for apcupsd communication"
+msgstr ""
+
 msgid "Processes"
 msgstr "Processes"
 
@@ -502,6 +514,9 @@ msgstr "TTL for ping packets"
 msgid "Table"
 msgstr "Table"
 
+msgid "The APCUPS plugin collects statistics about the APC UPS."
+msgstr ""
+
 msgid "The NUT plugin reads information about Uninterruptible Power Supplies."
 msgstr ""
 
index 18c2581..3c811ff 100644 (file)
@@ -13,6 +13,12 @@ msgstr ""
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 "X-Generator: Pootle 2.0.6\n"
 
+msgid "APC UPS"
+msgstr ""
+
+msgid "APCUPS Plugin Configuration"
+msgstr ""
+
 msgid "Action (target)"
 msgstr "Acción (objetivo)"
 
@@ -292,6 +298,9 @@ msgstr "Monitorizar discos y particiones"
 msgid "Monitor filesystem types"
 msgstr "Monitorizar tipos de sistema de archivos"
 
+msgid "Monitor host"
+msgstr ""
+
 msgid "Monitor hosts"
 msgstr "Monitorizar máquinas"
 
@@ -378,6 +387,9 @@ msgstr "Configuración del plugin \"Ping\""
 msgid "Port"
 msgstr "Puerto"
 
+msgid "Port for apcupsd communication"
+msgstr ""
+
 msgid "Processes"
 msgstr "Procesos"
 
@@ -501,6 +513,9 @@ msgstr "TTL para paquetes de ping"
 msgid "Table"
 msgstr "Tabla"
 
+msgid "The APCUPS plugin collects statistics about the APC UPS."
+msgstr ""
+
 msgid "The NUT plugin reads information about Uninterruptible Power Supplies."
 msgstr ""
 "El plugin NUT obtiene información sobre Sistemas de Alimentación "
index b657bd3..bc156dd 100644 (file)
@@ -13,6 +13,12 @@ msgstr ""
 "Plural-Forms: nplurals=2; plural=(n > 1);\n"
 "X-Generator: Pootle 2.0.4\n"
 
+msgid "APC UPS"
+msgstr ""
+
+msgid "APCUPS Plugin Configuration"
+msgstr ""
+
 msgid "Action (target)"
 msgstr "Action (cible)"
 
@@ -294,6 +300,9 @@ msgstr "Disques et partitions à surveiller"
 msgid "Monitor filesystem types"
 msgstr "types de systèmes de fichier à surveiller"
 
+msgid "Monitor host"
+msgstr ""
+
 msgid "Monitor hosts"
 msgstr "Hôtes à surveiller"
 
@@ -380,6 +389,9 @@ msgstr "Configuration du greffon Ping"
 msgid "Port"
 msgstr ""
 
+msgid "Port for apcupsd communication"
+msgstr ""
+
 msgid "Processes"
 msgstr "Processus"
 
@@ -503,6 +515,9 @@ msgstr "TTL des paquets ping"
 msgid "Table"
 msgstr "Table"
 
+msgid "The APCUPS plugin collects statistics about the APC UPS."
+msgstr ""
+
 msgid "The NUT plugin reads information about Uninterruptible Power Supplies."
 msgstr ""
 
index 6f40a47..35f978e 100644 (file)
@@ -13,6 +13,12 @@ msgstr ""
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 "X-Generator: Pootle 2.0.6\n"
 
+msgid "APC UPS"
+msgstr ""
+
+msgid "APCUPS Plugin Configuration"
+msgstr ""
+
 msgid "Action (target)"
 msgstr ""
 
@@ -283,6 +289,9 @@ msgstr ""
 msgid "Monitor filesystem types"
 msgstr ""
 
+msgid "Monitor host"
+msgstr ""
+
 msgid "Monitor hosts"
 msgstr ""
 
@@ -369,6 +378,9 @@ msgstr ""
 msgid "Port"
 msgstr ""
 
+msgid "Port for apcupsd communication"
+msgstr ""
+
 msgid "Processes"
 msgstr ""
 
@@ -492,6 +504,9 @@ msgstr ""
 msgid "Table"
 msgstr ""
 
+msgid "The APCUPS plugin collects statistics about the APC UPS."
+msgstr ""
+
 msgid "The NUT plugin reads information about Uninterruptible Power Supplies."
 msgstr ""
 
index 979c72f..e5c4e60 100644 (file)
@@ -11,6 +11,12 @@ msgstr ""
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 "X-Generator: Pootle 2.0.6\n"
 
+msgid "APC UPS"
+msgstr ""
+
+msgid "APCUPS Plugin Configuration"
+msgstr ""
+
 msgid "Action (target)"
 msgstr "Tevékenység (cél)"
 
@@ -295,6 +301,9 @@ msgstr "Lemezek és partíciók figyelése"
 msgid "Monitor filesystem types"
 msgstr "Fájlrendszer típusok figyelése"
 
+msgid "Monitor host"
+msgstr ""
+
 msgid "Monitor hosts"
 msgstr "Gépek figyelése"
 
@@ -381,6 +390,9 @@ msgstr "Ping bővítmény beállítása"
 msgid "Port"
 msgstr "Port"
 
+msgid "Port for apcupsd communication"
+msgstr ""
+
 msgid "Processes"
 msgstr "Folyamatok"
 
@@ -508,6 +520,9 @@ msgstr "TTL a ping csomagokhoz"
 msgid "Table"
 msgstr "Táblázat"
 
+msgid "The APCUPS plugin collects statistics about the APC UPS."
+msgstr ""
+
 msgid "The NUT plugin reads information about Uninterruptible Power Supplies."
 msgstr "A NUT bővítmény a szünetmentes tápokról ad információkat."
 
index b0ae3d6..2451503 100644 (file)
@@ -13,6 +13,12 @@ msgstr ""
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 "X-Generator: Pootle 2.0.6\n"
 
+msgid "APC UPS"
+msgstr ""
+
+msgid "APCUPS Plugin Configuration"
+msgstr ""
+
 msgid "Action (target)"
 msgstr "Azione (destinazione)"
 
@@ -293,6 +299,9 @@ msgstr ""
 msgid "Monitor filesystem types"
 msgstr ""
 
+msgid "Monitor host"
+msgstr ""
+
 msgid "Monitor hosts"
 msgstr ""
 
@@ -379,6 +388,9 @@ msgstr ""
 msgid "Port"
 msgstr ""
 
+msgid "Port for apcupsd communication"
+msgstr ""
+
 msgid "Processes"
 msgstr ""
 
@@ -502,6 +514,9 @@ msgstr ""
 msgid "Table"
 msgstr "Tabella"
 
+msgid "The APCUPS plugin collects statistics about the APC UPS."
+msgstr ""
+
 msgid "The NUT plugin reads information about Uninterruptible Power Supplies."
 msgstr ""
 
index 690d920..53941cf 100644 (file)
@@ -13,6 +13,12 @@ msgstr ""
 "X-Generator: Poedit 1.8.11\n"
 "Language-Team: \n"
 
+msgid "APC UPS"
+msgstr ""
+
+msgid "APCUPS Plugin Configuration"
+msgstr ""
+
 msgid "Action (target)"
 msgstr "アクション(対象)"
 
@@ -295,6 +301,9 @@ msgstr "ディスクとパーティションをモニターする"
 msgid "Monitor filesystem types"
 msgstr "ファイルシステム タイプをモニターする"
 
+msgid "Monitor host"
+msgstr ""
+
 msgid "Monitor hosts"
 msgstr "ホストをモニターする"
 
@@ -384,6 +393,9 @@ msgstr "Ping プラグイン設定"
 msgid "Port"
 msgstr "ポート"
 
+msgid "Port for apcupsd communication"
+msgstr ""
+
 msgid "Processes"
 msgstr "プロセス"
 
@@ -507,6 +519,9 @@ msgstr "pingパケットのTTL"
 msgid "Table"
 msgstr "テーブル"
 
+msgid "The APCUPS plugin collects statistics about the APC UPS."
+msgstr ""
+
 msgid "The NUT plugin reads information about Uninterruptible Power Supplies."
 msgstr "NUT プラグインは、無停電電源装置についての情報を読み取ります。"
 
index 582314c..c02556f 100644 (file)
@@ -10,6 +10,12 @@ msgstr ""
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 
+msgid "APC UPS"
+msgstr ""
+
+msgid "APCUPS Plugin Configuration"
+msgstr ""
+
 msgid "Action (target)"
 msgstr ""
 
@@ -280,6 +286,9 @@ msgstr ""
 msgid "Monitor filesystem types"
 msgstr ""
 
+msgid "Monitor host"
+msgstr ""
+
 msgid "Monitor hosts"
 msgstr ""
 
@@ -366,6 +375,9 @@ msgstr ""
 msgid "Port"
 msgstr ""
 
+msgid "Port for apcupsd communication"
+msgstr ""
+
 msgid "Processes"
 msgstr ""
 
@@ -489,6 +501,9 @@ msgstr ""
 msgid "Table"
 msgstr ""
 
+msgid "The APCUPS plugin collects statistics about the APC UPS."
+msgstr ""
+
 msgid "The NUT plugin reads information about Uninterruptible Power Supplies."
 msgstr ""
 
index d37bc48..4de2ee6 100644 (file)
@@ -4,6 +4,12 @@ msgstr ""
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 
+msgid "APC UPS"
+msgstr ""
+
+msgid "APCUPS Plugin Configuration"
+msgstr ""
+
 msgid "Action (target)"
 msgstr "Handling (mål)"
 
@@ -282,6 +288,9 @@ msgstr "Overvåk disker og partisjoner"
 msgid "Monitor filesystem types"
 msgstr "Overvåk filsystem typer"
 
+msgid "Monitor host"
+msgstr ""
+
 msgid "Monitor hosts"
 msgstr "Overvåk verter"
 
@@ -368,6 +377,9 @@ msgstr "Ping plugin konfigurasjon"
 msgid "Port"
 msgstr ""
 
+msgid "Port for apcupsd communication"
+msgstr ""
+
 msgid "Processes"
 msgstr "Prosesser"
 
@@ -491,6 +503,9 @@ msgstr "TTL for ping pakker"
 msgid "Table"
 msgstr "Tabell"
 
+msgid "The APCUPS plugin collects statistics about the APC UPS."
+msgstr ""
+
 msgid "The NUT plugin reads information about Uninterruptible Power Supplies."
 msgstr ""
 
index bf2ec93..6e34ce0 100644 (file)
@@ -14,6 +14,12 @@ msgstr ""
 "|| n%100>=20) ? 1 : 2);\n"
 "X-Generator: Pootle 2.0.6\n"
 
+msgid "APC UPS"
+msgstr ""
+
+msgid "APCUPS Plugin Configuration"
+msgstr ""
+
 msgid "Action (target)"
 msgstr "Akcja (cel)"
 
@@ -296,6 +302,9 @@ msgstr "Monitoruj dyski i partycje"
 msgid "Monitor filesystem types"
 msgstr "Monitoruj system plików"
 
+msgid "Monitor host"
+msgstr ""
+
 msgid "Monitor hosts"
 msgstr "Monitoruj hosty"
 
@@ -382,6 +391,9 @@ msgstr "Konfiguracja wtyczki Ping"
 msgid "Port"
 msgstr "Port"
 
+msgid "Port for apcupsd communication"
+msgstr ""
+
 msgid "Processes"
 msgstr "Procesy"
 
@@ -506,6 +518,9 @@ msgstr "TTL dla pakietów ping"
 msgid "Table"
 msgstr "Tabela"
 
+msgid "The APCUPS plugin collects statistics about the APC UPS."
+msgstr ""
+
 msgid "The NUT plugin reads information about Uninterruptible Power Supplies."
 msgstr "Wtyczka Nut Informuje o Nie przerywalnym Zasilaniu"
 
index 74c4a26..c5d6899 100644 (file)
@@ -13,6 +13,12 @@ msgstr ""
 "X-Generator: Poedit 1.8.11\n"
 "Language-Team: \n"
 
+msgid "APC UPS"
+msgstr ""
+
+msgid "APCUPS Plugin Configuration"
+msgstr ""
+
 msgid "Action (target)"
 msgstr "Ação (destino)"
 
@@ -299,6 +305,9 @@ msgstr "Monitoras discos e partições"
 msgid "Monitor filesystem types"
 msgstr "Monitorar tipos de sistemas de arquivos"
 
+msgid "Monitor host"
+msgstr ""
+
 msgid "Monitor hosts"
 msgstr "Monitorar os equipamentos"
 
@@ -388,6 +397,9 @@ msgstr "Configuração do plugin Ping"
 msgid "Port"
 msgstr "Porta"
 
+msgid "Port for apcupsd communication"
+msgstr ""
+
 msgid "Processes"
 msgstr "Processos"
 
@@ -511,6 +523,9 @@ msgstr "TTL para os pacotes do ping"
 msgid "Table"
 msgstr "Tabela"
 
+msgid "The APCUPS plugin collects statistics about the APC UPS."
+msgstr ""
+
 msgid "The NUT plugin reads information about Uninterruptible Power Supplies."
 msgstr "O plugin NUT lê informações sobre Fontes de alimentação ininterruptas."
 
index 79c7bd0..245e6e9 100644 (file)
@@ -13,6 +13,12 @@ msgstr ""
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 "X-Generator: Pootle 2.0.6\n"
 
+msgid "APC UPS"
+msgstr ""
+
+msgid "APCUPS Plugin Configuration"
+msgstr ""
+
 msgid "Action (target)"
 msgstr "Ação (destino)"
 
@@ -295,6 +301,9 @@ msgstr "Monitoras discos e partições"
 msgid "Monitor filesystem types"
 msgstr "Monitorar tipos de sistemas de arquivos"
 
+msgid "Monitor host"
+msgstr ""
+
 msgid "Monitor hosts"
 msgstr "Monitorar os hosts"
 
@@ -381,6 +390,9 @@ msgstr "Configuração do plugin Ping"
 msgid "Port"
 msgstr "Porta"
 
+msgid "Port for apcupsd communication"
+msgstr ""
+
 msgid "Processes"
 msgstr "Processos"
 
@@ -504,6 +516,9 @@ msgstr "TTL para os pacotes do ping"
 msgid "Table"
 msgstr "Tabela"
 
+msgid "The APCUPS plugin collects statistics about the APC UPS."
+msgstr ""
+
 msgid "The NUT plugin reads information about Uninterruptible Power Supplies."
 msgstr ""
 
index c5dfcfe..a326fec 100644 (file)
@@ -14,6 +14,12 @@ msgstr ""
 "20)) ? 1 : 2);;\n"
 "X-Generator: Pootle 2.0.4\n"
 
+msgid "APC UPS"
+msgstr ""
+
+msgid "APCUPS Plugin Configuration"
+msgstr ""
+
 msgid "Action (target)"
 msgstr ""
 
@@ -287,6 +293,9 @@ msgstr ""
 msgid "Monitor filesystem types"
 msgstr ""
 
+msgid "Monitor host"
+msgstr ""
+
 msgid "Monitor hosts"
 msgstr ""
 
@@ -373,6 +382,9 @@ msgstr ""
 msgid "Port"
 msgstr ""
 
+msgid "Port for apcupsd communication"
+msgstr ""
+
 msgid "Processes"
 msgstr "Procese"
 
@@ -496,6 +508,9 @@ msgstr ""
 msgid "Table"
 msgstr ""
 
+msgid "The APCUPS plugin collects statistics about the APC UPS."
+msgstr ""
+
 msgid "The NUT plugin reads information about Uninterruptible Power Supplies."
 msgstr ""
 
index 3a418de..9d0ff9f 100644 (file)
@@ -15,6 +15,12 @@ msgstr ""
 "X-Generator: Pootle 2.0.6\n"
 "X-Poedit-SourceCharset: UTF-8\n"
 
+msgid "APC UPS"
+msgstr ""
+
+msgid "APCUPS Plugin Configuration"
+msgstr ""
+
 msgid "Action (target)"
 msgstr "Действие (цель)"
 
@@ -297,6 +303,9 @@ msgstr "Собирать статистику с дисков и раздело
 msgid "Monitor filesystem types"
 msgstr "Собирать статистику с файловых систем"
 
+msgid "Monitor host"
+msgstr ""
+
 msgid "Monitor hosts"
 msgstr "Собирать статистику с хостов"
 
@@ -383,6 +392,9 @@ msgstr "Конфигурация модуля Ping"
 msgid "Port"
 msgstr "Порт"
 
+msgid "Port for apcupsd communication"
+msgstr ""
+
 msgid "Processes"
 msgstr "Процессы"
 
@@ -508,6 +520,9 @@ msgstr "TTL для ping-пакетов"
 msgid "Table"
 msgstr "Таблица"
 
+msgid "The APCUPS plugin collects statistics about the APC UPS."
+msgstr ""
+
 msgid "The NUT plugin reads information about Uninterruptible Power Supplies."
 msgstr ""
 
index 6dba7d0..53858ca 100644 (file)
@@ -8,6 +8,12 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
 
+msgid "APC UPS"
+msgstr ""
+
+msgid "APCUPS Plugin Configuration"
+msgstr ""
+
 msgid "Action (target)"
 msgstr ""
 
@@ -278,6 +284,9 @@ msgstr ""
 msgid "Monitor filesystem types"
 msgstr ""
 
+msgid "Monitor host"
+msgstr ""
+
 msgid "Monitor hosts"
 msgstr ""
 
@@ -364,6 +373,9 @@ msgstr ""
 msgid "Port"
 msgstr ""
 
+msgid "Port for apcupsd communication"
+msgstr ""
+
 msgid "Processes"
 msgstr ""
 
@@ -487,6 +499,9 @@ msgstr ""
 msgid "Table"
 msgstr ""
 
+msgid "The APCUPS plugin collects statistics about the APC UPS."
+msgstr ""
+
 msgid "The NUT plugin reads information about Uninterruptible Power Supplies."
 msgstr ""
 
index bef0f2d..9d738f2 100644 (file)
@@ -9,6 +9,12 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
+msgid "APC UPS"
+msgstr ""
+
+msgid "APCUPS Plugin Configuration"
+msgstr ""
+
 msgid "Action (target)"
 msgstr ""
 
@@ -283,6 +289,9 @@ msgstr "Övervaka hårddiskar och partitioner"
 msgid "Monitor filesystem types"
 msgstr "Övervaka filsystemtyper"
 
+msgid "Monitor host"
+msgstr ""
+
 msgid "Monitor hosts"
 msgstr "Övervaka värdar"
 
@@ -369,6 +378,9 @@ msgstr ""
 msgid "Port"
 msgstr "Port"
 
+msgid "Port for apcupsd communication"
+msgstr ""
+
 msgid "Processes"
 msgstr "Processer"
 
@@ -492,6 +504,9 @@ msgstr "TTL för ping-paket"
 msgid "Table"
 msgstr "Tabell"
 
+msgid "The APCUPS plugin collects statistics about the APC UPS."
+msgstr ""
+
 msgid "The NUT plugin reads information about Uninterruptible Power Supplies."
 msgstr ""
 
index c57a85b..ec630b6 100644 (file)
@@ -1,6 +1,12 @@
 msgid ""
 msgstr "Content-Type: text/plain; charset=UTF-8"
 
+msgid "APC UPS"
+msgstr ""
+
+msgid "APCUPS Plugin Configuration"
+msgstr ""
+
 msgid "Action (target)"
 msgstr ""
 
@@ -271,6 +277,9 @@ msgstr ""
 msgid "Monitor filesystem types"
 msgstr ""
 
+msgid "Monitor host"
+msgstr ""
+
 msgid "Monitor hosts"
 msgstr ""
 
@@ -357,6 +366,9 @@ msgstr ""
 msgid "Port"
 msgstr ""
 
+msgid "Port for apcupsd communication"
+msgstr ""
+
 msgid "Processes"
 msgstr ""
 
@@ -480,6 +492,9 @@ msgstr ""
 msgid "Table"
 msgstr ""
 
+msgid "The APCUPS plugin collects statistics about the APC UPS."
+msgstr ""
+
 msgid "The NUT plugin reads information about Uninterruptible Power Supplies."
 msgstr ""
 
index 6d7056f..860ff95 100644 (file)
@@ -9,6 +9,12 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=1; plural=0;\n"
 
+msgid "APC UPS"
+msgstr ""
+
+msgid "APCUPS Plugin Configuration"
+msgstr ""
+
 msgid "Action (target)"
 msgstr ""
 
@@ -279,6 +285,9 @@ msgstr ""
 msgid "Monitor filesystem types"
 msgstr ""
 
+msgid "Monitor host"
+msgstr ""
+
 msgid "Monitor hosts"
 msgstr ""
 
@@ -365,6 +374,9 @@ msgstr ""
 msgid "Port"
 msgstr ""
 
+msgid "Port for apcupsd communication"
+msgstr ""
+
 msgid "Processes"
 msgstr ""
 
@@ -488,6 +500,9 @@ msgstr ""
 msgid "Table"
 msgstr ""
 
+msgid "The APCUPS plugin collects statistics about the APC UPS."
+msgstr ""
+
 msgid "The NUT plugin reads information about Uninterruptible Power Supplies."
 msgstr ""
 
index de17a3c..ac9ae50 100644 (file)
@@ -14,6 +14,12 @@ msgstr ""
 "10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
 "X-Generator: Pootle 2.0.6\n"
 
+msgid "APC UPS"
+msgstr ""
+
+msgid "APCUPS Plugin Configuration"
+msgstr ""
+
 msgid "Action (target)"
 msgstr ""
 
@@ -284,6 +290,9 @@ msgstr ""
 msgid "Monitor filesystem types"
 msgstr ""
 
+msgid "Monitor host"
+msgstr ""
+
 msgid "Monitor hosts"
 msgstr ""
 
@@ -370,6 +379,9 @@ msgstr ""
 msgid "Port"
 msgstr ""
 
+msgid "Port for apcupsd communication"
+msgstr ""
+
 msgid "Processes"
 msgstr ""
 
@@ -493,6 +505,9 @@ msgstr ""
 msgid "Table"
 msgstr ""
 
+msgid "The APCUPS plugin collects statistics about the APC UPS."
+msgstr ""
+
 msgid "The NUT plugin reads information about Uninterruptible Power Supplies."
 msgstr ""
 
index bdb7f1a..f5798a2 100644 (file)
@@ -14,6 +14,12 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "X-Generator: Pootle 1.1.0\n"
 
+msgid "APC UPS"
+msgstr ""
+
+msgid "APCUPS Plugin Configuration"
+msgstr ""
+
 msgid "Action (target)"
 msgstr "Action (target)"
 
@@ -294,6 +300,9 @@ msgstr "Kiểm soát đĩa và phân vùng"
 msgid "Monitor filesystem types"
 msgstr "Kiểm soát loại filesystem"
 
+msgid "Monitor host"
+msgstr ""
+
 msgid "Monitor hosts"
 msgstr "Monitor hosts"
 
@@ -380,6 +389,9 @@ msgstr "Cấu hình Ping plugin"
 msgid "Port"
 msgstr ""
 
+msgid "Port for apcupsd communication"
+msgstr ""
+
 msgid "Processes"
 msgstr "Quá trình xử lý"
 
@@ -503,6 +515,9 @@ msgstr "TTl cho gói ping"
 msgid "Table"
 msgstr "Table"
 
+msgid "The APCUPS plugin collects statistics about the APC UPS."
+msgstr ""
+
 msgid "The NUT plugin reads information about Uninterruptible Power Supplies."
 msgstr ""
 
index 46cf59f..20f5a93 100644 (file)
@@ -13,6 +13,12 @@ msgstr ""
 "X-Generator: Poedit 2.0.1\n"
 "Language-Team: \n"
 
+msgid "APC UPS"
+msgstr ""
+
+msgid "APCUPS Plugin Configuration"
+msgstr ""
+
 msgid "Action (target)"
 msgstr "动作(目标)"
 
@@ -289,6 +295,9 @@ msgstr "监测磁盘和分区"
 msgid "Monitor filesystem types"
 msgstr "监测文件系统类型"
 
+msgid "Monitor host"
+msgstr ""
+
 msgid "Monitor hosts"
 msgstr "监测主机"
 
@@ -377,6 +386,9 @@ msgstr "Ping插件配置"
 msgid "Port"
 msgstr "端口"
 
+msgid "Port for apcupsd communication"
+msgstr ""
+
 msgid "Processes"
 msgstr "进程"
 
@@ -500,6 +512,9 @@ msgstr "ping包TTL"
 msgid "Table"
 msgstr "表"
 
+msgid "The APCUPS plugin collects statistics about the APC UPS."
+msgstr ""
+
 msgid "The NUT plugin reads information about Uninterruptible Power Supplies."
 msgstr "NUT插件读取UPS信息。"
 
index cbd6d9d..36e42c1 100644 (file)
@@ -7,6 +7,12 @@ msgstr ""
 "MIME-Version: 1.0\n"
 "Content-Transfer-Encoding: 8bit\n"
 
+msgid "APC UPS"
+msgstr ""
+
+msgid "APCUPS Plugin Configuration"
+msgstr ""
+
 msgid "Action (target)"
 msgstr ""
 
@@ -277,6 +283,9 @@ msgstr ""
 msgid "Monitor filesystem types"
 msgstr ""
 
+msgid "Monitor host"
+msgstr ""
+
 msgid "Monitor hosts"
 msgstr ""
 
@@ -363,6 +372,9 @@ msgstr ""
 msgid "Port"
 msgstr ""
 
+msgid "Port for apcupsd communication"
+msgstr ""
+
 msgid "Processes"
 msgstr ""
 
@@ -486,6 +498,9 @@ msgstr ""
 msgid "Table"
 msgstr ""
 
+msgid "The APCUPS plugin collects statistics about the APC UPS."
+msgstr ""
+
 msgid "The NUT plugin reads information about Uninterruptible Power Supplies."
 msgstr ""
 
index 86382f6..a418a8e 100644 (file)
@@ -5,8 +5,8 @@ module("luci.controller.travelmate", package.seeall)
 
 local fs = require("nixio.fs")
 local util = require("luci.util")
-local template = require("luci.template")
 local i18n = require("luci.i18n")
+local templ = require("luci.template")
 
 function index()
        if not nixio.fs.access("/etc/config/travelmate") then
@@ -14,15 +14,22 @@ function index()
        end
        entry({"admin", "services", "travelmate"}, firstchild(), _("Travelmate"), 40).dependent = false
        entry({"admin", "services", "travelmate", "tab_from_cbi"}, cbi("travelmate/overview_tab", {hideresetbtn=true, hidesavebtn=true}), _("Overview"), 10).leaf = true
-       entry({"admin", "services", "travelmate", "logfile"}, call("logread"), _("View Logfile"), 20).leaf = true
+       entry({"admin", "services", "travelmate", "stations"}, template("travelmate/stations"), _("Wireless Stations"), 20).leaf = true
+       entry({"admin", "services", "travelmate", "logfile"}, call("logread"), _("View Logfile"), 30).leaf = true
        entry({"admin", "services", "travelmate", "advanced"}, firstchild(), _("Advanced"), 100)
        entry({"admin", "services", "travelmate", "advanced", "configuration"}, cbi("travelmate/configuration_tab"), _("Edit Travelmate Configuration"), 110).leaf = true
        entry({"admin", "services", "travelmate", "advanced", "cfg_wireless"}, cbi("travelmate/cfg_wireless_tab"), _("Edit Wireless Configuration"), 120).leaf = true
        entry({"admin", "services", "travelmate", "advanced", "cfg_network"}, cbi("travelmate/cfg_network_tab"), _("Edit Network Configuration"), 130).leaf = true
        entry({"admin", "services", "travelmate", "advanced", "cfg_firewall"}, cbi("travelmate/cfg_firewall_tab"), _("Edit Firewall Configuration"), 140).leaf = true
+
+       entry({"admin", "services", "travelmate", "wifiscan"}, template("travelmate/wifi_scan")).leaf = true
+       entry({"admin", "services", "travelmate", "wifiadd"}, cbi("travelmate/wifi_add", {hideresetbtn=true, hidesavebtn=true})).leaf = true
+       entry({"admin", "services", "travelmate", "wifiedit"}, cbi("travelmate/wifi_edit", {hideresetbtn=true, hidesavebtn=true})).leaf = true
+       entry({"admin", "services", "travelmate", "wifidelete"}, cbi("travelmate/wifi_delete", {hideresetbtn=true, hidesavebtn=true})).leaf = true
+       entry({"admin", "services", "travelmate", "wifiorder"}, cbi("travelmate/wifi_order", {hideresetbtn=true, hidesavebtn=true})).leaf = true
 end
 
 function logread()
        local logfile = util.trim(util.exec("logread -e 'travelmate'"))
-       template.render("travelmate/logread", {title = i18n.translate("Travelmate Logfile"), content = logfile})
+       templ.render("travelmate/logread", {title = i18n.translate("Travelmate Logfile"), content = logfile})
 end
index 236bbb0..12cb72c 100644 (file)
@@ -2,12 +2,13 @@
 -- This is free software, licensed under the Apache License, Version 2.0
 
 local fs = require("nixio.fs")
-local uci = require("uci")
+local uci = require("luci.model.uci").cursor()
 local json = require("luci.jsonc")
 local nw  = require("luci.model.network").init()
 local fw  = require("luci.model.firewall").init()
-local uplink = uci.get("network", "trm_wwan") or ""
+local trmiface = uci.get("travelmate", "global", "trm_iface") or "trm_wwan"
 local trminput = uci.get("travelmate", "global", "trm_rtfile") or "/tmp/trm_runtime.json"
+local uplink = uci.get("network", trmiface) or ""
 local parse = json.parse(fs.readfile(trminput) or "")
 
 m = Map("travelmate", translate("Travelmate"),
@@ -21,70 +22,87 @@ function m.on_after_commit(self)
        luci.http.redirect(luci.dispatcher.build_url("admin", "services", "travelmate"))
 end
 
--- Main travelmate options
-
 s = m:section(NamedSection, "global", "travelmate")
 
-o1 = s:option(Flag, "trm_enabled", translate("Enable travelmate"))
-o1.default = o1.disabled
-o1.rmempty = false
+-- Interface Wizard
 
-o2 = s:option(Flag, "trm_automatic", translate("Enable 'automatic' mode"),
-       translate("Keep travelmate in an active state."))
-o2.default = o2.enabled
-o2.rmempty = false
-
-o3 = s:option(Value, "trm_iface", translate("Restrict interface trigger to certain interface(s)"),
-       translate("Space separated list of interfaces that trigger travelmate processing. "..
-       "To disable event driven (re-)starts remove all entries."))
-o3.rmempty = true
-
-o4 = s:option(Value, "trm_triggerdelay", translate("Trigger delay"),
-       translate("Additional trigger delay in seconds before travelmate processing begins."))
-o4.default = 2
-o4.datatype = "range(1,90)"
-o4.rmempty = false
+if uplink == "" then
+       dv = s:option(DummyValue, "nil", translate("Interface Wizard"))
+       dv.template = "cbi/nullsection"
 
-o5 = s:option(Flag, "trm_debug", translate("Enable verbose debug logging"))
-o5.default = o5.disabled
-o5.rmempty = false
+       o = s:option(Value, "trm_iface", translate("Uplink interface"))
+       o.datatype = "and(uciname,rangelength(3,15))"
+       o.default = "trm_wwan"
+       o.rmempty = false
 
--- Interface setup
+       function o.write(self, section, value)
+               iface = o:formvalue(section)
+               uci:set("travelmate", section, "trm_iface", iface)
+               uci:save("travelmate")
+               uci:commit("travelmate")
+       end
 
-if uplink == "" then
-       dv = s:option(DummyValue, "_dummy", translate("Interface Setup"))
-       dv.template = "cbi/nullsection"
        btn = s:option(Button, "", translate("Create Uplink Interface"),
-               translate("Automatically create a new wireless wan uplink interface 'trm_wwan', configure it to use dhcp and ")
+               translate("Create a new wireless wan uplink interface, configure it to use dhcp and ")
                .. translate("add it to the wan zone of the firewall. This step has only to be done once."))
        btn.inputtitle = translate("Add Interface")
        btn.inputstyle = "apply"
        btn.disabled = false
        function btn.write()
-               local name = "trm_wwan"
-               local net = nw:add_network(name, { proto = "dhcp" })
+               local net = nw:add_network(iface, { proto = "dhcp" })
                if net then
                        nw:save("network")
                        nw:commit("network")
                        local zone = fw:get_zone_by_network("wan")
                        if zone then
-                               zone:add_network(name)
+                               zone:add_network(iface)
                                fw:save("firewall")
                                fw:commit("firewall")
+                               luci.sys.call("env -i /bin/ubus call network reload >/dev/null 2>&1")
                        end
-                       luci.sys.call("env -i /bin/ubus call network reload >/dev/null 2>&1")
-                       luci.http.redirect(luci.dispatcher.build_url("admin", "services", "travelmate"))
                end
+               luci.http.redirect(luci.dispatcher.build_url("admin", "services", "travelmate"))
        end
-else
-       dv = s:option(DummyValue, "_dummy", translate("Interface Setup"),
-               translate("<br />&nbsp;Network Interface 'trm_wwan' created successfully. ")
-               .. translatef("Scan &amp; Add new wireless stations via standard "
-               .. "<a href=\"%s\">"
-               .. "Wireless Setup</a>", luci.dispatcher.build_url("admin/network/wireless")))
-       dv.template = "cbi/nullsection"
+       return m
 end
 
+-- Main travelmate options
+
+o1 = s:option(Flag, "trm_enabled", translate("Enable travelmate"))
+o1.default = o1.disabled
+o1.rmempty = false
+
+o2 = s:option(Flag, "trm_automatic", translate("Enable 'automatic' mode"),
+       translate("Keep travelmate in an active state. Check every n seconds the connection status, i.e. the uplink availability."))
+o2.default = o2.enabled
+o2.rmempty = false
+
+btn = s:option(Button, "", translate("Manual Rescan"))
+btn:depends("trm_automatic", "")
+btn.inputtitle = translate("Rescan")
+btn.inputstyle = "find"
+btn.disabled = false
+function btn.write()
+       luci.sys.call("/etc/init.d/travelmate start >/dev/null 2>&1")
+       luci.http.redirect(luci.dispatcher.build_url("admin", "services", "travelmate"))
+end
+
+o3 = s:option(Value, "trm_iface", translate("Uplink / Trigger interface"),
+       translate("Name of the uplink interface that triggers travelmate processing in 'manual' mode."))
+o3.datatype = "and(uciname,rangelength(3,15))"
+o3.default = trmiface
+o3.rmempty = false
+
+o4 = s:option(Value, "trm_triggerdelay", translate("Trigger delay"),
+       translate("Additional trigger delay in seconds before travelmate processing begins."))
+o4.default = 2
+o4.datatype = "range(1,90)"
+o4.rmempty = false
+
+o5 = s:option(Flag, "trm_debug", translate("Enable verbose debug logging"))
+o5.default = o5.disabled
+o5.rmempty = false
+
 -- Runtime information
 
 ds = s:option(DummyValue, "_dummy", translate("Runtime information"))
diff --git a/applications/luci-app-travelmate/luasrc/model/cbi/travelmate/wifi_add.lua b/applications/luci-app-travelmate/luasrc/model/cbi/travelmate/wifi_add.lua
new file mode 100644 (file)
index 0000000..979307e
--- /dev/null
@@ -0,0 +1,69 @@
+-- Copyright 2017 Dirk Brenken (dev@brenken.org)
+-- This is free software, licensed under the Apache License, Version 2.0
+
+local fs = require("nixio.fs")
+local uci = require("luci.model.uci").cursor()
+local http = require("luci.http")
+local trmiface = uci.get("travelmate", "global", "trm_iface") or "trm_wwan"
+
+m = SimpleForm("add", translate("Add Wireless Uplink Configuration"))
+m.cancel = translate("Back to overview")
+m.reset = false
+
+function m.on_cancel()
+       http.redirect(luci.dispatcher.build_url("admin/services/travelmate/stations"))
+end
+
+m.hidden = {
+       device      = http.formvalue("device"),
+       ssid        = http.formvalue("ssid"),
+       wep         = http.formvalue("wep"),
+       wpa_suites      = http.formvalue("wpa_suites"),
+       wpa_version = http.formvalue("wpa_version")
+}
+
+if m.hidden.ssid ~= "" then
+       wssid = m:field(Value, "ssid", translate("SSID"))
+       wssid.default = m.hidden.ssid
+else
+       wssid = m:field(Value, "ssid", translate("SSID (hidden)"))
+end
+
+if (tonumber(m.hidden.wep) or 0) == 1 then
+       wkey = m:field(Value, "key", translate("WEP passphrase"),
+               translate("Specify the secret encryption key here."))
+       wkey.password = true
+       wkey.datatype = "wepkey"
+elseif (tonumber(m.hidden.wpa_version) or 0) > 0 and
+       (m.hidden.wpa_suites == "PSK" or m.hidden.wpa_suites == "PSK2")
+then
+       wkey = m:field(Value, "key", translate("WPA passphrase"),
+               translate("Specify the secret encryption key here."))
+       wkey.password = true
+       wkey.datatype = "wpakey"
+end
+
+function wssid.write(self, section, value)
+       newsection = uci:section("wireless", "wifi-iface", nil, {
+               mode       = "sta",
+               network    = trmiface,
+               device     = m.hidden.device,
+               ssid       = wssid:formvalue(section),
+               disabled   = "1"
+       })
+       if (tonumber(m.hidden.wep) or 0) == 1 then
+               uci:set("wireless", newsection, "encryption", "wep-open")
+               uci:set("wireless", newsection, "key", "1")
+               uci:set("wireless", newsection, "key1", wkey:formvalue(section))
+       elseif (tonumber(m.hidden.wpa_version) or 0) > 0 then
+               uci:set("wireless", newsection, "encryption", "psk2")
+               uci:set("wireless", newsection, "key", wkey:formvalue(section))
+       else
+               uci:set("wireless", newsection, "encryption", "none")
+       end
+       uci:save("wireless")
+       uci:commit("wireless")
+       http.redirect(luci.dispatcher.build_url("admin/services/travelmate/stations"))
+end
+
+return m
diff --git a/applications/luci-app-travelmate/luasrc/model/cbi/travelmate/wifi_delete.lua b/applications/luci-app-travelmate/luasrc/model/cbi/travelmate/wifi_delete.lua
new file mode 100644 (file)
index 0000000..97ec1ca
--- /dev/null
@@ -0,0 +1,14 @@
+-- Copyright 2017 Dirk Brenken (dev@brenken.org)
+-- This is free software, licensed under the Apache License, Version 2.0
+
+local uci = require("luci.model.uci").cursor()
+local http = require("luci.http")
+local cfg = http.formvalue("cfg")
+
+if cfg ~= nil then
+       uci:delete("wireless", cfg)
+       uci:save("wireless")
+       uci:commit("wireless")
+end
+
+http.redirect(luci.dispatcher.build_url("admin/services/travelmate/stations"))
diff --git a/applications/luci-app-travelmate/luasrc/model/cbi/travelmate/wifi_edit.lua b/applications/luci-app-travelmate/luasrc/model/cbi/travelmate/wifi_edit.lua
new file mode 100644 (file)
index 0000000..0bae984
--- /dev/null
@@ -0,0 +1,49 @@
+-- Copyright 2017 Dirk Brenken (dev@brenken.org)
+-- This is free software, licensed under the Apache License, Version 2.0
+
+local fs = require("nixio.fs")
+local uci = require("luci.model.uci").cursor()
+local http = require("luci.http")
+
+m = SimpleForm("edit", translate("Edit Wireless Uplink Configuration"))
+m.cancel = translate("Back to overview")
+m.reset = false
+
+function m.on_cancel()
+       http.redirect(luci.dispatcher.build_url("admin/services/travelmate/stations"))
+end
+
+m.hidden = {
+       cfg = http.formvalue("cfg")
+}
+
+local s = uci:get_all("wireless", m.hidden.cfg)
+if s ~= nil then
+       wssid = m:field(Value, "ssid", translate("SSID"))
+       wssid.default = s.ssid
+       
+       if s.encryption and s.key then
+               wkey = m:field(Value, "key", translatef("Passphrase (%s)", s.encryption))
+               wkey.password = true
+               wkey.default = s.key
+               if s.encryption == "wep" then
+                       wkey.datatype = "wepkey"
+               else
+                       wkey.datatype = "wpakey"
+               end
+       end
+else
+       http.redirect(luci.dispatcher.build_url("admin/services/travelmate/stations"))
+end
+
+function wssid.write(self, section, value)
+       uci:set("wireless", m.hidden.cfg, "ssid", wssid:formvalue(section))
+       if s.encryption and s.key then
+               uci:set("wireless", m.hidden.cfg, "key", wkey:formvalue(section))
+       end
+       uci:save("wireless")
+       uci:commit("wireless")
+       http.redirect(luci.dispatcher.build_url("admin/services/travelmate/stations"))
+end
+
+return m
diff --git a/applications/luci-app-travelmate/luasrc/model/cbi/travelmate/wifi_order.lua b/applications/luci-app-travelmate/luasrc/model/cbi/travelmate/wifi_order.lua
new file mode 100644 (file)
index 0000000..d53e1f5
--- /dev/null
@@ -0,0 +1,51 @@
+-- Copyright 2017 Dirk Brenken (dev@brenken.org)
+-- This is free software, licensed under the Apache License, Version 2.0
+
+local http = require("luci.http")
+local cfg = http.formvalue("cfg")
+local dir = http.formvalue("dir")
+local uci = require("luci.model.uci").cursor()
+local trmiface = uci:get("travelmate", "global", "trm_iface") or "trm_wwan"
+
+if cfg ~= nil then
+       local iface = ""
+       local section = ""
+       local idx = ""
+       local idx_change = ""
+       if dir == "up" then
+               uci:foreach("wireless", "wifi-iface", function(s)
+                       iface = s.network
+                       if iface == trmiface then
+                               section = s['.name']
+                               if cfg == section then
+                                       idx = s['.index']
+                               else
+                                       idx_change = s['.index']
+                               end
+                               if idx ~= "" and idx_change ~= "" and idx_change < idx then
+                                       uci:reorder("wireless", cfg, idx_change)
+                                       idx = ""
+                               end
+                       end
+               end)
+       elseif dir == "down" then
+               uci:foreach("wireless", "wifi-iface", function(s)
+                       iface = s.network
+                       if iface == trmiface then
+                               section = s['.name']
+                               if cfg == section then
+                                       idx = s['.index']
+                               else
+                                       idx_change = s['.index']
+                               end
+                               if idx ~= "" and idx_change ~= "" and idx_change > idx then
+                                       uci:reorder("wireless", cfg, idx_change)
+                                       idx = ""
+                               end
+                       end
+               end)
+       end
+       uci:save("wireless")
+       uci:commit("wireless")
+end
+http.redirect(luci.dispatcher.build_url("admin/services/travelmate/stations"))
index ee3a455..2b98855 100644 (file)
@@ -5,6 +5,6 @@ This is free software, licensed under the Apache License, Version 2.0
 
 <%+cbi/valueheader%>
 
-<input name="runtime" id="runtime" type="text" class="cbi-input-text" style="border: none; box-shadow: none; background-color: #ffffff; color: #0069d6;" value="<%=self:cfgvalue(section)%>" disabled="disabled" />
+<input name="runtime" id="runtime" type="text" class="cbi-input-text" style="border:none; box-shadow:none; background-color:#ffffff; color:#0069d6;" value="<%=self:cfgvalue(section)%>" disabled="disabled" />
 
 <%+cbi/valuefooter%>
diff --git a/applications/luci-app-travelmate/luasrc/view/travelmate/stations.htm b/applications/luci-app-travelmate/luasrc/view/travelmate/stations.htm
new file mode 100644 (file)
index 0000000..f1f2680
--- /dev/null
@@ -0,0 +1,78 @@
+<%#
+Copyright 2017 Dirk Brenken (dev@brenken.org)
+This is free software, licensed under the Apache License, Version 2.0
+-%>
+
+<%-
+  local write = io.write
+  local uci = require "luci.model.uci".cursor()
+  local trmiface = uci:get("travelmate", "global", "trm_iface") or "trm_wwan"
+-%>
+
+<%+header%>
+
+<div class="cbi-map">
+<h2 name="content"><%:Wireless Stations%></h2>
+<div class="cbi-map-descr">
+  <%=translatef("Provides an overview of all configured uplinks for the travelmate interface (%s). You can edit, delete or re-order existing uplinks or scan for a new one. The currently used uplink is emphasized in blue.", trmiface)%>
+</div>
+
+<fieldset class="cbi-section">
+  <table class="cbi-section-table" style="empty-cells:hide">
+    <tr class="cbi-section-table-titles">
+      <th class="cbi-section-table-cell" style="text-align:left"><%:Device%></th>
+      <th class="cbi-section-table-cell" style="text-align:left"><%:SSID%></th>
+      <th class="cbi-section-table-cell" style="text-align:left"><%:Encryption%></th>
+      <th class="cbi-section-table-cell" style="text-align:center" colspan="2"><%:Actions%></th>
+    </tr>
+<%
+  uci:foreach("wireless", "wifi-iface", function(s)
+    local iface = s.network or ""
+    if iface == trmiface then
+      local section = s['.name']
+      local device = s.device or ""
+      local mode = s.mode or ""
+      local ssid = s.ssid or ""
+      local encryption = s.encryption or ""
+      local disabled = s.disabled or ""
+      local style = "color:#000000"
+      if disabled == "0" then
+        style = "color:#0069d6;font-weight:bold"
+      end
+%>
+    <tr class="cbi-section-table-row cbi-rowstyle-1" style="<%=style%>">
+      <td style="text-align:left"><%=device%></td>
+      <td style="text-align:left"><%=ssid%></td>
+      <td style="text-align:left"><%=encryption%></td>
+      <td class="cbi-value-field" style="width:70px;text-align:right">
+        <input class="cbi-button cbi-button-up" type="button" value="" onclick="location.href='<%=url('admin/services/travelmate/wifiorder')%>?cfg=<%=section%>;dir=up'" alt="<%:Move up%>" title="<%:Move up%>"/>
+        <input class="cbi-button cbi-button-down" type="button" value="" onclick="location.href='<%=url('admin/services/travelmate/wifiorder')%>?cfg=<%=section%>;dir=down'" alt="<%:Move down%>" title="<%:Move down%>"/>
+      </td>
+      <td class="cbi-value-field" style="width:150px;text-align:right">
+        <input type="button" class="cbi-button cbi-button-edit" onclick="location.href='<%=url('admin/services/travelmate/wifiedit')%>?cfg=<%=section%>'" title="<%:Edit this Uplink%>" value="<%:Edit%>"/>
+        <input type="button" class="cbi-button cbi-button-remove" onclick="location.href='<%=url('admin/services/travelmate/wifidelete')%>?cfg=<%=section%>'" title="<%:Delete this Uplink%>" value="<%:Delete%>"/>
+      </td>
+    </tr>
+<% 
+    end
+  end)
+%>
+  </table>
+</fieldset>
+<div class="cbi-page-actions right">
+<%
+  uci:foreach("wireless", "wifi-device", function(s)
+    local device = s[".name"]
+%>
+  <form class="inline" action="<%=url('admin/services/travelmate/wifiscan')%>" method="post">
+    <input type="hidden" name="device" value="<%=device%>"/>
+    <input type="hidden" name="token" value="<%=token%>"/>
+    <input type="submit" class="cbi-button cbi-button-find" title="<%:Find and join network on %><%=device%>" value="<%:Scan %><%=device%>"/>
+  </form>
+<%
+  end)
+%>
+</div>
+</div>
+
+<%+footer%>
diff --git a/applications/luci-app-travelmate/luasrc/view/travelmate/wifi_scan.htm b/applications/luci-app-travelmate/luasrc/view/travelmate/wifi_scan.htm
new file mode 100644 (file)
index 0000000..dea107e
--- /dev/null
@@ -0,0 +1,90 @@
+<%#
+Copyright 2017 Dirk Brenken (dev@brenken.org)
+This is free software, licensed under the Apache License, Version 2.0
+-%>
+
+<%-
+    local sys = require "luci.sys"
+    local utl = require "luci.util"
+    local dev = luci.http.formvalue("device")
+    local iw = luci.sys.wifi.getiwinfo(dev)
+
+    if not iw then
+        luci.http.redirect(luci.dispatcher.build_url("admin/services/travelmate/stations"))
+    end
+
+    function format_wifi_encryption(info)
+        if info.wep == true then
+            return translate("WEP")
+        elseif info.wpa > 0 then
+            return translate("WPA / WPA2")
+        elseif info.enabled then
+            return translate("Unknown")
+        else
+            return translate("Open")
+        end
+    end
+
+    function percent_wifi_signal(info)
+        local qc = info.quality or 0
+        local qm = info.quality_max or 0
+        if info.bssid and qc > 0 and qm > 0 then
+            return math.floor((100 / qm) * qc)
+        else
+            return 0
+        end
+    end
+-%>
+
+<%+header%>
+
+<div class="cbi-map">
+<h2 name="content"><%:Wireless Scan%></h2>
+    <fieldset class="cbi-section">
+        <table class="cbi-section-table" style="empty-cells:hide">
+            <tr class="cbi-section-table-titles">
+                <th class="cbi-section-table-cell" style="text-align:left"><%:Uplink SSID%></th>
+                <th class="cbi-section-table-cell" style="text-align:left"><%:Encryption%></th>
+                <th class="cbi-section-table-cell" style="text-align:left" colspan="2"><%:Signal strength%></th>
+            </tr>
+            <% for i, net in ipairs(iw.scanlist or { }) do%>
+            <tr class="cbi-section-table-row cbi-rowstyle-1">
+                <td class="cbi-value-field" style="text-align:left">
+                    <strong><%=net.ssid and utl.pcdata(net.ssid) or "<em>%s</em>" % translate("hidden")%></strong>
+                </td>
+                <td class="cbi-value-field" style="text-align:left">
+                    <%=format_wifi_encryption(net.encryption)%>
+                </td>
+                <td class="cbi-value-field" style="text-align:left">
+                    <%=percent_wifi_signal(net)%> %
+                </td>
+                <td class="cbi-value-field" style="width:100px;text-align:right">
+                    <form class="inline" action="<%=url('admin/services/travelmate/wifiadd')%>" method="post">
+                        <input type="hidden" name="token" value="<%=token%>"/>
+                        <input type="hidden" name="device" value="<%=utl.pcdata(dev)%>"/>
+                        <input type="hidden" name="ssid" value="<%=utl.pcdata(net.ssid)%>"/>
+                        <input type="hidden" name="wep" value="<%=net.encryption.wep and 1 or 0%>"/>
+                        <% if net.encryption.wpa then %>
+                        <input type="hidden" name="wpa_version" value="<%=net.encryption.wpa%>"/>
+                        <% for _, v in ipairs(net.encryption.auth_suites) do %><input type="hidden" name="wpa_suites" value="<%=v%>"/>
+                        <% end; end %>
+                        <input class="cbi-button cbi-button-apply" type="submit" value="<%:Add Uplink%>"/>
+                    </form>
+                </td>
+            </tr>
+            <% end %>
+        </table>
+    </fieldset>
+<div class="cbi-page-actions right">
+    <form class="inline" action="<%=url('admin/services/travelmate/stations')%>" method="post">
+        <input class="cbi-button cbi-button-reset" type="submit" value="<%:Back to overview%>"/>
+    </form>
+    <form class="inline" action="<%=url('admin/services/travelmate/wifiscan')%>" method="post">
+        <input type="hidden" name="token" value="<%=token%>"/>
+        <input type="hidden" name="device" value="<%=utl.pcdata(dev)%>"/>
+        <input class="cbi-button cbi-input-find" type="submit" value="<%:Repeat scan%>"/>
+    </form>
+</div>
+</div>
+
+<%+footer%>
index e4a8b8b..150ef70 100644 (file)
@@ -7,18 +7,23 @@ msgstr ""
 "Language-Team: \n"
 "MIME-Version: 1.0\n"
 "Content-Transfer-Encoding: 8bit\n"
-"X-Generator: Poedit 2.0.1\n"
+"X-Generator: Poedit 2.0.3\n"
 "Last-Translator: INAGAKI Hiroshi <musashino.open@gmail.com>\n"
 "Plural-Forms: nplurals=1; plural=0;\n"
 "Language: ja\n"
 
-msgid "<br />&nbsp;Network Interface 'trm_wwan' created successfully."
-msgstr ""
-"<br />&nbsp;ネットワーク インターフェース 'trm_wwan' の作成に成功しました。"
+msgid "Actions"
+msgstr "操作"
 
 msgid "Add Interface"
 msgstr "インターフェースの追加"
 
+msgid "Add Uplink"
+msgstr "アップリンクの追加"
+
+msgid "Add Wireless Uplink Configuration"
+msgstr "無線アップリンク追加の設定"
+
 msgid ""
 "Additional trigger delay in seconds before travelmate processing begins."
 msgstr "Travelmate の処理が開始されるまでの、追加の遅延時間(秒)です。"
@@ -26,12 +31,8 @@ msgstr "Travelmate の処理が開始されるまでの、追加の遅延時間
 msgid "Advanced"
 msgstr "詳細設定"
 
-msgid ""
-"Automatically create a new wireless wan uplink interface 'trm_wwan', "
-"configure it to use dhcp and"
-msgstr ""
-"新しい無線 WAN インターフェース 'trm_wwan' を自動的に作成し、 DHCP を使用する"
-"よう構成して"
+msgid "Back to overview"
+msgstr "概要へ戻る"
 
 msgid ""
 "Configuration of the travelmate package to to enable travel router "
@@ -45,6 +46,24 @@ msgstr "接続制限"
 msgid "Create Uplink Interface"
 msgstr "アップリンク インターフェースの作成"
 
+msgid ""
+"Create a new wireless wan uplink interface, configure it to use dhcp and"
+msgstr ""
+"新規の無線 WAN アップリンク インターフェースを作成し、 DHCP を使用するよう構"
+"成して"
+
+msgid "Delete"
+msgstr "削除"
+
+msgid "Delete this Uplink"
+msgstr "このアップリンクを削除"
+
+msgid "Device"
+msgstr "デバイス"
+
+msgid "Edit"
+msgstr "編集"
+
 msgid "Edit Firewall Configuration"
 msgstr "ファイアウォール設定の編集"
 
@@ -57,6 +76,12 @@ msgstr "Travelmate 設定の編集"
 msgid "Edit Wireless Configuration"
 msgstr "無線設定の編集"
 
+msgid "Edit Wireless Uplink Configuration"
+msgstr "無線アップリンク設定の編集"
+
+msgid "Edit this Uplink"
+msgstr "このアップリンクを編集"
+
 msgid "Enable 'automatic' mode"
 msgstr "'automatic' モードの有効化"
 
@@ -66,15 +91,21 @@ msgstr "Travelmate の有効化"
 msgid "Enable verbose debug logging"
 msgstr "詳細なデバッグ ログの有効化"
 
+msgid "Encryption"
+msgstr "暗号化"
+
 msgid "Extra options"
 msgstr "拡張オプション"
 
+msgid "Find and join network on"
+msgstr "ネットワークの検索と参加:"
+
 msgid ""
 "For further information <a href=\"%s\" target=\"_blank\">see online "
 "documentation</a>"
 msgstr ""
-"詳細な情報は <a href=\"%s\" target=\"_blank\">オンライン ドキュメント</a>を確"
-"認してください。"
+"詳細な情報は <a href=\"%s\" target=\"_blank\">オンライン ドキュメント</a> を"
+"認してください。"
 
 msgid "How long should travelmate wait for a successful wlan interface reload"
 msgstr ""
@@ -82,26 +113,49 @@ msgstr ""
 "す。"
 
 msgid "How many times should travelmate try to connect to an Uplink"
-msgstr "Travelmate ã\81\8cã\82¢ã\83\83ã\83\97ã\83ªã\83³ã\82¯ã\81«å¯¾ã\81\97ã\81¦接続を試行する回数です。"
+msgstr "Travelmate ã\81\8cã\82¢ã\83\83ã\83\97ã\83ªã\83³ã\82¯ã\81¸ã\81®接続を試行する回数です。"
 
 msgid "Input file not found, please check your configuration."
 msgstr "入力ファイルが見つかりません。設定を確認してください。"
 
-msgid "Interface Setup"
-msgstr "インターフェース設定"
-
 msgid "Interface Timeout"
 msgstr "インターフェース タイムアウト"
 
-msgid "Keep travelmate in an active state."
-msgstr "Travelmate をアクティブ状態で維持します。"
+msgid "Interface Wizard"
+msgstr "インターフェース ウィザード"
+
+msgid ""
+"Keep travelmate in an active state. Check every n seconds the connection "
+"status, i.e. the uplink availability."
+msgstr ""
+"Travelmate をアクティブ状態で維持します。 n秒ごとにアップリンクの可用性確認と"
+"して、接続状態をチェックします"
 
 msgid "Last rundate"
 msgstr "最終実行日時"
 
+msgid "Manual Rescan"
+msgstr "手動再スキャン"
+
+msgid "Move down"
+msgstr "下へ"
+
+msgid "Move up"
+msgstr "上へ"
+
+msgid ""
+"Name of the uplink interface that triggers travelmate processing in 'manual' "
+"mode."
+msgstr ""
+"'manual' モード時に Travelmate の処理のトリガーとなる、アップリンク インター"
+"フェースの名前です。"
+
 msgid "Online Status"
 msgstr "オンライン ステータス"
 
+msgid "Open"
+msgstr "オープン"
+
 msgid ""
 "Options for further tweaking in case the defaults are not suitable for you."
 msgstr "デフォルトの設定が適切でない場合、さらに設定するためのオプションです。"
@@ -112,11 +166,27 @@ msgstr "全体タイムアウト"
 msgid "Overview"
 msgstr "概要"
 
+msgid "Passphrase (%s)"
+msgstr "暗号フレーズ (%s)"
+
+msgid ""
+"Provides an overview of all configured uplinks for the travelmate interface "
+"(%s). You can edit, delete or re-order existing uplinks or scan for a new "
+"one. The currently used uplink is emphasized in blue."
+msgstr ""
+"Travelmate 用インターフェース(%s)に設定済みの全アップリンクの一覧です。既存"
+"のアップリンクの編集や削除、並べ替えを行ったり、スキャンを行って新規アップリ"
+"ンクを追加することができます。現在使用されているアップリンクは、青色で強調さ"
+"れます。"
+
 msgid "Radio selection"
 msgstr "無線の選択"
 
-msgid "Restrict interface trigger to certain interface(s)"
-msgstr "インターフェース トリガーを特定のインターフェースに限定する"
+msgid "Repeat scan"
+msgstr "再スキャン"
+
+msgid "Rescan"
+msgstr "再スキャン"
 
 msgid "Restrict travelmate to a dedicated radio, e.g. 'radio0'"
 msgstr "Travelmate が特定の無線に接続するようにします。例: 'radio0'"
@@ -124,26 +194,26 @@ msgstr "Travelmate が特定の無線に接続するようにします。例: 'r
 msgid "Runtime information"
 msgstr "実行情報"
 
-msgid ""
-"Scan &amp; Add new wireless stations via standard <a href=\"%s\">Wireless "
-"Setup</a>"
-msgstr ""
-"通常の<a href=\"%s\">無線設定</a>にて、新規の無線ステーションのスキャン及び追"
-"加を行います。"
+msgid "SSID"
+msgstr "SSID"
 
-msgid ""
-"Space separated list of interfaces that trigger travelmate processing. To "
-"disable event driven (re-)starts remove all entries."
-msgstr ""
-"Travelmate の処理のトリガーとなる、スペースで区切られたインターフェースのリス"
-"トです。処理を発生させるイベントを無効にするには、全てのエントリーを削除して"
-"空欄にします。"
+msgid "SSID (hidden)"
+msgstr "SSID(ステルス)"
+
+msgid "Scan"
+msgstr "スキャン:"
+
+msgid "Signal strength"
+msgstr "信号強度"
+
+msgid "Specify the secret encryption key here."
+msgstr "暗号キーを設定します。"
 
 msgid "Station Interface"
 msgstr "ステーション インターフェース"
 
 msgid "Station Radio"
-msgstr "ステーション 無線"
+msgstr "ステーション電波"
 
 msgid "Station SSID"
 msgstr "ステーション SSID"
@@ -198,9 +268,39 @@ msgstr "Travelmate バージョン"
 msgid "Trigger delay"
 msgstr "トリガー遅延"
 
+msgid "Unknown"
+msgstr "不明"
+
+msgid "Uplink / Trigger interface"
+msgstr "アップリンク/トリガー インターフェース"
+
+msgid "Uplink SSID"
+msgstr "アップリンク SSID"
+
+msgid "Uplink interface"
+msgstr "アップリンク インターフェース"
+
 msgid "View Logfile"
 msgstr "ログファイルの確認"
 
+msgid "WEP"
+msgstr "WEP"
+
+msgid "WEP passphrase"
+msgstr "WEP 暗号キー"
+
+msgid "WPA / WPA2"
+msgstr "WPA / WPA2"
+
+msgid "WPA passphrase"
+msgstr "WPA 暗号キー"
+
+msgid "Wireless Scan"
+msgstr "無線スキャン"
+
+msgid "Wireless Stations"
+msgstr "無線ステーション"
+
 msgid ""
 "add it to the wan zone of the firewall. This step has only to be done once."
 msgstr ""
@@ -210,126 +310,11 @@ msgstr ""
 msgid "connected"
 msgstr "接続済み"
 
+msgid "hidden"
+msgstr "(不明)"
+
 msgid "n/a"
 msgstr "利用不可"
 
 msgid "not connected"
 msgstr "未接続"
-
-#~ msgid "."
-#~ msgstr "。"
-
-#~ msgid ""
-#~ "Automatically create a new wireless wan interface, configure it to use "
-#~ "dhcp and add it to the wan zone of the firewall. This step has only to be "
-#~ "done once."
-#~ msgstr ""
-#~ "新しい無線 WAN インターフェースを自動的に作成し、DHCP を使用するよう構成し"
-#~ "てファイアウォールの wan ゾーンに追加します。このステップは、一度だけ実行"
-#~ "する必要があります。"
-
-#~ msgid "Direct Link: <a href=\"%s\">Wireless Setup</a>"
-#~ msgstr "ダイレクト リンク: <a href=\"%s\">無線設定</a>"
-
-#~ msgid "For further information"
-#~ msgstr "詳細情報は"
-
-#~ msgid "Name of the new wireless wan interface"
-#~ msgstr "新しい無線 WAN のインターフェース名"
-
-#~ msgid ""
-#~ "Network Interface '%s' created successfully. Feel free to scan & add new "
-#~ "stations via standard wireless setup."
-#~ msgstr ""
-#~ "ネットワーク インターフェース '%s' の作成に成功しました。通常の無線設定に"
-#~ "て、スキャン及び新規ステーションの追加が可能です。"
-
-#~ msgid "Setup WWAN Interface"
-#~ msgstr "WWAN インターフェース設定"
-
-#~ msgid ""
-#~ "The allowed characters are: <code>A-Z</code>, <code>a-z</code>, "
-#~ "<code>0-9</code> and <code>_</code> (3-15 characters)."
-#~ msgstr ""
-#~ "使用可能文字: <code>A-Z</code>, <code>a-z</code>, <code>0-9</code> and "
-#~ "<code>_</code>(3 - 15文字)"
-
-#~ msgid "The given network interface name already exist"
-#~ msgstr "入力されたネットワーク インターフェース名は、既に存在しています。"
-
-#~ msgid "see online documentation"
-#~ msgstr "オンライン ドキュメントを確認してください"
-
-#~ msgid ""
-#~ "Brief advice: Create a wwan interface, configure it to use dhcp and add "
-#~ "it to the wan zone in firewall. Create the wifi interfaces to be used "
-#~ "('client' mode, assigned to wwan network, left as disabled). Travelmate "
-#~ "will try to connect to the known wifi client interfaces in the defined "
-#~ "order."
-#~ msgstr ""
-#~ "簡単な解説: 予めWWANインターフェースを作成し、DHCPを使用するよう構成して"
-#~ "ファイアウォールのWANゾーンに追加します。また、使用される無線インター"
-#~ "フェースを作成しておきます(\"クライアント\" モード、WWANに割り当て、無効"
-#~ "状態)。Travelmateは、登録されている順序で既知の無線クライアント インター"
-#~ "フェースへの接続を試行します。"
-
-#~ msgid ""
-#~ "Configuration of the Travelmate package to enable travel router "
-#~ "functionality."
-#~ msgstr "トラベル ルータ機能を有効にする、Travelmate パッケージの設定です。"
-
-#~ msgid "Debug logging"
-#~ msgstr "デバッグ ログ"
-
-#~ msgid "Default 20, range 10-60"
-#~ msgstr "既定値 20、範囲 10 - 60"
-
-#~ msgid "Default 3, range 1-10"
-#~ msgstr "既定値 3、範囲 1 - 10"
-
-#~ msgid "Disable this if you want to use iwinfo instead of iw"
-#~ msgstr "iw の代わりに iwinfo を使用したい場合、この設定を無効にします。"
-
-#~ msgid "Enable Travelmate"
-#~ msgstr "Travelmateの有効化"
-
-#~ msgid "Global options"
-#~ msgstr "全般オプション"
-
-#~ msgid "Link to detailed advice"
-#~ msgstr "詳細な解説へのリンク"
-
-#~ msgid "Max. number of connection retries to an uplink"
-#~ msgstr "確立までの接続試行回数"
-
-#~ msgid "Max. timeout in seconds for wlan interface reload"
-#~ msgstr "無線LANインターフェース リロード時の最大待機時間(秒)"
-
-#~ msgid "Restrict reload trigger to certain interface(s)"
-#~ msgstr "リロード トリガを特定のインターフェースに限定する"
-
-#~ msgid ""
-#~ "Space separated list of wwan interfaces that trigger reload action. To "
-#~ "disable reload trigger set it to 'false'. Default: empty"
-#~ msgstr ""
-#~ "リロード動作のトリガとなる、スペースで区切られたWWAN インターフェースのリ"
-#~ "ストです。リロードのトリガを無効にするには、'false' を設定します。既定値:"
-#~ "(空)"
-
-#~ msgid "Use iw for scanning"
-#~ msgstr "スキャンに iw を使用する"
-
-#~ msgid "Default 3, range 0-10. Set to 0 to allow unlimited retries"
-#~ msgstr "既定値 3、範囲 0 - 10。再試行回数を制限しない場合、0 に設定します。"
-
-#~ msgid "Default 30, range 5-60"
-#~ msgstr "既定値 30、範囲 5 - 60"
-
-#~ msgid "Default: empty = use all radios."
-#~ msgstr "デフォルト:(空)= 全ての無線を使用"
-
-#~ msgid "Loop timeout in seconds for wlan monitoring"
-#~ msgstr "無線LAN モニターのループ タイムアウト(秒)"
-
-#~ msgid "Use only one radio, e.g. 'radio0'"
-#~ msgstr "単一の無線のみ使用する 例: 'radio0'"
index 4eff34e..d74373d 100644 (file)
@@ -12,12 +12,18 @@ msgstr ""
 "Plural-Forms: nplurals=2; plural=(n > 1);\n"
 "Language: pt_BR\n"
 
-msgid "<br />&nbsp;Network Interface 'trm_wwan' created successfully."
+msgid "Actions"
 msgstr ""
 
 msgid "Add Interface"
 msgstr ""
 
+msgid "Add Uplink"
+msgstr ""
+
+msgid "Add Wireless Uplink Configuration"
+msgstr ""
+
 msgid ""
 "Additional trigger delay in seconds before travelmate processing begins."
 msgstr ""
@@ -25,9 +31,7 @@ msgstr ""
 msgid "Advanced"
 msgstr ""
 
-msgid ""
-"Automatically create a new wireless wan uplink interface 'trm_wwan', "
-"configure it to use dhcp and"
+msgid "Back to overview"
 msgstr ""
 
 msgid ""
@@ -41,6 +45,22 @@ msgstr ""
 msgid "Create Uplink Interface"
 msgstr ""
 
+msgid ""
+"Create a new wireless wan uplink interface, configure it to use dhcp and"
+msgstr ""
+
+msgid "Delete"
+msgstr ""
+
+msgid "Delete this Uplink"
+msgstr ""
+
+msgid "Device"
+msgstr ""
+
+msgid "Edit"
+msgstr ""
+
 msgid "Edit Firewall Configuration"
 msgstr ""
 
@@ -53,6 +73,12 @@ msgstr ""
 msgid "Edit Wireless Configuration"
 msgstr ""
 
+msgid "Edit Wireless Uplink Configuration"
+msgstr ""
+
+msgid "Edit this Uplink"
+msgstr ""
+
 msgid "Enable 'automatic' mode"
 msgstr ""
 
@@ -62,9 +88,15 @@ msgstr ""
 msgid "Enable verbose debug logging"
 msgstr ""
 
+msgid "Encryption"
+msgstr ""
+
 msgid "Extra options"
 msgstr "Opções adicionais"
 
+msgid "Find and join network on"
+msgstr ""
+
 msgid ""
 "For further information <a href=\"%s\" target=\"_blank\">see online "
 "documentation</a>"
@@ -79,21 +111,40 @@ msgstr ""
 msgid "Input file not found, please check your configuration."
 msgstr ""
 
-msgid "Interface Setup"
+msgid "Interface Timeout"
 msgstr ""
 
-msgid "Interface Timeout"
+msgid "Interface Wizard"
 msgstr ""
 
-msgid "Keep travelmate in an active state."
+msgid ""
+"Keep travelmate in an active state. Check every n seconds the connection "
+"status, i.e. the uplink availability."
 msgstr ""
 
 msgid "Last rundate"
 msgstr ""
 
+msgid "Manual Rescan"
+msgstr ""
+
+msgid "Move down"
+msgstr ""
+
+msgid "Move up"
+msgstr ""
+
+msgid ""
+"Name of the uplink interface that triggers travelmate processing in 'manual' "
+"mode."
+msgstr ""
+
 msgid "Online Status"
 msgstr ""
 
+msgid "Open"
+msgstr ""
+
 msgid ""
 "Options for further tweaking in case the defaults are not suitable for you."
 msgstr ""
@@ -104,10 +155,22 @@ msgstr ""
 msgid "Overview"
 msgstr ""
 
+msgid "Passphrase (%s)"
+msgstr ""
+
+msgid ""
+"Provides an overview of all configured uplinks for the travelmate interface "
+"(%s). You can edit, delete or re-order existing uplinks or scan for a new "
+"one. The currently used uplink is emphasized in blue."
+msgstr ""
+
 msgid "Radio selection"
 msgstr ""
 
-msgid "Restrict interface trigger to certain interface(s)"
+msgid "Repeat scan"
+msgstr ""
+
+msgid "Rescan"
 msgstr ""
 
 msgid "Restrict travelmate to a dedicated radio, e.g. 'radio0'"
@@ -116,14 +179,19 @@ msgstr ""
 msgid "Runtime information"
 msgstr ""
 
-msgid ""
-"Scan &amp; Add new wireless stations via standard <a href=\"%s\">Wireless "
-"Setup</a>"
+msgid "SSID"
 msgstr ""
 
-msgid ""
-"Space separated list of interfaces that trigger travelmate processing. To "
-"disable event driven (re-)starts remove all entries."
+msgid "SSID (hidden)"
+msgstr ""
+
+msgid "Scan"
+msgstr ""
+
+msgid "Signal strength"
+msgstr ""
+
+msgid "Specify the secret encryption key here."
 msgstr ""
 
 msgid "Station Interface"
@@ -175,9 +243,39 @@ msgstr ""
 msgid "Trigger delay"
 msgstr ""
 
+msgid "Unknown"
+msgstr ""
+
+msgid "Uplink / Trigger interface"
+msgstr ""
+
+msgid "Uplink SSID"
+msgstr ""
+
+msgid "Uplink interface"
+msgstr ""
+
 msgid "View Logfile"
 msgstr ""
 
+msgid "WEP"
+msgstr ""
+
+msgid "WEP passphrase"
+msgstr ""
+
+msgid "WPA / WPA2"
+msgstr ""
+
+msgid "WPA passphrase"
+msgstr ""
+
+msgid "Wireless Scan"
+msgstr ""
+
+msgid "Wireless Stations"
+msgstr ""
+
 msgid ""
 "add it to the wan zone of the firewall. This step has only to be done once."
 msgstr ""
@@ -185,6 +283,9 @@ msgstr ""
 msgid "connected"
 msgstr ""
 
+msgid "hidden"
+msgstr ""
+
 msgid "n/a"
 msgstr ""
 
index 615c3a7..dc0583f 100644 (file)
@@ -1,12 +1,18 @@
 msgid ""
 msgstr "Content-Type: text/plain; charset=UTF-8"
 
-msgid "<br />&nbsp;Network Interface 'trm_wwan' created successfully."
+msgid "Actions"
 msgstr ""
 
 msgid "Add Interface"
 msgstr ""
 
+msgid "Add Uplink"
+msgstr ""
+
+msgid "Add Wireless Uplink Configuration"
+msgstr ""
+
 msgid ""
 "Additional trigger delay in seconds before travelmate processing begins."
 msgstr ""
@@ -14,9 +20,7 @@ msgstr ""
 msgid "Advanced"
 msgstr ""
 
-msgid ""
-"Automatically create a new wireless wan uplink interface 'trm_wwan', "
-"configure it to use dhcp and"
+msgid "Back to overview"
 msgstr ""
 
 msgid ""
@@ -30,6 +34,22 @@ msgstr ""
 msgid "Create Uplink Interface"
 msgstr ""
 
+msgid ""
+"Create a new wireless wan uplink interface, configure it to use dhcp and"
+msgstr ""
+
+msgid "Delete"
+msgstr ""
+
+msgid "Delete this Uplink"
+msgstr ""
+
+msgid "Device"
+msgstr ""
+
+msgid "Edit"
+msgstr ""
+
 msgid "Edit Firewall Configuration"
 msgstr ""
 
@@ -42,6 +62,12 @@ msgstr ""
 msgid "Edit Wireless Configuration"
 msgstr ""
 
+msgid "Edit Wireless Uplink Configuration"
+msgstr ""
+
+msgid "Edit this Uplink"
+msgstr ""
+
 msgid "Enable 'automatic' mode"
 msgstr ""
 
@@ -51,9 +77,15 @@ msgstr ""
 msgid "Enable verbose debug logging"
 msgstr ""
 
+msgid "Encryption"
+msgstr ""
+
 msgid "Extra options"
 msgstr ""
 
+msgid "Find and join network on"
+msgstr ""
+
 msgid ""
 "For further information <a href=\"%s\" target=\"_blank\">see online "
 "documentation</a>"
@@ -68,21 +100,40 @@ msgstr ""
 msgid "Input file not found, please check your configuration."
 msgstr ""
 
-msgid "Interface Setup"
+msgid "Interface Timeout"
 msgstr ""
 
-msgid "Interface Timeout"
+msgid "Interface Wizard"
 msgstr ""
 
-msgid "Keep travelmate in an active state."
+msgid ""
+"Keep travelmate in an active state. Check every n seconds the connection "
+"status, i.e. the uplink availability."
 msgstr ""
 
 msgid "Last rundate"
 msgstr ""
 
+msgid "Manual Rescan"
+msgstr ""
+
+msgid "Move down"
+msgstr ""
+
+msgid "Move up"
+msgstr ""
+
+msgid ""
+"Name of the uplink interface that triggers travelmate processing in 'manual' "
+"mode."
+msgstr ""
+
 msgid "Online Status"
 msgstr ""
 
+msgid "Open"
+msgstr ""
+
 msgid ""
 "Options for further tweaking in case the defaults are not suitable for you."
 msgstr ""
@@ -93,10 +144,22 @@ msgstr ""
 msgid "Overview"
 msgstr ""
 
+msgid "Passphrase (%s)"
+msgstr ""
+
+msgid ""
+"Provides an overview of all configured uplinks for the travelmate interface "
+"(%s). You can edit, delete or re-order existing uplinks or scan for a new "
+"one. The currently used uplink is emphasized in blue."
+msgstr ""
+
 msgid "Radio selection"
 msgstr ""
 
-msgid "Restrict interface trigger to certain interface(s)"
+msgid "Repeat scan"
+msgstr ""
+
+msgid "Rescan"
 msgstr ""
 
 msgid "Restrict travelmate to a dedicated radio, e.g. 'radio0'"
@@ -105,14 +168,19 @@ msgstr ""
 msgid "Runtime information"
 msgstr ""
 
-msgid ""
-"Scan &amp; Add new wireless stations via standard <a href=\"%s\">Wireless "
-"Setup</a>"
+msgid "SSID"
 msgstr ""
 
-msgid ""
-"Space separated list of interfaces that trigger travelmate processing. To "
-"disable event driven (re-)starts remove all entries."
+msgid "SSID (hidden)"
+msgstr ""
+
+msgid "Scan"
+msgstr ""
+
+msgid "Signal strength"
+msgstr ""
+
+msgid "Specify the secret encryption key here."
 msgstr ""
 
 msgid "Station Interface"
@@ -164,9 +232,39 @@ msgstr ""
 msgid "Trigger delay"
 msgstr ""
 
+msgid "Unknown"
+msgstr ""
+
+msgid "Uplink / Trigger interface"
+msgstr ""
+
+msgid "Uplink SSID"
+msgstr ""
+
+msgid "Uplink interface"
+msgstr ""
+
 msgid "View Logfile"
 msgstr ""
 
+msgid "WEP"
+msgstr ""
+
+msgid "WEP passphrase"
+msgstr ""
+
+msgid "WPA / WPA2"
+msgstr ""
+
+msgid "WPA passphrase"
+msgstr ""
+
+msgid "Wireless Scan"
+msgstr ""
+
+msgid "Wireless Stations"
+msgstr ""
+
 msgid ""
 "add it to the wan zone of the firewall. This step has only to be done once."
 msgstr ""
@@ -174,6 +272,9 @@ msgstr ""
 msgid "connected"
 msgstr ""
 
+msgid "hidden"
+msgstr ""
+
 msgid "n/a"
 msgstr ""
 
index 07aa726..96c73e3 100644 (file)
@@ -19,8 +19,8 @@ msgid ""
 "How often to check internet connection. Default unit is seconds, you can you "
 "use the suffix 'm' for minutes, 'h' for hours or 'd' for days"
 msgstr ""
-"Hur ofta internet-anslutningen ska kollas. Standardenheten är sekunder, du kan använda "
-"tillägget 'm' för minutrar, 't' för timmar eller 'd' för dagar"
+"Hur ofta internet-anslutningen ska kollas. Standardenheten är sekunder, du "
+"kan använda tillägget 'm' för minutrar, 't' för timmar eller 'd' för dagar"
 
 msgid ""
 "In periodic mode, it defines the reboot period. In internet mode, it defines "
index ca4e5aa..50953aa 100644 (file)
@@ -1,5 +1,5 @@
 msgid ""
-msgstr "Content-Type: text/plain; charset=UTF-8"
+msgstr "Content-Type: text/plain; charset=UTF-8\n"
 
 msgid "Activate wifi"
 msgstr "Aktivera wifi"
index b7e3ed5..1aa68e2 100644 (file)
@@ -1,5 +1,5 @@
 msgid ""
-msgstr "Content-Type: text/plain; charset=UTF-8"
+msgstr "Content-Type: text/plain; charset=UTF-8\n"
 
 msgid "Allowed IPs"
 msgstr "Tillåtna IP-adresser"
index 8e66cbc..b819230 100644 (file)
@@ -481,8 +481,9 @@ function cbi_d_check(deps) {
                                istat = (istat && cbi_d_checkvalue(j, deps[i][j]))
                        }
                }
-               if (istat) {
-                       return !reverse;
+
+               if (istat ^ reverse) {
+                       return true;
                }
        }
        return def;
@@ -648,9 +649,6 @@ function cbi_combobox(id, values, def, man, focus) {
        var dt = obj.getAttribute('cbi_datatype');
        var op = obj.getAttribute('cbi_optional');
 
-       if (dt)
-               cbi_validate_field(sel, op == 'true', dt);
-
        if (!values[obj.value]) {
                if (obj.value == "") {
                        var optdef = document.createElement("option");
@@ -685,6 +683,9 @@ function cbi_combobox(id, values, def, man, focus) {
 
        obj.style.display = "none";
 
+       if (dt)
+               cbi_validate_field(sel, op == 'true', dt);
+
        cbi_bind(sel, "change", function() {
                if (sel.selectedIndex == sel.options.length - 1) {
                        obj.style.display = "inline";
@@ -727,7 +728,7 @@ function cbi_filebrowser(id, defpath) {
        browser.focus();
 }
 
-function cbi_browser_init(id, defpath)
+function cbi_browser_init(id, resource, defpath)
 {
        function cbi_browser_btnclick(e) {
                cbi_filebrowser(id, defpath);
@@ -738,7 +739,7 @@ function cbi_browser_init(id, defpath)
 
        var btn = document.createElement('img');
        btn.className = 'cbi-image-button';
-       btn.src = cbi_strings.path.resource + '/cbi/folder.gif';
+       btn.src = (resource || cbi_strings.path.resource) + '/cbi/folder.gif';
        field.parentNode.insertBefore(btn, field.nextSibling);
 
        cbi_bind(btn, 'click', cbi_browser_btnclick);
@@ -805,7 +806,7 @@ function cbi_dynlist_init(parent, datatype, optional, choices)
                        parent.appendChild(b);
                        if (datatype == 'file')
                        {
-                               cbi_browser_init(t.id, parent.getAttribute('data-browser-path'));
+                               cbi_browser_init(t.id, null, parent.getAttribute('data-browser-path'));
                        }
 
                        parent.appendChild(document.createElement('br'));
index 99f3ee2..115c54d 100644 (file)
@@ -348,8 +348,10 @@ end
 
 function net.devices()
        local devs = {}
+       local seen = {}
        for k, v in ipairs(nixio.getifaddrs()) do
-               if v.family == "packet" then
+               if v.name and not seen[v.name] then
+                       seen[v.name] = true
                        devs[#devs+1] = v.name
                end
        end
index 9f4efdd..0486ec2 100644 (file)
@@ -2342,6 +2342,9 @@ msgstr ""
 msgid "Password successfully changed!"
 msgstr "La contrasenya s'ha canviat amb èxit!"
 
+msgid "Password2"
+msgstr ""
+
 msgid "Path to CA-Certificate"
 msgstr "Ruta als Certificats CA"
 
index 9efe3b7..c217b0c 100644 (file)
@@ -2365,6 +2365,9 @@ msgstr ""
 msgid "Password successfully changed!"
 msgstr "Heslo bylo úspěšně změněno!"
 
+msgid "Password2"
+msgstr ""
+
 msgid "Path to CA-Certificate"
 msgstr "Cesta k certifikátu CA"
 
index fa26a1d..183e495 100644 (file)
@@ -2427,6 +2427,9 @@ msgstr "Password des inneren, privaten Schlüssels"
 msgid "Password successfully changed!"
 msgstr "Passwort erfolgreich geändert!"
 
+msgid "Password2"
+msgstr ""
+
 msgid "Path to CA-Certificate"
 msgstr "Pfad zum CA-Zertifikat"
 
index e3969af..b385651 100644 (file)
@@ -2372,6 +2372,9 @@ msgstr ""
 msgid "Password successfully changed!"
 msgstr "Ο κωδικός πρόσβασης άλλαξε επιτυχώς!"
 
+msgid "Password2"
+msgstr ""
+
 msgid "Path to CA-Certificate"
 msgstr "Διαδρομή για Πιστοποιητικό CA"
 
index 6537370..0420733 100644 (file)
@@ -2339,6 +2339,9 @@ msgstr ""
 msgid "Password successfully changed!"
 msgstr ""
 
+msgid "Password2"
+msgstr ""
+
 msgid "Path to CA-Certificate"
 msgstr "Path to CA-Certificate"
 
index a347673..626e374 100644 (file)
@@ -2379,6 +2379,9 @@ msgstr ""
 msgid "Password successfully changed!"
 msgstr "¡Contraseña cambiada!"
 
+msgid "Password2"
+msgstr ""
+
 msgid "Path to CA-Certificate"
 msgstr "Ruta al Certificado CA"
 
index 1aab5cb..b0b4b43 100644 (file)
@@ -2392,6 +2392,9 @@ msgstr ""
 msgid "Password successfully changed!"
 msgstr "Mot de passe changé avec succès !"
 
+msgid "Password2"
+msgstr ""
+
 msgid "Path to CA-Certificate"
 msgstr "Chemin de la CA"
 
index d8eae1f..2c2c5d2 100644 (file)
@@ -2306,6 +2306,9 @@ msgstr ""
 msgid "Password successfully changed!"
 msgstr ""
 
+msgid "Password2"
+msgstr ""
+
 msgid "Path to CA-Certificate"
 msgstr ""
 
index dfed45d..8f5aee4 100644 (file)
@@ -2382,6 +2382,9 @@ msgstr ""
 msgid "Password successfully changed!"
 msgstr "A jelszó megváltoztatása sikeres!"
 
+msgid "Password2"
+msgstr ""
+
 msgid "Path to CA-Certificate"
 msgstr "CA tanúsítvány elérési útja"
 
index 04331ab..ea7578e 100644 (file)
@@ -2378,6 +2378,9 @@ msgstr ""
 msgid "Password successfully changed!"
 msgstr "Password cambiata con successo!"
 
+msgid "Password2"
+msgstr ""
+
 msgid "Path to CA-Certificate"
 msgstr "Percorso al certificato CA"
 
index 714b064..7d23abe 100644 (file)
@@ -3,14 +3,14 @@ msgstr ""
 "Project-Id-Version: \n"
 "Report-Msgid-Bugs-To: \n"
 "POT-Creation-Date: 2009-06-10 03:40+0200\n"
-"PO-Revision-Date: 2017-04-03 02:32+0900\n"
+"PO-Revision-Date: 2017-07-28 12:17+0900\n"
 "Last-Translator: INAGAKI Hiroshi <musashino.open@gmail.com>\n"
 "Language: ja\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=1; plural=0;\n"
-"X-Generator: Poedit 2.0\n"
+"X-Generator: Poedit 2.0.3\n"
 "Language-Team: \n"
 
 msgid "%s is untagged in multiple VLANs!"
@@ -44,7 +44,7 @@ msgid "-- match by label --"
 msgstr "-- ラベルを指定 --"
 
 msgid "-- match by uuid --"
-msgstr "-- UUIDを指定 --"
+msgstr "-- UUID を指定 --"
 
 msgid "1 Minute Load:"
 msgstr "過去1分の負荷:"
@@ -157,6 +157,8 @@ msgid ""
 "<br/>Note: you need to manually restart the cron service if the crontab file "
 "was empty before editing."
 msgstr ""
+"<br />注意: 編集前の crontab ファイルが空の場合、手動で cron サービスの再起動"
+"を行う必要があります。"
 
 msgid "A43C + J43 + A43"
 msgstr ""
@@ -283,7 +285,7 @@ msgid "Allocate IP sequentially"
 msgstr ""
 
 msgid "Allow <abbr title=\"Secure Shell\">SSH</abbr> password authentication"
-msgstr "<abbr title=\"Secure Shell\">SSH</abbr> パスワード認証を許可します"
+msgstr "<abbr title=\"Secure Shell\">SSH</abbr> パスワード認証を許可します"
 
 msgid "Allow all except listed"
 msgstr "リスト内の端末からのアクセスを禁止"
@@ -299,10 +301,10 @@ msgstr ""
 "リモートホストがSSH転送されたローカルのポートに接続することを許可します"
 
 msgid "Allow root logins with password"
-msgstr "ã\83\91ã\82¹ã\83¯ã\83¼ã\83\89ã\82\92使ç\94¨ã\81\97ã\81\9froot権é\99\90ã\81§ã\81®ã\83­ã\82°ã\82¤ã\83³ã\82\92許å\8f¯ã\81\99ã\82\8b"
+msgstr "ã\83\91ã\82¹ã\83¯ã\83¼ã\83\89ã\81§ã\81® root ã\83­ã\82°ã\82¤ã\83³ã\82\92許å\8f¯"
 
 msgid "Allow the <em>root</em> user to login with password"
-msgstr "パスワードを使用した<em>root</em>権限でのログインを許可する"
+msgstr "パスワードを使用した <em>root</em> 権限でのログインを許可します。"
 
 msgid ""
 "Allow upstream responses in the 127.0.0.0/8 range, e.g. for RBL services"
@@ -515,8 +517,8 @@ msgid ""
 "defined backup patterns."
 msgstr ""
 "以下は、バックアップの際に含まれるファイルのリストです。このリストは、opkgに"
-"よって認識されている設定ファイル、重要なベースファイル、ユーザーが設定した"
-"規表現に一致したファイルの一覧です。"
+"よって認識されている設定ファイル、重要なベースファイル、ユーザーが設定した"
+"ターンに一致したファイルの一覧です。"
 
 msgid "Bind interface"
 msgstr ""
@@ -858,7 +860,7 @@ msgid "Device is rebooting..."
 msgstr "デバイスを再起動中です..."
 
 msgid "Device unreachable"
-msgstr ""
+msgstr "デバイスに到達できません"
 
 msgid "Diagnostics"
 msgstr "診断機能"
@@ -1217,7 +1219,7 @@ msgid "Force TKIP and CCMP (AES)"
 msgstr "TKIP 及びCCMP (AES) を使用"
 
 msgid "Force link"
-msgstr ""
+msgstr "強制リンク"
 
 msgid "Force use of NAT-T"
 msgstr "NAT-Tの強制使用"
@@ -1467,7 +1469,7 @@ msgid "IPv6 routed prefix"
 msgstr ""
 
 msgid "IPv6 suffix"
-msgstr ""
+msgstr "IPv6 サフィックス"
 
 msgid "IPv6-Address"
 msgstr "IPv6-アドレス"
@@ -2380,6 +2382,9 @@ msgstr ""
 msgid "Password successfully changed!"
 msgstr "パスワードを変更しました"
 
+msgid "Password2"
+msgstr "パスワード2"
+
 msgid "Path to CA-Certificate"
 msgstr "CA証明書のパス"
 
@@ -3672,7 +3677,7 @@ msgid "Waiting for command to complete..."
 msgstr "コマンド実行中です..."
 
 msgid "Waiting for device..."
-msgstr "デバイスの起動をお待ちください..."
+msgstr "デバイスの起動を待っています..."
 
 msgid "Warning"
 msgstr "警告"
index 587b489..770a49c 100644 (file)
@@ -2332,6 +2332,9 @@ msgstr ""
 msgid "Password successfully changed!"
 msgstr ""
 
+msgid "Password2"
+msgstr ""
+
 msgid "Path to CA-Certificate"
 msgstr ""
 
index 9791218..c2f6272 100644 (file)
@@ -2311,6 +2311,9 @@ msgstr ""
 msgid "Password successfully changed!"
 msgstr ""
 
+msgid "Password2"
+msgstr ""
+
 msgid "Path to CA-Certificate"
 msgstr "Path ke CA-Sijil"
 
index 9e3df81..6a6e818 100644 (file)
@@ -2357,6 +2357,9 @@ msgstr ""
 msgid "Password successfully changed!"
 msgstr "Passordet er endret!"
 
+msgid "Password2"
+msgstr ""
+
 msgid "Path to CA-Certificate"
 msgstr "Sti til CA-sertifikat"
 
index 98067c8..e364616 100644 (file)
@@ -2401,6 +2401,9 @@ msgstr ""
 msgid "Password successfully changed!"
 msgstr "Pomyślnie zmieniono hasło!"
 
+msgid "Password2"
+msgstr ""
+
 msgid "Path to CA-Certificate"
 msgstr "Ścieżka do certyfikatu CA"
 
index f2ba575..87c32bf 100644 (file)
@@ -2497,6 +2497,9 @@ msgstr "Senha da Chave Privada interna"
 msgid "Password successfully changed!"
 msgstr "A senha foi alterada com sucesso!"
 
+msgid "Password2"
+msgstr ""
+
 msgid "Path to CA-Certificate"
 msgstr "Caminho para o Certificado da AC"
 
index cb90df5..bea93f5 100644 (file)
@@ -2379,6 +2379,9 @@ msgstr ""
 msgid "Password successfully changed!"
 msgstr "Password alterada com sucesso!"
 
+msgid "Password2"
+msgstr ""
+
 msgid "Path to CA-Certificate"
 msgstr "Directorio do Certificado CA"
 
index d51f4d0..2ee8537 100644 (file)
@@ -2303,6 +2303,9 @@ msgstr ""
 msgid "Password successfully changed!"
 msgstr "Parola schimbata cu succes !"
 
+msgid "Password2"
+msgstr ""
+
 msgid "Path to CA-Certificate"
 msgstr "Calea catre certificatul CA"
 
index d30c643..6515772 100644 (file)
@@ -2387,6 +2387,9 @@ msgstr ""
 msgid "Password successfully changed!"
 msgstr "Пароль успешно изменён!"
 
+msgid "Password2"
+msgstr ""
+
 msgid "Path to CA-Certificate"
 msgstr "Путь к центру сертификации"
 
index f4037ea..ab876ce 100644 (file)
@@ -2278,6 +2278,9 @@ msgstr ""
 msgid "Password successfully changed!"
 msgstr ""
 
+msgid "Password2"
+msgstr ""
+
 msgid "Path to CA-Certificate"
 msgstr ""
 
index 6af6d61..803ac28 100644 (file)
@@ -2284,6 +2284,9 @@ msgstr ""
 msgid "Password successfully changed!"
 msgstr ""
 
+msgid "Password2"
+msgstr ""
+
 msgid "Path to CA-Certificate"
 msgstr ""
 
index d3fc6a7..1aa1816 100644 (file)
@@ -2271,6 +2271,9 @@ msgstr ""
 msgid "Password successfully changed!"
 msgstr ""
 
+msgid "Password2"
+msgstr ""
+
 msgid "Path to CA-Certificate"
 msgstr ""
 
index afabfa2..3c814cd 100644 (file)
@@ -2291,6 +2291,9 @@ msgstr ""
 msgid "Password successfully changed!"
 msgstr ""
 
+msgid "Password2"
+msgstr ""
+
 msgid "Path to CA-Certificate"
 msgstr ""
 
index 7ee0688..83e5501 100644 (file)
@@ -2398,6 +2398,9 @@ msgstr ""
 msgid "Password successfully changed!"
 msgstr "Пароль успішно змінено!"
 
+msgid "Password2"
+msgstr ""
+
 msgid "Path to CA-Certificate"
 msgstr "Шлях до центру сертифікції"
 
index 239e1c2..7bd7868 100644 (file)
@@ -2314,6 +2314,9 @@ msgstr ""
 msgid "Password successfully changed!"
 msgstr ""
 
+msgid "Password2"
+msgstr ""
+
 msgid "Path to CA-Certificate"
 msgstr "Đường dẫn tới CA-Certificate"
 
index 7f19c28..7f49185 100644 (file)
@@ -1,32 +1,22 @@
 msgid ""
 msgstr ""
-"Project-Id-Version: \n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2009-12-21 23:08+0200\n"
-"PO-Revision-Date: 2017-04-09 15:04+0800\n"
-"Last-Translator: Hsing-Wang Liao <kuoruan@gmail.com>\n"
-"Language: zh_CN\n"
-"MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=1; plural=0;\n"
-"X-Generator: Poedit 2.0\n"
-"Language-Team: \n"
+"Last-Translator: Hsing-Wang Liao <kuoruan@gmail.com>\n"
 
 msgid "%s is untagged in multiple VLANs!"
-msgstr "%s 在多个 VLAN 中未标记"
+msgstr "%s 在多个 VLAN 中均未关联!"
 
 msgid "(%d minute window, %d second interval)"
-msgstr "(%d 分钟信息,%d 秒刷新)"
+msgstr "(%d 分钟信息,每 %d 秒刷新)"
 
 msgid "(%s available)"
-msgstr "(%s 可用)"
+msgstr "(%s 可用)"
 
 msgid "(empty)"
-msgstr "(空)"
+msgstr "(空)"
 
 msgid "(no interfaces attached)"
-msgstr "(未连接接口)"
+msgstr "(没有接口连接)"
 
 msgid "-- Additional Field --"
 msgstr "-- 更多选项 --"
@@ -47,10 +37,10 @@ msgid "-- match by uuid --"
 msgstr "-- 根据 UUID 匹配 --"
 
 msgid "1 Minute Load:"
-msgstr "1 分钟负载:"
+msgstr "1 分钟负载"
 
 msgid "15 Minute Load:"
-msgstr "15 分钟负载:"
+msgstr "15 分钟负载"
 
 msgid "4-character hexadecimal ID"
 msgstr "4 字符的十六进制 ID"
@@ -59,13 +49,13 @@ msgid "464XLAT (CLAT)"
 msgstr "464XLAT (CLAT)"
 
 msgid "5 Minute Load:"
-msgstr "5 分钟负载:"
+msgstr "5 分钟负载"
 
 msgid "6-octet identifier as a hex string - no colons"
-msgstr "6 个八位字节的标识符 (十六进制字符串) - 无冒号"
+msgstr "十六进制表示的 6 字节标识符,无冒号分隔"
 
 msgid "802.11r Fast Transition"
-msgstr "802.11r 快速换"
+msgstr "802.11r 快速换"
 
 msgid "802.11w Association SA Query maximum timeout"
 msgstr "802.11w 关联 SA 查询最大超时"
@@ -83,62 +73,67 @@ msgid "802.11w retry timeout"
 msgstr "802.11w 重试超时"
 
 msgid "<abbr title=\"Basic Service Set Identifier\">BSSID</abbr>"
-msgstr "<abbr title=\"基本服务集标识符\">BSSID</abbr>"
+msgstr "<abbr title=\"Basic Service Set Identifier\">BSSID</abbr>"
 
 msgid "<abbr title=\"Domain Name System\">DNS</abbr> query port"
-msgstr "<abbr title=\"域名服务系统\">DNS</abbr> 查询端口"
+msgstr "<abbr title=\"Domain Name System\">DNS</abbr> 查询端口"
 
 msgid "<abbr title=\"Domain Name System\">DNS</abbr> server port"
-msgstr "<abbr title=\"域名服务系统\">DNS</abbr> 服务器端口"
+msgstr "<abbr title=\"Domain Name System\">DNS</abbr> 服务器端口"
 
 msgid ""
 "<abbr title=\"Domain Name System\">DNS</abbr> servers will be queried in the "
 "order of the resolvfile"
-msgstr "将会按照指定的顺序查询 <abbr title=\"域名服务系统\">DNS</abbr>"
+msgstr ""
+"按照 resolvfile 里的顺序查询 <abbr title=\"Domain Name System\">DNS</abbr> 服"
+"务器"
 
 msgid "<abbr title=\"Extended Service Set Identifier\">ESSID</abbr>"
-msgstr "<abbr title=\"扩展服务集标识符\">ESSID</abbr>"
+msgstr "<abbr title=\"Extended Service Set Identifier\">ESSID</abbr>"
 
 msgid "<abbr title=\"Internet Protocol Version 4\">IPv4</abbr>-Address"
-msgstr "<abbr title=\"互联网协议第4版\">IPv4</abbr>-地址"
+msgstr "<abbr title=\"Internet Protocol Version 4\">IPv4</abbr> 地址"
 
 msgid "<abbr title=\"Internet Protocol Version 4\">IPv4</abbr>-Gateway"
-msgstr "<abbr title=\"互联网协议第4版\">IPv4</abbr>-网关"
+msgstr "<abbr title=\"Internet Protocol Version 4\">IPv4</abbr> 网关"
 
 msgid "<abbr title=\"Internet Protocol Version 4\">IPv4</abbr>-Netmask"
-msgstr "<abbr title=\"互联网协议第4版\">IPv4</abbr>-子网掩码"
+msgstr "<abbr title=\"Internet Protocol Version 4\">IPv4</abbr> 子网掩码"
 
 msgid ""
 "<abbr title=\"Internet Protocol Version 6\">IPv6</abbr>-Address or Network "
 "(CIDR)"
 msgstr ""
-"<abbr title=\"互联网协议第6版\">IPv6</abbr>-地址或超网 (<abbr title=\"无类别"
-"域间路由\">CIDR</abbr>)"
+"<abbr title=\"Internet Protocol Version 6\">IPv6</abbr> 地址或网段(CIDR)"
 
 msgid "<abbr title=\"Internet Protocol Version 6\">IPv6</abbr>-Gateway"
-msgstr "<abbr title=\"互联网协议第6版\">IPv6</abbr>-网关"
+msgstr "<abbr title=\"Internet Protocol Version 6\">IPv6</abbr> 网关"
 
 msgid "<abbr title=\"Internet Protocol Version 6\">IPv6</abbr>-Suffix (hex)"
-msgstr "<abbr title=\"互联网协议第6版\">IPv6</abbr>-后缀 (十六进制)"
+msgstr ""
+"<abbr title=\"Internet Protocol Version 6\">IPv6</abbr> 后缀(十六进制)"
 
 msgid "<abbr title=\"Light Emitting Diode\">LED</abbr> Configuration"
-msgstr "<abbr title=\"发光二极管\">LED</abbr> 配置"
+msgstr "<abbr title=\"Light Emitting Diode\">LED</abbr> 配置"
 
 msgid "<abbr title=\"Light Emitting Diode\">LED</abbr> Name"
-msgstr "<abbr title=\"发光二极管\">LED</abbr> 名称"
+msgstr "<abbr title=\"Light Emitting Diode\">LED</abbr> 名称"
 
 msgid "<abbr title=\"Media Access Control\">MAC</abbr>-Address"
-msgstr "<abbr title=\"介质访问控制\">MAC</abbr>-地址"
+msgstr "<abbr title=\"Media Access Control\">MAC</abbr> 地址"
 
 msgid ""
 "<abbr title=\"maximal\">Max.</abbr> <abbr title=\"Dynamic Host Configuration "
 "Protocol\">DHCP</abbr> leases"
-msgstr "最大 <abbr title=\"动态主机配置协议\">DHCP</abbr> 分配数量"
+msgstr ""
+"最大 <abbr title=\"Dynamic Host Configuration Protocol\">DHCP</abbr> 租约数量"
 
 msgid ""
 "<abbr title=\"maximal\">Max.</abbr> <abbr title=\"Extension Mechanisms for "
 "Domain Name System\">EDNS0</abbr> packet size"
-msgstr "最大 <abbr title=\"DNS扩展名机制\">EDNS0</abbr> 数据包大小"
+msgstr ""
+"最大 <abbr title=\"Extension Mechanisms for Domain Name System\">EDNS0</"
+"abbr> 数据包大小"
 
 msgid "<abbr title=\"maximal\">Max.</abbr> concurrent queries"
 msgstr "最大并发查询数"
@@ -150,6 +145,7 @@ msgid ""
 "<br/>Note: you need to manually restart the cron service if the crontab file "
 "was empty before editing."
 msgstr ""
+"<br/>注意:如果 crontab 文件在编辑前为空,则需要手动重新启动 cron 服务。"
 
 msgid "A43C + J43 + A43"
 msgstr "A43C + J43 + A43"
@@ -173,16 +169,16 @@ msgid "ARP retry threshold"
 msgstr "ARP 重试阈值"
 
 msgid "ATM (Asynchronous Transfer Mode)"
-msgstr "ATM (异步传输模式)"
+msgstr "ATM(异步传输模式)"
 
 msgid "ATM Bridges"
 msgstr "ATM 桥接"
 
 msgid "ATM Virtual Channel Identifier (VCI)"
-msgstr "ATM 虚拟通道标识 (VCI)"
+msgstr "ATM 虚拟通道标识(VCI)"
 
 msgid "ATM Virtual Path Identifier (VPI)"
-msgstr "ATM 虚拟路径标识 (VPI)"
+msgstr "ATM 虚拟路径标识(VPI)"
 
 msgid ""
 "ATM bridges expose encapsulated ethernet in AAL5 connections as virtual "
@@ -217,10 +213,10 @@ msgid "Activate this network"
 msgstr "激活此网络"
 
 msgid "Active <abbr title=\"Internet Protocol Version 4\">IPv4</abbr>-Routes"
-msgstr "活动的 <abbr title=\"互联网协议第4版\">IPv4</abbr>-链路"
+msgstr "活动的 <abbr title=\"Internet Protocol Version 4\">IPv4</abbr> 路由"
 
 msgid "Active <abbr title=\"Internet Protocol Version 6\">IPv6</abbr>-Routes"
-msgstr "活动的 <abbr title=\"互联网协议第6版\">IPv6</abbr>-链路"
+msgstr "活动的 <abbr title=\"Internet Protocol Version 6\">IPv6</abbr> 路由"
 
 msgid "Active Connections"
 msgstr "活动连接"
@@ -262,7 +258,7 @@ msgid "Advanced Settings"
 msgstr "高级设置"
 
 msgid "Aggregate Transmit Power(ACTATP)"
-msgstr "总发射功率 (ACTATP)"
+msgstr "总发射功率(ACTATP)"
 
 msgid "Alert"
 msgstr "警戒"
@@ -276,7 +272,7 @@ msgid "Allocate IP sequentially"
 msgstr "顺序分配 IP"
 
 msgid "Allow <abbr title=\"Secure Shell\">SSH</abbr> password authentication"
-msgstr "允许 <abbr title=\"安全外壳协议\">SSH</abbr> 密码验证"
+msgstr "允许 <abbr title=\"Secure Shell\">SSH</abbr> 密码验证"
 
 msgid "Allow all except listed"
 msgstr "仅允许列表外"
@@ -291,14 +287,14 @@ msgid "Allow remote hosts to connect to local SSH forwarded ports"
 msgstr "允许远程主机连接到本地 SSH 转发端口"
 
 msgid "Allow root logins with password"
-msgstr "允许 Root 用户凭密码登录"
+msgstr "允许 root 用户凭密码登录"
 
 msgid "Allow the <em>root</em> user to login with password"
 msgstr "允许 <em>root</em> 用户凭密码登录"
 
 msgid ""
 "Allow upstream responses in the 127.0.0.0/8 range, e.g. for RBL services"
-msgstr "允许 127.0.0.0/8 回环范围内的上行响应,例如RBL 服务"
+msgstr "允许 127.0.0.0/8 回环范围内的上行响应,例如RBL 服务"
 
 msgid "Allowed IPs"
 msgstr "允许的 IP"
@@ -311,13 +307,13 @@ msgstr ""
 "faq=comparison\">隧道对比</a>"
 
 msgid "Always announce default router"
-msgstr "总是广播默认路由"
+msgstr "总是通告默认路由"
 
 msgid "Annex"
 msgstr "Annex"
 
 msgid "Annex A + L + M (all)"
-msgstr "Annex A + L + M (全部)"
+msgstr "Annex A + L + M(全部)"
 
 msgid "Annex A G.992.1"
 msgstr "Annex A G.992.1"
@@ -332,7 +328,7 @@ msgid "Annex A G.992.5"
 msgstr "Annex A G.992.5"
 
 msgid "Annex B (all)"
-msgstr "Annex B (全部)"
+msgstr "Annex B(全部)"
 
 msgid "Annex B G.992.1"
 msgstr "Annex B G.992.1"
@@ -344,13 +340,13 @@ msgid "Annex B G.992.5"
 msgstr "Annex B G.992.5"
 
 msgid "Annex J (all)"
-msgstr "Annex J (全部)"
+msgstr "Annex J(全部)"
 
 msgid "Annex L G.992.3 POTS 1"
 msgstr "Annex L G.992.3 POTS 1"
 
 msgid "Annex M (all)"
-msgstr "Annex M (全部)"
+msgstr "Annex M(全部)"
 
 msgid "Annex M G.992.3"
 msgstr "Annex M G.992.3"
@@ -359,13 +355,13 @@ msgid "Annex M G.992.5"
 msgstr "Annex M G.992.5"
 
 msgid "Announce as default router even if no public prefix is available."
-msgstr "即使没有可用的公共前缀也广播默认路由。"
+msgstr "即使没有可用的公网前缀,也仍通告自己为默认路由。"
 
 msgid "Announced DNS domains"
-msgstr "广播的 DNS 域名"
+msgstr "通告的 DNS 域名"
 
 msgid "Announced DNS servers"
-msgstr "广播的 DNS 服务器"
+msgstr "通告的 DNS 服务器"
 
 msgid "Anonymous Identity"
 msgstr "匿名身份"
@@ -396,14 +392,14 @@ msgstr "正在应用更改"
 
 msgid ""
 "Assign a part of given length of every public IPv6-prefix to this interface"
-msgstr "给每个公共 IPv6 前缀分配指定长度的固定部分"
+msgstr "将每个公共 IPv6 前缀的给定长度部分分配给此接口"
 
 msgid "Assign interfaces..."
 msgstr "分配接口..."
 
 msgid ""
 "Assign prefix parts using this hexadecimal subprefix ID for this interface."
-msgstr "指定此接口使用的十六进制子 ID 前缀部分。"
+msgstr "将此十六进制子 ID 前缀分配给此接口"
 
 msgid "Associated Stations"
 msgstr "已连接站点"
@@ -430,16 +426,16 @@ msgid "Automatic"
 msgstr "自动"
 
 msgid "Automatic Homenet (HNCP)"
-msgstr "自动家庭网络 (HNCP)"
+msgstr "自动家庭网络(HNCP)"
 
 msgid "Automatically check filesystem for errors before mounting"
 msgstr "在挂载前自动检查文件系统错误"
 
 msgid "Automatically mount filesystems on hotplug"
-msgstr "通过 Hotplug 自动挂载磁盘"
+msgstr "通过 hotplug 自动挂载磁盘"
 
 msgid "Automatically mount swap on hotplug"
-msgstr "通过 Hotplug 自动挂载 Swap 分区"
+msgstr "通过 hotplug 自动挂载 swap 分区"
 
 msgid "Automount Filesystem"
 msgstr "自动挂载磁盘"
@@ -454,7 +450,7 @@ msgid "Available packages"
 msgstr "可用软件包"
 
 msgid "Average:"
-msgstr "平均:"
+msgstr "平均"
 
 msgid "B43 + B43C"
 msgstr "B43 + B43C"
@@ -516,7 +512,7 @@ msgid "Bind only to specific interfaces rather than wildcard address."
 msgstr "仅绑定到特定接口,而不是全部地址。"
 
 msgid "Bind the tunnel to this interface (optional)."
-msgstr "将隧道绑定到此接口 (可选)。"
+msgstr "将隧道绑定到此接口(可选)。"
 
 msgid "Bitrate"
 msgstr "传输速率"
@@ -537,10 +533,10 @@ msgid "Bring up on boot"
 msgstr "开机自动运行"
 
 msgid "Broadcom 802.11%s Wireless Controller"
-msgstr "Broadcom 802.11%s 无线网卡"
+msgstr "Broadcom 802.11%s 无线控制器"
 
 msgid "Broadcom BCM%04x 802.11 Wireless Controller"
-msgstr "Broadcom BCM%04x 802.11 无线网卡"
+msgstr "Broadcom BCM%04x 802.11 无线控制器"
 
 msgid "Buffered"
 msgstr "已缓冲"
@@ -554,10 +550,10 @@ msgid "Buttons"
 msgstr "按键"
 
 msgid "CA certificate; if empty it will be saved after the first connection."
-msgstr "CA 证书,如果留空的话证书将在第一次连接时被保存。"
+msgstr "CA 证书,如果留空,则证书将在第一次连接后被保存。"
 
 msgid "CPU usage (%)"
-msgstr "CPU 使用率 (%)"
+msgstr "CPU 使用率(%)"
 
 msgid "Cancel"
 msgstr "取消"
@@ -597,12 +593,14 @@ msgid ""
 "<em>unspecified</em> to remove the interface from the associated zone or "
 "fill out the <em>create</em> field to define a new zone and attach the "
 "interface to it."
-msgstr "此接口的防火墙区域。填写<em>创建</em>栏可新建防火墙区域。"
+msgstr ""
+"为此接口分配所属的防火墙区域,选择“不指定”可将该接口移出已关联的区域,或者填"
+"写“创建”栏来创建一个新的区域,并将当前接口与之建立关联。"
 
 msgid ""
 "Choose the network(s) you want to attach to this wireless interface or fill "
 "out the <em>create</em> field to define a new network."
-msgstr "选择指派到此无线接口的网络。填写<em>创建</em>栏可新建网络。"
+msgstr "选择指派到此无线接口的网络,或者填写“创建”栏来新建网络。"
 
 msgid "Cipher"
 msgstr "算法"
@@ -616,7 +614,7 @@ msgid ""
 "\"Perform reset\" (only possible with squashfs images)."
 msgstr ""
 "点击“生成备份”下载当前配置文件的 tar 存档。要将固件恢复到初始状态,请单击“执"
-"行重置” (仅 Squashfs 固件有效)。"
+"行重置”(仅 squashfs 格式的固件有效)。"
 
 msgid "Client"
 msgstr "客户端 Client"
@@ -627,7 +625,7 @@ msgstr "请求 DHCP 时发送的客户 ID"
 msgid ""
 "Close inactive connection after the given amount of seconds, use 0 to "
 "persist connection"
-msgstr "å®\9aæ\97¶å\85³é\97­é\9d\9eæ´»å\8a¨é\93¾æ\8e¥ (ç§\92)ï¼\8c0 ä¸ºæ\8c\81ç»­连接"
+msgstr "å\9c¨ç»\99å®\9aæ\97¶é\97´ï¼\88ç§\92ï¼\89å\90\8eå\85³é\97­é\9d\9eæ´»å\8a¨é\93¾æ\8e¥ï¼\8c0 ä¸ºä¿\9dæ\8c\81连接"
 
 msgid "Close list..."
 msgstr "关闭列表..."
@@ -636,10 +634,10 @@ msgid "Collecting data..."
 msgstr "正在收集数据..."
 
 msgid "Command"
-msgstr "进程命令"
+msgstr "命令"
 
 msgid "Common Configuration"
-msgstr "一般置"
+msgstr "一般置"
 
 msgid "Configuration"
 msgstr "配置"
@@ -666,7 +664,7 @@ msgid "Connection to server fails when TLS cannot be used"
 msgstr "当 TLS 不可用时,与服务器连接失败"
 
 msgid "Connections"
-msgstr "接"
+msgstr "接"
 
 msgid "Country"
 msgstr "国家"
@@ -704,16 +702,16 @@ msgstr "自定义分配的 IPv6 前缀"
 msgid ""
 "Custom feed definitions, e.g. private feeds. This file can be preserved in a "
 "sysupgrade."
-msgstr ""
-"自定义的软件源地址 (例如私有的软件源)。此处设定的源地址在系统升级时将被保留"
+msgstr "自定义软件源地址,例如:私有的软件源。此文件在系统升级时将被保留。"
 
 msgid "Custom feeds"
-msgstr "自定义软件源"
+msgstr "自定义软件源"
 
 msgid ""
 "Customizes the behaviour of the device <abbr title=\"Light Emitting Diode"
 "\">LED</abbr>s if possible."
-msgstr "自定义 <abbr title=\"发光二极管\">LED</abbr> 的活动状态。"
+msgstr ""
+"自定义设备 <abbr title=\"Light Emitting Diode\">LED</abbr> 行为(如果可能)。"
 
 msgid "DHCP Leases"
 msgstr "DHCP 分配"
@@ -728,7 +726,7 @@ msgid "DHCP client"
 msgstr "DHCP 客户端"
 
 msgid "DHCP-Options"
-msgstr "DHCP-选项"
+msgstr "DHCP 选项"
 
 msgid "DHCPv6 Leases"
 msgstr "DHCPv6 分配"
@@ -755,7 +753,7 @@ msgid "DNSSEC"
 msgstr "DNSSEC"
 
 msgid "DNSSEC check unsigned"
-msgstr "DNSSEC æ\9cªç­¾å\90\8dæ£\80æ\9f¥"
+msgstr "DNSSEC æ£\80æ\9f¥æ\9cªç­¾å\90\8d"
 
 msgid "DPD Idle Timeout"
 msgstr "DPD 空闲超时"
@@ -788,7 +786,7 @@ msgid "Default gateway"
 msgstr "默认网关"
 
 msgid "Default is stateless + stateful"
-msgstr "默认是无状态 + 有状态"
+msgstr "默认是无状态的 + 有状态的"
 
 msgid "Default route"
 msgstr "默认路由"
@@ -849,7 +847,9 @@ msgstr "禁用"
 msgid ""
 "Disable <abbr title=\"Dynamic Host Configuration Protocol\">DHCP</abbr> for "
 "this interface."
-msgstr "禁用本接口的 <abbr title=\"动态主机配置协议\">DHCP</abbr>。"
+msgstr ""
+"不在此接口提供 <abbr title=\"Dynamic Host Configuration Protocol\">DHCP</"
+"abbr> 服务。"
 
 msgid "Disable DNS setup"
 msgstr "停用 DNS 设定"
@@ -861,7 +861,7 @@ msgid "Disabled"
 msgstr "禁用"
 
 msgid "Disabled (default)"
-msgstr "禁用 (默认)"
+msgstr "禁用(默认)"
 
 msgid "Discard upstream RFC1918 responses"
 msgstr "丢弃 RFC1918 上行响应数据"
@@ -873,7 +873,7 @@ msgid "Distance Optimization"
 msgstr "距离优化"
 
 msgid "Distance to farthest network member in meters."
-msgstr "最远网络用户的距离 (米)。"
+msgstr "最远网络用户的距离(米)。"
 
 msgid "Distribution feeds"
 msgstr "发行版软件源"
@@ -887,18 +887,18 @@ msgid ""
 "Forwarder for <abbr title=\"Network Address Translation\">NAT</abbr> "
 "firewalls"
 msgstr ""
-"Dnsmasq 为 <abbr title=\"网络地址转换\">NAT</abbr> 防火墙提供了一个集成的 "
-"<abbr title=\"动态主机配置协议\">DHCP</abbr> 服务器和 <abbr title=\"域名系统"
-"\">DNS</abbr> 转发器"
+"Dnsmasq 为 <abbr title=\"Network Address Translation\">NAT</abbr> 防火墙提供"
+"了一个集成的 <abbr title=\"Dynamic Host Configuration Protocol\">DHCP</abbr> "
+"服务器和 <abbr title=\"Domain Name System\">DNS</abbr> 转发器"
 
 msgid "Do not cache negative replies, e.g. for not existing domains"
-msgstr "不缓存无用的回应, 比如: 不存在的域。"
+msgstr "不缓存无用的回应, 比如:不存在的域名"
 
 msgid "Do not forward requests that cannot be answered by public name servers"
 msgstr "不转发公共域名服务器无法回应的请求"
 
 msgid "Do not forward reverse lookups for local networks"
-msgstr "不转发反向查询本地网络的 Lookups 命令"
+msgstr "不转发本地网络的反向查询"
 
 msgid "Domain required"
 msgstr "忽略空域名解析"
@@ -907,12 +907,13 @@ msgid "Domain whitelist"
 msgstr "域名白名单"
 
 msgid "Don't Fragment"
-msgstr "禁止片"
+msgstr "禁止片"
 
 msgid ""
 "Don't forward <abbr title=\"Domain Name System\">DNS</abbr>-Requests without "
 "<abbr title=\"Domain Name System\">DNS</abbr>-Name"
-msgstr "不转发没有 <abbr title=\"域名系统\">DNS</abbr> 名称的解析请求"
+msgstr ""
+"不转发没有 <abbr title=\"Domain Name System\">DNS</abbr> 名称的解析请求"
 
 msgid "Download and install package"
 msgstr "下载并安装软件包"
@@ -927,14 +928,14 @@ msgid ""
 "Dropbear offers <abbr title=\"Secure Shell\">SSH</abbr> network shell access "
 "and an integrated <abbr title=\"Secure Copy\">SCP</abbr> server"
 msgstr ""
-"Dropbear 提供了集成的 <abbr title=\"安全复制\">SCP</abbr> 服务器和基于 <abbr "
-"title=\"安全外壳协议\">SSH</abbr> 的 Shell 访问"
+"Dropbear 提供 <abbr title=\"Secure Shell\">SSH</abbr> 访问和 <abbr title="
+"\"Secure Copy\">SCP</abbr> 服务"
 
 msgid "Dual-Stack Lite (RFC6333)"
 msgstr "Dual-Stack Lite (RFC6333)"
 
 msgid "Dynamic <abbr title=\"Dynamic Host Configuration Protocol\">DHCP</abbr>"
-msgstr "动态 <abbr title=\"动态主机配置协议\">DHCP</abbr>"
+msgstr "动态 <abbr title=\"Dynamic Host Configuration Protocol\">DHCP</abbr>"
 
 msgid "Dynamic tunnel"
 msgstr "动态隧道"
@@ -943,7 +944,7 @@ msgid ""
 "Dynamically allocate DHCP addresses for clients. If disabled, only clients "
 "having static leases will be served."
 msgstr ""
-"动态分配 DHCP 地址。如果禁用,则只能为静态租用表中的客户端提供网络服务。"
+"为所有客户端提供 DHCP 服务。如果禁用,将只对具有静态租约的客户提供服务。"
 
 msgid "EA-bits length"
 msgstr "EA-bits 长度"
@@ -957,7 +958,7 @@ msgstr "修改"
 msgid ""
 "Edit the raw configuration data above to fix any error and hit \"Save\" to "
 "reload the page."
-msgstr "编辑上方的原始配置以修复错误并按下“保存”按钮以重新载入此页面。"
+msgstr "编辑上方的原始配置数据来修复错误,点击“保存”按钮以重新载入此页面。"
 
 msgid "Edit this interface"
 msgstr "修改此接口"
@@ -972,7 +973,7 @@ msgid "Enable"
 msgstr "启用"
 
 msgid "Enable <abbr title=\"Spanning Tree Protocol\">STP</abbr>"
-msgstr "开启 <abbr title=\"生成树协议\">STP</abbr>"
+msgstr "开启 <abbr title=\"Spanning Tree Protocol\">STP</abbr>"
 
 msgid "Enable HE.net dynamic endpoint update"
 msgstr "启用 HE.net 动态终端更新"
@@ -999,7 +1000,7 @@ msgid "Enable VLAN functionality"
 msgstr "启用 VLAN"
 
 msgid "Enable WPS pushbutton, requires WPA(2)-PSK"
-msgstr "启用 WPS 按键配置,要求使用 WPA(2)-PSK"
+msgstr "启用 WPS 一键加密按钮,需要 WPA(2)-PSK"
 
 msgid "Enable learning and aging"
 msgstr "启用智能交换学习"
@@ -1011,7 +1012,7 @@ msgid "Enable mirroring of outgoing packets"
 msgstr "启用流出数据包镜像"
 
 msgid "Enable the DF (Don't Fragment) flag of the encapsulating packets."
-msgstr "å\90¯ç\94¨å°\81è£\85æ\95°æ\8d®å\8c\85ç\9a\84 DF (ç¦\81æ­¢ç¢\8eç\89\87标志。"
+msgstr "å\90¯ç\94¨å\90\8eæ\8a¥æ\96\87ç\9a\84 DFï¼\88ç¦\81æ­¢å\88\86ç\89\87ï¼\89标志。"
 
 msgid "Enable this mount"
 msgstr "启用挂载点"
@@ -1031,7 +1032,7 @@ msgid ""
 msgstr "启用属于同一移动域的接入点之间的快速漫游"
 
 msgid "Enables the Spanning Tree Protocol on this bridge"
-msgstr "在此桥接上启用生成协议树"
+msgstr "在此桥接上启用生成树协议"
 
 msgid "Encapsulation mode"
 msgstr "封装模式"
@@ -1052,7 +1053,7 @@ msgid "Error"
 msgstr "错误"
 
 msgid "Errored seconds (ES)"
-msgstr "错误秒数 (ES)"
+msgstr "错误秒数(ES)"
 
 msgid "Ethernet Adapter"
 msgstr "以太网适配器"
@@ -1071,25 +1072,25 @@ msgstr "到期时间"
 
 msgid ""
 "Expiry time of leased addresses, minimum is 2 minutes (<code>2m</code>)."
-msgstr "租用地址的到期时间,最短 2 分钟 (<code>2m</code>)。"
+msgstr "租用地址的到期时间,最短 2 分钟(<code>2m</code>)。"
 
 msgid "External"
 msgstr "外部"
 
 msgid "External R0 Key Holder List"
-msgstr "外部 R0KH (R0 Key Holder) 列表"
+msgstr "外部 <abbr title=\"R0 Key Holder\">R0KH</abbr> 列表"
 
 msgid "External R1 Key Holder List"
-msgstr "外部 R1KH (R1 Key Holder) 列表"
+msgstr "外部 <abbr title=\"R1 Key Holder\">R1KH</abbr> 列表"
 
 msgid "External system log server"
-msgstr "外部日志服务器"
+msgstr "外部系统日志服务器地址"
 
 msgid "External system log server port"
-msgstr "外部日志服务器端口"
+msgstr "外部系统日志服务器端口"
 
 msgid "External system log server protocol"
-msgstr "外部日志服务器协议"
+msgstr "外部系统日志服务器协议"
 
 msgid "Extra SSH command options"
 msgstr "额外的 SSH 命令选项"
@@ -1115,9 +1116,7 @@ msgstr "过滤无用包"
 msgid ""
 "Find all currently attached filesystems and swap and replace configuration "
 "with defaults based on what was detected"
-msgstr ""
-"查找所有当前系统上的分区和 Swap 并使用基于所找到的分区生成的配置文件替换默认"
-"配置"
+msgstr "查找当前系统上的所有分区和 swap 设备,并根据查找结果生成并替换现有配置"
 
 msgid "Find and join network"
 msgstr "搜索并加入网络"
@@ -1168,7 +1167,7 @@ msgid "Force"
 msgstr "强制"
 
 msgid "Force CCMP (AES)"
-msgstr "强制 CCMP (AES)"
+msgstr "强制 CCMP(AES)"
 
 msgid "Force DHCP on this network even if another server is detected."
 msgstr "即使检测到另一台服务器,也要强制使用此网络上的 DHCP。"
@@ -1177,7 +1176,7 @@ msgid "Force TKIP"
 msgstr "强制 TKIP"
 
 msgid "Force TKIP and CCMP (AES)"
-msgstr "强制 TKIP 和 CCMP (AES)"
+msgstr "强制 TKIP 和 CCMP(AES)"
 
 msgid "Force link"
 msgstr "强制链路"
@@ -1192,7 +1191,7 @@ msgid "Forward DHCP traffic"
 msgstr "转发 DHCP 数据包"
 
 msgid "Forward Error Correction Seconds (FECS)"
-msgstr "前向纠错秒数 (FECS)"
+msgstr "前向纠错秒数(FECS)"
 
 msgid "Forward broadcast traffic"
 msgstr "转发广播数据包"
@@ -1216,7 +1215,7 @@ msgid ""
 "Further information about WireGuard interfaces and peers at <a href=\"http://"
 "wireguard.io\">wireguard.io</a>."
 msgstr ""
-"有关 WireGuard 接口和 Peer 的更多信息<a href=\"http://wireguard.io"
+"有关 WireGuard 接口和 Peer 的更多信息<a href=\"http://wireguard.io"
 "\">wireguard.io</a>。"
 
 msgid "GHz"
@@ -1247,7 +1246,7 @@ msgid "Generate archive"
 msgstr "生成备份"
 
 msgid "Generic 802.11%s Wireless Controller"
-msgstr "通用 802.11%s 无线网卡"
+msgstr "通用 802.11%s 无线控制器"
 
 msgid "Given password confirmation did not match, password not changed!"
 msgstr "由于密码验证不匹配,密码没有更改!"
@@ -1277,7 +1276,7 @@ msgid "HE.net username"
 msgstr "HE.net 用户名"
 
 msgid "HT mode (802.11n)"
-msgstr "HT 模式 (802.11n)"
+msgstr "HT 模式(802.11n)"
 
 msgid "Handler"
 msgstr "处理程序"
@@ -1286,7 +1285,7 @@ msgid "Hang Up"
 msgstr "挂起"
 
 msgid "Header Error Code Errors (HEC)"
-msgstr "请求头的错误代码错误 (HEC)"
+msgstr "请求头错误代码错误(HEC)"
 
 msgid "Heartbeat"
 msgstr "心跳"
@@ -1299,13 +1298,13 @@ msgstr "配置路由器的部分基础信息。"
 msgid ""
 "Here you can paste public SSH-Keys (one per line) for SSH public-key "
 "authentication."
-msgstr "请在这里粘贴公共 SSH 密钥用于 SSH 公钥认证 (每行一个)。"
+msgstr "请在此处粘贴 SSH 公钥,每行一个,用于 SSH 公钥认证。"
 
 msgid "Hermes 802.11b Wireless Controller"
-msgstr "Hermes 802.11b 无线网卡"
+msgstr "Hermes 802.11b 无线控制器"
 
 msgid "Hide <abbr title=\"Extended Service Set Identifier\">ESSID</abbr>"
-msgstr "隐藏 <abbr title=\"扩展服务集标识符\">ESSID</abbr>"
+msgstr "隐藏 <abbr title=\"Extended Service Set Identifier\">ESSID</abbr>"
 
 msgid "Host"
 msgstr "主机"
@@ -1317,7 +1316,7 @@ msgid "Host expiry timeout"
 msgstr "主机到期超时"
 
 msgid "Host-<abbr title=\"Internet Protocol Address\">IP</abbr> or Network"
-msgstr "主机 IP 或网络"
+msgstr "主机 <abbr title=\"Internet Protocol Address\">IP</abbr> 或网络"
 
 msgid "Hostname"
 msgstr "主机名"
@@ -1377,7 +1376,7 @@ msgid "IPv4 prefix length"
 msgstr "IPv4 地址前缀长度"
 
 msgid "IPv4-Address"
-msgstr "IPv4-地址"
+msgstr "IPv4 地址"
 
 msgid "IPv4-in-IPv4 (RFC2003)"
 msgstr "IPv4-in-IPv4 (RFC2003)"
@@ -1404,7 +1403,7 @@ msgid "IPv6 address"
 msgstr "IPv6 地址"
 
 msgid "IPv6 address delegated to the local tunnel endpoint (optional)"
-msgstr "绑定到本地隧道终点的 IPv6 地址 (可选)"
+msgstr "绑定到隧道本端的 IPv6 地址(可选)"
 
 msgid "IPv6 assignment hint"
 msgstr "IPv6 分配提示"
@@ -1428,10 +1427,10 @@ msgid "IPv6 routed prefix"
 msgstr "IPv6 路由前缀"
 
 msgid "IPv6 suffix"
-msgstr ""
+msgstr "IPv6 后缀"
 
 msgid "IPv6-Address"
-msgstr "IPv6-地址"
+msgstr "IPv6 地址"
 
 msgid "IPv6-PD"
 msgstr "IPv6-PD"
@@ -1456,12 +1455,12 @@ msgstr "选中以禁用加密"
 
 msgid ""
 "If specified, mount the device by its UUID instead of a fixed device node"
-msgstr "用 UUID 来挂载设备"
+msgstr "如果指定,则通过 UUID 而不是固定的设备文件来挂载设备"
 
 msgid ""
 "If specified, mount the device by the partition label instead of a fixed "
 "device node"
-msgstr "用卷标来挂载设备"
+msgstr "如果指定,则通过分区卷标而不是固定的设备文件来挂载设备"
 
 msgid "If unchecked, no default route is configured"
 msgstr "留空则不配置默认路由"
@@ -1475,7 +1474,11 @@ msgid ""
 "\"Random Access Memory\">RAM</abbr>. Be aware that swapping data is a very "
 "slow process as the swap-device cannot be accessed with the high datarates "
 "of the <abbr title=\"Random Access Memory\">RAM</abbr>."
-msgstr "如果物理内存不足,闲置数据可自动移到交换区暂存,以提高可用内存。"
+msgstr ""
+"如果物理内存不足,闲置数据可自动移到 swap 区暂存,以增加可用的 <abbr title="
+"\"Random Access Memory\">RAM</abbr>。请注意:swap 区的数据处理会非常慢,因为 "
+"swap设备无法像 <abbr title=\"Random Access Memory\">RAM</abbr> 这样的高速率访"
+"问。"
 
 msgid "Ignore <code>/etc/hosts</code>"
 msgstr "忽略 <code>/etc/hosts</code>"
@@ -1496,14 +1499,14 @@ msgid ""
 "In order to prevent unauthorized access to the system, your request has been "
 "blocked. Click \"Continue »\" below to return to the previous page."
 msgstr ""
-"为了防止对系统的未授权访问,您的请求已被阻止。点击下面的 “继续 »” 来返回上一"
+"为了防止未经授权访问系统,您的请求已被阻止。点击下面的 “继续 »” 来返回上一"
 "页。"
 
 msgid "Inactivity timeout"
 msgstr "活动超时"
 
 msgid "Inbound:"
-msgstr "入站:"
+msgstr "入站"
 
 msgid "Info"
 msgstr "信息"
@@ -1533,7 +1536,7 @@ msgid "Interface"
 msgstr "接口"
 
 msgid "Interface %q device auto-migrated from %q to %q."
-msgstr ""
+msgstr "接口设备 %q 从 %q 自动迁移到了 %q。"
 
 msgid "Interface Configuration"
 msgstr "接口配置"
@@ -1575,13 +1578,13 @@ msgid "Invalid VLAN ID given! Only IDs between %d and %d are allowed."
 msgstr "无效的 VLAN ID!只有 %d 和 %d 之间的 ID 有效。"
 
 msgid "Invalid VLAN ID given! Only unique IDs are allowed"
-msgstr "无效的 VLAN ID!只允许唯一的 ID"
+msgstr "无效的 VLAN ID!只允许唯一的 ID"
 
 msgid "Invalid username and/or password! Please try again."
 msgstr "无效的用户名和/或密码!请重试。"
 
 msgid "Isolate Clients"
-msgstr ""
+msgstr "隔离客户端"
 
 msgid ""
 "It appears that you are trying to flash an image that does not fit into the "
@@ -1595,10 +1598,10 @@ msgid "Join Network"
 msgstr "加入网络"
 
 msgid "Join Network: Wireless Scan"
-msgstr "加入网络搜索无线"
+msgstr "加入网络搜索无线"
 
 msgid "Joining Network: %q"
-msgstr "加入网络%q"
+msgstr "加入网络%q"
 
 msgid "Keep settings"
 msgstr "保留配置"
@@ -1667,19 +1670,19 @@ msgid "Leave empty to use the current WAN address"
 msgstr "留空则使用当前 WAN 地址"
 
 msgid "Legend:"
-msgstr "图例:"
+msgstr "图例"
 
 msgid "Limit"
 msgstr "客户数"
 
 msgid "Limit DNS service to subnets interfaces on which we are serving DNS."
-msgstr "将DNS服务限制到我们提供DNS的子网接口。"
+msgstr "仅在网卡所属的子网中提供 DNS 服务。"
 
 msgid "Limit listening to these interfaces, and loopback."
 msgstr "仅监听这些接口和环回接口。"
 
 msgid "Line Attenuation (LATN)"
-msgstr "线路衰减 (LATN)"
+msgstr "线路衰减(LATN)"
 
 msgid "Line Mode"
 msgstr "线路模式"
@@ -1697,8 +1700,7 @@ msgid ""
 "List of <abbr title=\"Domain Name System\">DNS</abbr> servers to forward "
 "requests to"
 msgstr ""
-"将指定域名的解析请求转发到指定的 <abbr title=\"域名系统\">DNS</abbr> 服务器 "
-"(按照示例填写)"
+"将请求转发到的 <abbr title=\"Domain Name System\">DNS</abbr> 服务器列表"
 
 msgid ""
 "List of R0KHs in the same Mobility Domain. <br />Format: MAC-address,NAS-"
@@ -1707,9 +1709,9 @@ msgid ""
 "from the R0KH that the STA used during the Initial Mobility Domain "
 "Association."
 msgstr ""
-"同一移动域中的 R0KH 列表。<br />格式: MAC 地址,NAS标识符,128位密钥 (十六进制"
-"字符串)。<br />在从初始移动域关联期间使用的 R0KH 中请求 PMK-R1 密钥时,该列表"
-"用于将 R0KH-ID (NAS标识符)映射到目标 MAC 地址。"
+"同一移动域中的 R0KH 列表。<br />格式:MAC 地址,NAS 标识符,128 位密钥(十六"
+"进制字符串)。<br />在从初始移动域关联期间使用的 R0KH 中请求 PMK-R1 密钥时,"
+"该列表用于将 R0KH-ID(NAS 标识符)映射到目标 MAC 地址。"
 
 msgid ""
 "List of R1KHs in the same Mobility Domain. <br />Format: MAC-address,R1KH-ID "
@@ -1718,10 +1720,10 @@ msgid ""
 "R0KH. This is also the list of authorized R1KHs in the MD that can request "
 "PMK-R1 keys."
 msgstr ""
-"同一移动域中的 R1KH 列表。<br />格式: MAC地址,R1KH-ID (包含冒号的6个八位字"
-"节),128位密钥 (十六进制字符串)。<br />当从 R0KH 发送 PMK-R1 键时,此列表用于"
-"将 R1KH-ID 映射到目标 MAC 地址。这也是可以请求 PMK-R1 键的 MD 中授权的 R1KH "
-"的列表。"
+"同一移动域中的 R1KH 列表。<br />格式:MAC 地址,R1KH-ID(包含冒号的 6 个八位"
+"字节),128 位密钥(十六进制字符串)。<br />当从 R0KH 发送 PMK-R1 键时,此列"
+"表用于将 R1KH-ID 映射到目标 MAC 地址。这也是可以请求 PMK-R1 键的 MD 中授权的 "
+"R1KH 的列表。"
 
 msgid "List of SSH key files for auth"
 msgstr "用于认证的 SSH 密钥文件列表"
@@ -1812,22 +1814,22 @@ msgid "Logout"
 msgstr "退出"
 
 msgid "Loss of Signal Seconds (LOSS)"
-msgstr "信号丢失秒数 (LOSS)"
+msgstr "信号丢失秒数(LOSS)"
 
 msgid "Lowest leased address as offset from the network address."
 msgstr "网络地址的起始分配基址。"
 
 msgid "MAC-Address"
-msgstr "MAC-地址"
+msgstr "MAC 地址"
 
 msgid "MAC-Address Filter"
-msgstr "MAC-地址过滤"
+msgstr "MAC 地址过滤"
 
 msgid "MAC-Filter"
-msgstr "MAC-过滤"
+msgstr "MAC 过滤"
 
 msgid "MAC-List"
-msgstr "MAC-列表"
+msgstr "MAC 列表"
 
 msgid "MAP / LW4over6"
 msgstr "MAP / LW4over6"
@@ -1847,13 +1849,13 @@ msgstr "MTU"
 msgid ""
 "Make sure to clone the root filesystem using something like the commands "
 "below:"
-msgstr "请确认你已经复制过整个根文件系统,例如使用以下命令:"
+msgstr "确保使用以下命令来复制根文件系统:"
 
 msgid "Manual"
 msgstr "手动"
 
 msgid "Max. Attainable Data Rate (ATTNDR)"
-msgstr "最大可达数据速率 (ATTNDR)"
+msgstr "最大可达数据速率(ATTNDR)"
 
 msgid "Maximum allowed number of active DHCP leases"
 msgstr "允许的最大 DHCP 租用数"
@@ -1865,7 +1867,7 @@ msgid "Maximum allowed size of EDNS.0 UDP packets"
 msgstr "允许的最大 EDNS.0 UDP 数据包大小"
 
 msgid "Maximum amount of seconds to wait for the modem to become ready"
-msgstr "调制解调器就绪的最大等待时间 (秒)"
+msgstr "调制解调器就绪的最大等待时间(秒)"
 
 msgid "Maximum hold time"
 msgstr "最大持续时间"
@@ -1874,7 +1876,8 @@ msgid ""
 "Maximum length of the name is 15 characters including the automatic protocol/"
 "bridge prefix (br-, 6in4-, pppoe- etc.)"
 msgstr ""
-"名称的最大长度为 15 个字符,包括自动协议/网桥前缀 (br-, 6in4-, pppoe- 等等)"
+"名称的最大长度为 15 个字符,包含根据协议类型,网桥自动添加上的名字前缀(br-、"
+"6in4-、pppoe- 等)"
 
 msgid "Maximum number of leased addresses."
 msgstr "最大地址分配数量。"
@@ -1886,7 +1889,7 @@ msgid "Memory"
 msgstr "内存"
 
 msgid "Memory usage (%)"
-msgstr "内存使用率 (%)"
+msgstr "内存使用率(%)"
 
 msgid "Metric"
 msgstr "跃点数"
@@ -1951,7 +1954,7 @@ msgid "Mount point"
 msgstr "挂载点"
 
 msgid "Mount swap not specifically configured"
-msgstr "自动挂载未专门配置的 Swap 分区"
+msgstr "自动挂载未专门配置的 swap 分区"
 
 msgid "Mounted file systems"
 msgstr "已挂载的文件系统"
@@ -1975,10 +1978,10 @@ msgid "NAT64 Prefix"
 msgstr "NAT64 前缀"
 
 msgid "NCM"
-msgstr ""
+msgstr "NCM"
 
 msgid "NDP-Proxy"
-msgstr "NDP-代理"
+msgstr "NDP 代理"
 
 msgid "NT Domain"
 msgstr "NT 域"
@@ -2059,13 +2062,13 @@ msgid "Noise"
 msgstr "噪声"
 
 msgid "Noise Margin (SNR)"
-msgstr "噪声容限 (SNR)"
+msgstr "噪声容限(SNR)"
 
 msgid "Noise:"
-msgstr "噪声:"
+msgstr "噪声"
 
 msgid "Non Pre-emtive CRC errors (CRC_P)"
-msgstr "非抢占 CRC 错误 (CRC_P)"
+msgstr "非抢占 CRC 错误(CRC_P)"
 
 msgid "Non-wildcard"
 msgstr "非全部地址"
@@ -2086,10 +2089,10 @@ msgid "Not connected"
 msgstr "未连接"
 
 msgid "Note: Configuration files will be erased."
-msgstr "注意配置文件将被删除。"
+msgstr "注意配置文件将被删除。"
 
 msgid "Note: interface name length"
-msgstr "注意接口名称长度"
+msgstr "注意接口名称长度"
 
 msgid "Notice"
 msgstr "注意"
@@ -2101,7 +2104,7 @@ msgid "OK"
 msgstr "确认"
 
 msgid "OPKG-Configuration"
-msgstr "OPKG-配置"
+msgstr "OPKG 配置"
 
 msgid "Obfuscated Group Password"
 msgstr "混淆组密码"
@@ -2121,8 +2124,9 @@ msgid ""
 "<samp>eth0.1</samp>)."
 msgstr ""
 "在此页面,你可以配置网络接口。你可以勾选“桥接接口”,并输入由空格分隔的多个网"
-"络接口的名称来桥接多个接口。还可以使用 <abbr title=\"虚拟局域网\">VLAN</"
-"abbr> 符号 <samp>INTERFACE.VLANNR</samp> (例如: <samp>eth0.1</samp>)。"
+"络接口的名称来桥接多个接口。接口名称中可以使用 <abbr title=\"Virtual Local "
+"Area Network\">VLAN</abbr> 记号 <samp>INTERFACE.VLANNR</samp>(例如:"
+"<samp>eth0.1</samp>)。"
 
 msgid "On-State Delay"
 msgstr "通电时间"
@@ -2143,7 +2147,7 @@ msgid "Open list..."
 msgstr "打开列表..."
 
 msgid "OpenConnect (CISCO AnyConnect)"
-msgstr "开放连接 (CISCO AnyConnect)"
+msgstr "OpenConnect (CISCO AnyConnect)"
 
 msgid "Operating frequency"
 msgstr "工作频率"
@@ -2158,10 +2162,10 @@ msgid "Optional"
 msgstr "可选"
 
 msgid "Optional, specify to override default server (tic.sixxs.net)"
-msgstr "可选,设置这个选项会覆盖默认设定的服务器 (tic.sixxs.net)"
+msgstr "可选,设置这个选项会覆盖默认服务器(tic.sixxs.net)"
 
 msgid "Optional, use when the SIXXS account has more than one tunnel"
-msgstr "可选,如果你的 SIXXS 账号拥有一个以上的隧道请设置此项."
+msgstr "可选,如果你的 SIXXS 账号拥有一个以上的隧道请设置此项"
 
 msgid ""
 "Optional. 32-bit mark for outgoing encrypted packets. Enter value in hex, "
@@ -2175,6 +2179,9 @@ msgid ""
 "server, use the suffix (like '::1') to form the IPv6 address ('a:b:c:d::1') "
 "for the interface."
 msgstr ""
+"可选,允许的值:'eui64'、'random' 和其他固定值(例如:'::1' 或 '::1:2')。当"
+"从授权服务器获取到 IPv6 前缀(如 'a:b:c:d::'),使用后缀(如 '::1')合成 "
+"IPv6 地址('a:b:c:d::1')分配给此接口。"
 
 msgid ""
 "Optional. Base64-encoded preshared key. Adds in an additional layer of "
@@ -2193,14 +2200,14 @@ msgid "Optional. Maximum Transmission Unit of tunnel interface."
 msgstr "可选,隧道接口的最大传输单元。"
 
 msgid "Optional. Port of peer."
-msgstr "可选,Peer的端口。"
+msgstr "可选,Peer 的端口。"
 
 msgid ""
 "Optional. Seconds between keep alive messages. Default is 0 (disabled). "
 "Recommended value if this device is behind a NAT is 25."
 msgstr ""
-"可选,Keep-Alive 消息之间的秒数,默认为 0 (禁用)。如果此设备位于 NAT 之后,建"
-"议使用的值为 25。"
+"可选,Keep-Alive 消息之间的秒数,默认为 0(禁用)。如果此设备位于 NAT 之后,"
+"议使用的值为 25。"
 
 msgid "Optional. UDP port used for outgoing and incoming packets."
 msgstr "可选,用于传出和传入数据包的 UDP 端口。"
@@ -2209,13 +2216,13 @@ msgid "Options"
 msgstr "选项"
 
 msgid "Other:"
-msgstr "其余:"
+msgstr "其余"
 
 msgid "Out"
 msgstr "出口"
 
 msgid "Outbound:"
-msgstr "出站:"
+msgstr "出站"
 
 msgid "Output Interface"
 msgstr "网络出口"
@@ -2292,7 +2299,7 @@ msgid "PSID-bits length"
 msgstr "PSID-bits 长度"
 
 msgid "PTM/EFM (Packet Transfer Mode)"
-msgstr "PTM/EFM (分组传输模式)"
+msgstr "PTM/EFM(分组传输模式)"
 
 msgid "Package libiwinfo required!"
 msgstr "需要 libiwinfo 软件包!"
@@ -2324,6 +2331,9 @@ msgstr "内部私钥的密码"
 msgid "Password successfully changed!"
 msgstr "密码修改成功!"
 
+msgid "Password2"
+msgstr "密码 2"
+
 msgid "Path to CA-Certificate"
 msgstr "CA 证书路径"
 
@@ -2337,7 +2347,7 @@ msgid "Path to executable which handles the button event"
 msgstr "处理按键动作的可执行文件路径"
 
 msgid "Path to inner CA-Certificate"
-msgstr "内部CA证书的路径"
+msgstr "内部 CA 证书的路径"
 
 msgid "Path to inner Client-Certificate"
 msgstr "内部客户端证书的路径"
@@ -2346,7 +2356,7 @@ msgid "Path to inner Private Key"
 msgstr "内部私钥的路径"
 
 msgid "Peak:"
-msgstr "峰值:"
+msgstr "峰值"
 
 msgid "Peer IP address to assign"
 msgstr "要分配的 Peer IP 地址"
@@ -2367,7 +2377,7 @@ msgid "Persistent Keep Alive"
 msgstr "持续 Keep-Alive"
 
 msgid "Phy Rate:"
-msgstr "物理速率:"
+msgstr "物理速率"
 
 msgid "Physical Settings"
 msgstr "物理设置"
@@ -2388,13 +2398,13 @@ msgid "Port"
 msgstr "端口"
 
 msgid "Port status:"
-msgstr "端口状态:"
+msgstr "端口状态"
 
 msgid "Power Management Mode"
 msgstr "电源管理模式"
 
 msgid "Pre-emtive CRC errors (CRCP_P)"
-msgstr "抢占式 CRC 错误 (CRCP_P)"
+msgstr "抢占式 CRC 错误(CRCP_P)"
 
 msgid "Prefer LTE"
 msgstr "首选 LTE"
@@ -2420,7 +2430,7 @@ msgid "Prevents client-to-client communication"
 msgstr "禁止客户端间通信"
 
 msgid "Prism2/2.5/3 802.11b Wireless Controller"
-msgstr "Prism2/2.5/3 802.11b 无线网卡"
+msgstr "Prism2/2.5/3 802.11b 无线控制器"
 
 msgid "Private Key"
 msgstr "私钥"
@@ -2450,13 +2460,13 @@ msgid "Protocol support is not installed"
 msgstr "未安装协议支持"
 
 msgid "Provide NTP server"
-msgstr "NTP服务器"
+msgstr "作为 NTP 服务器提供服务"
 
 msgid "Provide new network"
 msgstr "添加新网络"
 
 msgid "Pseudo Ad-Hoc (ahdemo)"
-msgstr "伪装 Ad-Hoc (ahdemo)"
+msgstr "伪装 Ad-Hoc(ahdemo)"
 
 msgid "Public Key"
 msgstr "公钥"
@@ -2489,7 +2499,7 @@ msgid "RX Rate"
 msgstr "接收速率"
 
 msgid "RaLink 802.11%s Wireless Controller"
-msgstr "RaLink 802.11%s 无线网卡"
+msgstr "RaLink 802.11%s 无线控制器"
 
 msgid "Radius-Accounting-Port"
 msgstr "Radius 计费端口"
@@ -2513,8 +2523,8 @@ msgid ""
 "Read <code>/etc/ethers</code> to configure the <abbr title=\"Dynamic Host "
 "Configuration Protocol\">DHCP</abbr>-Server"
 msgstr ""
-"根据 <code>/etc/ethers</code> 来配置 <abbr title=\"动态主机配置协议\">DHCP</"
-"abbr>-服务器"
+"根据 <code>/etc/ethers</code> 来配置 <abbr title=\"Dynamic Host "
+"Configuration Protocol\">DHCP</abbr> 服务器"
 
 msgid ""
 "Really delete this interface? The deletion cannot be undone!\\nYou might "
@@ -2640,7 +2650,7 @@ msgid "Required"
 msgstr "必须"
 
 msgid "Required for certain ISPs, e.g. Charter with DOCSIS 3"
-msgstr "某些 ISP 需要,例如同轴线网络 DOCSIS 3"
+msgstr "某些 ISP 需要,例如同轴线网络 DOCSIS 3"
 
 msgid "Required. Base64-encoded private key for this interface."
 msgstr "必须,此接口的 Base64 编码私钥。"
@@ -2660,13 +2670,13 @@ msgid ""
 "Requires the 'full' version of wpad/hostapd and support from the wifi driver "
 "<br />(as of Feb 2017: ath9k and ath10k, in LEDE also mwlwifi and mt76)"
 msgstr ""
-"需要 wpad/hostapd 的完整版本和 WiFi 驱动程序的支持<br />(截至 2017 年 2 月: "
-"ath9k 和 ath10k,或者 LEDE 的 mwlwifi 和 mt76)"
+"需要完整版本的 wpad/hostapd,并且 WiFi 驱动支持<br />(截止 2017.02,已知支持"
+"此特性的驱动有 ath9k、ath10k,以及 LEDE 中的 mwlwifi 和 mt76)"
 
 msgid ""
 "Requires upstream supports DNSSEC; verify unsigned domain responses really "
 "come from unsigned domains"
-msgstr "é\9c\80è¦\81ä¸\8a级æ\94¯æ\8c\81 DNSSECï¼\8céª\8cè¯\81æ\9cªç­¾å\90\8dç\9a\84å\9f\9få\93\8dåº\94ç¡®å®\9eæ\98¯æ\9d¥è\87ªæ\9cªç­¾å\90\8dç\9a\84å\9f\9fã\80\82"
+msgstr "é\9c\80è¦\81ä¸\8a级æ\94¯æ\8c\81 DNSSECï¼\8céª\8cè¯\81æ\9cªç­¾å\90\8dç\9a\84å\93\8dåº\94ç¡®å®\9eæ\98¯æ\9d¥è\87ªæ\9cªç­¾å\90\8dç\9a\84å\9f\9få\90\8d"
 
 msgid "Reset"
 msgstr "复位"
@@ -2717,7 +2727,7 @@ msgid "Routed IPv6 prefix for downstream interfaces"
 msgstr "下行接口的路由 IPv6 前缀"
 
 msgid "Router Advertisement-Service"
-msgstr "路由器广告服务"
+msgstr "路由告服务"
 
 msgid "Router Password"
 msgstr "主机密码"
@@ -2742,7 +2752,7 @@ msgstr "SHA256"
 msgid ""
 "SIXXS supports TIC only, for static tunnels using IP protocol 41 (RFC4213) "
 "use 6in4 instead"
-msgstr "SIXXS 仅支持 TIC,对于使用 IP 协议 41 (RFC4213) 的静态隧道,使用 6in4"
+msgstr "SIXXS 仅支持 TIC,对于使用 IP 协议 41(RFC4213)的静态隧道,使用 6in4"
 
 msgid "SIXXS-handle[/Tunnel-ID]"
 msgstr "SIXXS-handle[/Tunnel-ID]"
@@ -2763,7 +2773,7 @@ msgid "SSH username"
 msgstr "SSH 用户名"
 
 msgid "SSH-Keys"
-msgstr "SSH-密钥"
+msgstr "SSH 密钥"
 
 msgid "SSID"
 msgstr "SSID"
@@ -2778,7 +2788,7 @@ msgid "Save &#38; Apply"
 msgstr "保存&#38;应用"
 
 msgid "Scan"
-msgstr "æ\90\9cç´¢"
+msgstr "æ\89«æ\8f\8f"
 
 msgid "Scheduled Tasks"
 msgstr "计划任务"
@@ -2795,7 +2805,7 @@ msgstr "详参 \"mount\" 联机帮助"
 msgid ""
 "Send LCP echo requests at the given interval in seconds, only effective in "
 "conjunction with failure threshold"
-msgstr "定时发送 LCP 响应 (秒),仅在结合了故障阈值时有效"
+msgstr "定时发送 LCP 响应(秒),仅在结合了故障阈值时有效"
 
 msgid "Separate Clients"
 msgstr "隔离客户端"
@@ -2809,7 +2819,7 @@ msgstr "服务器密码"
 msgid ""
 "Server password, enter the specific password of the tunnel when the username "
 "contains the tunnel ID"
-msgstr "服务器密码,如果用户名包含隧道 ID 则在此填写独立的密码"
+msgstr "服务器密码,如果用户名包含隧道 ID 则在此填写隧道自己的密码"
 
 msgid "Server username"
 msgstr "服务器用户名"
@@ -2827,8 +2837,8 @@ msgid ""
 "Set interface properties regardless of the link carrier (If set, carrier "
 "sense events do not invoke hotplug handlers)."
 msgstr ""
-"无论链路载荷如何都设置接口属性 (如果设置,载荷侦听事件不调用 Hotplug 处理程"
-"序)。"
+"不管接口的链路状态如何,总是用应用设置(如果勾选,链路状态变更将不再触发 "
+"hotplug 事件处理)。"
 
 msgid "Set up Time Synchronization"
 msgstr "设置时间同步"
@@ -2837,13 +2847,13 @@ msgid "Setup DHCP Server"
 msgstr "配置 DHCP 服务器"
 
 msgid "Severely Errored Seconds (SES)"
-msgstr "严重误码秒 (SES)"
+msgstr "严重误码秒(SES)"
 
 msgid "Short GI"
 msgstr "Short GI"
 
 msgid "Show current backup file list"
-msgstr "显示当前文件备份列表"
+msgstr "显示当前备份文件列表"
 
 msgid "Shutdown this interface"
 msgstr "关闭此接口"
@@ -2855,16 +2865,16 @@ msgid "Signal"
 msgstr "信号"
 
 msgid "Signal Attenuation (SATN)"
-msgstr "信号衰减 (SATN)"
+msgstr "信号衰减(SATN)"
 
 msgid "Signal:"
-msgstr "信号:"
+msgstr "信号"
 
 msgid "Size"
 msgstr "大小"
 
 msgid "Size (.ipk)"
-msgstr "大小 (.ipk)"
+msgstr "大小(.ipk)"
 
 msgid "Skip"
 msgstr "跳过"
@@ -2898,7 +2908,7 @@ msgid ""
 "flashed manually. Please refer to the wiki for device specific install "
 "instructions."
 msgstr ""
-"抱歉,您的设备暂不支持 Sysupgrade 升级,需手动更新固件。请参考 Wiki 中关于此"
+"抱歉,您的设备暂不支持 sysupgrade 升级,需手动更新固件。请参考 Wiki 中关于此"
 "设备的固件更新说明。"
 
 msgid "Sort"
@@ -2917,35 +2927,34 @@ msgid "Specifies the directory the device is attached to"
 msgstr "指定设备的挂载目录"
 
 msgid "Specifies the listening port of this <em>Dropbear</em> instance"
-msgstr "指定 <em>Dropbear</em> 的监听端口"
+msgstr "指定此 <em>Dropbear</em> 实例的监听端口"
 
 msgid ""
 "Specifies the maximum amount of failed ARP requests until hosts are presumed "
 "to be dead"
-msgstr "指定假设主机已丢失的最大失败 ARP 请求数"
+msgstr "判定主机已下线的最少 ARP 请求失败数"
 
 msgid ""
 "Specifies the maximum amount of seconds after which hosts are presumed to be "
 "dead"
-msgstr "指定假设主机已丢失的最大时间 (秒)"
+msgstr "判断主机已下线的超时时间(秒)"
 
 msgid "Specify a TOS (Type of Service)."
-msgstr "指定 TOS (服务类型)。"
+msgstr "指定 TOS(服务类型)。"
 
 msgid ""
 "Specify a TTL (Time to Live) for the encapsulating packet other than the "
 "default (64)."
-msgstr "为封装数据包设置 TTL (生存时间),缺省值: 64"
+msgstr "为封装数据包设置 TTL(生存时间),缺省值:64"
 
 msgid ""
 "Specify an MTU (Maximum Transmission Unit) other than the default (1280 "
 "bytes)."
-msgstr "设置 MTU (最大传输单位),缺省值: 1280 bytes"
+msgstr "设置 MTU(最大传输单位),缺省值:1280 bytes"
 
 msgid "Specify the secret encryption key here."
 msgstr "在此指定密钥。"
 
-# 关联了 启动项 和 接口>LAN>DHCP服务器>网址分配基址
 msgid "Start"
 msgstr "开始"
 
@@ -2997,10 +3006,10 @@ msgid "Suppress logging of the routine operation of these protocols"
 msgstr "不记录这些协议的常规操作日志。"
 
 msgid "Swap"
-msgstr "交换区"
+msgstr "Swap"
 
 msgid "Swap Entry"
-msgstr "交换项目"
+msgstr "Swap 节点"
 
 msgid "Switch"
 msgstr "交换机"
@@ -3009,17 +3018,17 @@ msgid "Switch %q"
 msgstr "交换机 %q"
 
 msgid "Switch %q (%s)"
-msgstr "交换机 %q (%s)"
+msgstr "交换机 %q(%s)"
 
 msgid ""
 "Switch %q has an unknown topology - the VLAN settings might not be accurate."
-msgstr "交换机 %q 具有未知的拓扑结构 - VLAN 设置可能不正确。"
+msgstr "交换机 %q 具有未知的拓扑结构VLAN 设置可能不正确。"
 
 msgid "Switch VLAN"
 msgstr "交换机 VLAN"
 
 msgid "Switch protocol"
-msgstr "交换机协议"
+msgstr "切换协议"
 
 msgid "Sync with browser"
 msgstr "同步浏览器时间"
@@ -3040,13 +3049,13 @@ msgid "System log buffer size"
 msgstr "系统日志缓冲区大小"
 
 msgid "TCP:"
-msgstr "TCP:"
+msgstr "TCP"
 
 msgid "TFTP Settings"
-msgstr "TFTP设置"
+msgstr "TFTP 设置"
 
 msgid "TFTP server root"
-msgstr "TFTP服务器根目录"
+msgstr "TFTP 服务器根目录"
 
 msgid "TX"
 msgstr "发送"
@@ -3073,14 +3082,14 @@ msgid ""
 "multi-SSID capable). Per network settings like encryption or operation mode "
 "are grouped in the <em>Interface Configuration</em>."
 msgstr ""
-"<em>设备配置</em>区域可配置无线的硬件参数,比如信道、发射功率或发射天线 (如果"
-"此无线模块硬件支持多 SSID,则全部SSID共用此设备配置)。<em>接口配置</em>区域则"
-"å\8f¯é\85\8d置此ç½\91ç»\9cç\9a\84å·¥ä½\9c模å¼\8få\92\8cå\8a å¯\86等。"
+"“设备配置”区域可配置无线的硬件参数,比如:信道、发射功率或发射天线,如果此无"
+"线硬件支持多 SSID,则全部 SSID 共用此设备配置。“接口配置”区域则可配置接口各自"
+"å\8f\82æ\95°ï¼\8cå¦\82å·¥ä½\9c模å¼\8fã\80\81å\8a å¯\86æ\96¹å¼\8f等。"
 
 msgid ""
 "The <em>libiwinfo-lua</em> package is not installed. You must install this "
 "component for working wireless configuration!"
-msgstr "软件包 <em>libiwinfo-lua</em> 未安装。必需安装此组件以配置无线!"
+msgstr "软件包 <em>libiwinfo-lua</em> 未安装,必须安装此组件以配置无线!"
 
 msgid ""
 "The HE.net endpoint update configuration changed, you must now use the plain "
@@ -3099,24 +3108,24 @@ msgid ""
 "The allowed characters are: <code>A-Z</code>, <code>a-z</code>, <code>0-9</"
 "code> and <code>_</code>"
 msgstr ""
-"合法字符<code>A-Z</code>, <code>a-z</code>, <code>0-9</code> 和 <code>_</"
+"合法字符<code>A-Z</code>, <code>a-z</code>, <code>0-9</code> 和 <code>_</"
 "code>"
 
 msgid "The configuration file could not be loaded due to the following error:"
-msgstr "由于以下错误,配置文件无法被加载:"
+msgstr "由于以下错误,配置文件无法被加载"
 
 msgid ""
 "The device file of the memory or partition (<abbr title=\"for example\">e.g."
 "</abbr> <code>/dev/sda1</code>)"
-msgstr "存储器或分区的设备节点,(例如: <code>/dev/sda1</code>)"
+msgstr "存储器或分区的设备文件,(例如:<code>/dev/sda1</code>)"
 
 msgid ""
 "The filesystem that was used to format the memory (<abbr title=\"for example"
 "\">e.g.</abbr> <samp><abbr title=\"Third Extended Filesystem\">ext3</abbr></"
 "samp>)"
 msgstr ""
-"用于格式化存储器的文件系统,(例如: <samp><abbr title=\"第三代扩展文件系统"
-"\">ext3</abbr></samp>)"
+"用于格式化存储器的文件系统,(例如:<samp><abbr title=\"Third Extended "
+"Filesystem\">ext3</abbr></samp>)"
 
 msgid ""
 "The flash image was uploaded. Below is the checksum and file size listed, "
@@ -3146,13 +3155,13 @@ msgstr "本机的硬件不支持多 SSID,如果继续,现有配置将被替
 msgid ""
 "The length of the IPv4 prefix in bits, the remainder is used in the IPv6 "
 "addresses."
-msgstr "IPv4 前缀长度 (bit),其余的用在 IPv6 地址。"
+msgstr "IPv4 前缀长度(bit),其余的用在 IPv6 地址。"
 
 msgid "The length of the IPv6 prefix in bits"
-msgstr "IPv6 前缀长度 (bit)"
+msgstr "IPv6 前缀长度(bit)"
 
 msgid "The local IPv4 address over which the tunnel is created (optional)."
-msgstr "所创建隧道的本地 IPv4 地址 (可选)。"
+msgstr "所创建隧道的本地 IPv4 地址(可选)。"
 
 msgid ""
 "The network ports on this device can be combined to several <abbr title="
@@ -3162,9 +3171,10 @@ msgid ""
 "segments. Often there is by default one Uplink port for a connection to the "
 "next greater network like the internet and other ports for a local network."
 msgstr ""
-"本设备可以划分为多个 <abbr title=\"虚拟局域网\">VLAN</abbr>,并支持电脑间的直"
-"接通讯。<abbr title=\"虚拟局域网\">VLAN</abbr> 也常用于分割不同网段。默认通常"
-"是一条上行端口连接 ISP,其余端口为本地子网。"
+"本设备可以划分为多个 <abbr title=\"Virtual Local Area Network\">VLAN</abbr>,"
+"并支持电脑间的直接通讯。<abbr title=\"Virtual Local Area Network\">VLAN</"
+"abbr> 也常用于分割不同网段。默认通常是一条上行端口连接 ISP,其余端口为本地子"
+"网。"
 
 msgid "The selected protocol needs a device assigned"
 msgstr "所选的协议需要分配设备"
@@ -3216,7 +3226,7 @@ msgstr "尚未分配设备,请在“物理设置”选项卡中选择网络设
 msgid ""
 "There is no password set on this router. Please configure a root password to "
 "protect the web interface and enable SSH."
-msgstr "尚未设置密码。请为 Root 用户设置密码以保护主机并开启 SSH。"
+msgstr "尚未设置密码。请为 root 用户设置密码以保护主机并启用 SSH。"
 
 msgid "This IPv4 address of the relay"
 msgstr "中继的 IPv4 地址"
@@ -3227,7 +3237,7 @@ msgid ""
 "Name System\">DNS</abbr> servers."
 msgstr ""
 "此文件包含类似于 'server=/domain/1.2.3.4' 或 'server=1.2.3.4' 的行,用于解析"
-"特定域名或指定上游 <abbr title=\"域名服务系统\">DNS</abbr> 服务器。"
+"特定域名或指定上游 <abbr title=\"Domain Name System\">DNS</abbr> 服务器。"
 
 msgid ""
 "This is a list of shell glob patterns for matching files and directories to "
@@ -3240,7 +3250,7 @@ msgstr ""
 msgid ""
 "This is either the \"Update Key\" configured for the tunnel or the account "
 "password if no update key has been configured"
-msgstr "如果更新密钥没有设置的话,隧道的“更新密钥”或者账户密码必须填写。"
+msgstr "如果更新密钥没有设置的话隧道的“更新密钥”或者账户密码必须填写。"
 
 msgid ""
 "This is the content of /etc/rc.local. Insert your own commands here (in "
@@ -3255,7 +3265,9 @@ msgstr "隧道代理分配的本地终端地址,通常以 <code>:2</code> 结
 msgid ""
 "This is the only <abbr title=\"Dynamic Host Configuration Protocol\">DHCP</"
 "abbr> in the local network"
-msgstr "这是内网中唯一的 <abbr title=\"动态主机配置协议\">DHCP</abbr> 服务器"
+msgstr ""
+"这是本地网络中唯一的 <abbr title=\"Dynamic Host Configuration Protocol"
+"\">DHCP</abbr> 服务器"
 
 msgid "This is the plain username for logging into the account"
 msgstr "登录账户时填写的用户名"
@@ -3265,7 +3277,7 @@ msgid ""
 msgstr "这是隧道代理分配给你的路由前缀,供客户端使用"
 
 msgid "This is the system crontab in which scheduled tasks can be defined."
-msgstr "自定义系统 Crontab 中的计划任务。"
+msgstr "自定义系统 crontab 中的计划任务。"
 
 msgid ""
 "This is usually the address of the nearest PoP operated by the tunnel broker"
@@ -3327,7 +3339,7 @@ msgid "Transmitter Antenna"
 msgstr "传送天线"
 
 msgid "Trigger"
-msgstr "触发"
+msgstr "触发"
 
 msgid "Trigger Mode"
 msgstr "触发模式"
@@ -3357,10 +3369,10 @@ msgid "Type"
 msgstr "类型"
 
 msgid "UDP:"
-msgstr "UDP:"
+msgstr "UDP"
 
 msgid "UMTS only"
-msgstr "仅 UMTS (WCDMA)"
+msgstr "仅 UMTS(WCDMA)"
 
 msgid "UMTS/GPRS/EV-DO"
 msgstr "UMTS/GPRS/EV-DO"
@@ -3378,7 +3390,7 @@ msgid "Unable to dispatch"
 msgstr "无法调度"
 
 msgid "Unavailable Seconds (UAS)"
-msgstr "不可用秒数 (UAS)"
+msgstr "不可用秒数(UAS)"
 
 msgid "Unknown"
 msgstr "未知"
@@ -3405,7 +3417,9 @@ msgid ""
 "Upload a sysupgrade-compatible image here to replace the running firmware. "
 "Check \"Keep settings\" to retain the current configuration (requires a "
 "compatible firmware image)."
-msgstr "上传兼容的 Sysupgrade 固件以刷新当前系统。"
+msgstr ""
+"上传一个 sysupgrade 格式的固件映像文件以替换当前运行的固件。勾选“保留配置”以"
+"使更新后的系统仍然使用当前的系统配置(新的固件需要和当前固件兼容)。"
 
 msgid "Upload archive..."
 msgstr "上传备份..."
@@ -3423,7 +3437,7 @@ msgid "Use DHCP gateway"
 msgstr "使用 DHCP 网关"
 
 msgid "Use DNS servers advertised by peer"
-msgstr "使用端局通告的DNS服务器"
+msgstr "使用对端通告的 DNS 服务器"
 
 msgid "Use ISO/IEC 3166 alpha2 country codes."
 msgstr "参考 ISO/IEC 3166 alpha2 国家代码。"
@@ -3435,10 +3449,10 @@ msgid "Use TTL on tunnel interface"
 msgstr "隧道接口的 TTL"
 
 msgid "Use as external overlay (/overlay)"
-msgstr "作为外部 Overlay 使用 (/overlay)"
+msgstr "作为外部 overlay 使用(/overlay)"
 
 msgid "Use as root filesystem (/)"
-msgstr "作为根文件系统使用 (/)"
+msgstr "作为根文件系统使用(/)"
 
 msgid "Use broadcast flag"
 msgstr "使用广播标签"
@@ -3465,8 +3479,9 @@ msgid ""
 "requesting host. The optional <em>Lease time</em> can be used to set non-"
 "standard host-specific lease time, e.g. 12h, 3d or infinite."
 msgstr ""
-"使用<em>添加</em>来增加新的租约条目。使用<em>MAC-地址</em>鉴别主机,<em>IPv4-"
-"地址</em>分配地址,<em>主机名</em>分配标识。"
+"使用“添加”按钮来增加新的租约条目。“IPv4 地址”和“主机名”字段的值将被固定分配"
+"给“MAC 地址”字段标识的主机,“租期”是一个可选字段,可为每个主机单独设定 DHCP "
+"租期的时长,例如:12h、3d、inifinite,分别表示 12 小时、3 天、永久。"
 
 msgid "Used"
 msgstr "已用"
@@ -3478,14 +3493,14 @@ msgid ""
 "Used for two different purposes: RADIUS NAS ID and 802.11r R0KH-ID. Not "
 "needed with normal WPA(2)-PSK."
 msgstr ""
-"用于两种不同的用途: RADIUS NAS ID 和 802.11r R0KH-ID。普通 WPA(2)-PSK 不需"
+"用于两种不同的用途:RADIUS NAS ID 和 802.11r R0KH-ID,普通 WPA(2)-PSK 不需"
 "要。"
 
 msgid "User certificate (PEM encoded)"
-msgstr "客户证书 (PEM加密的)"
+msgstr "用户证书(PEM)"
 
 msgid "User key (PEM encoded)"
-msgstr "客户 Key (PEM加密的)"
+msgstr "用户密钥(PEM)"
 
 msgid "Username"
 msgstr "用户名"
@@ -3500,7 +3515,7 @@ msgid "VLANs on %q"
 msgstr "%q 上的 VLAN"
 
 msgid "VLANs on %q (%s)"
-msgstr "%q (%s) 上的 VLAN"
+msgstr "%q(%s)上的 VLAN"
 
 msgid "VPN Local address"
 msgstr "VPN 本地地址"
@@ -3518,13 +3533,13 @@ msgid "VPN Server's certificate SHA1 hash"
 msgstr "VPN 服务器证书的 SHA1 哈希值"
 
 msgid "VPNC (CISCO 3000 (and others) VPN)"
-msgstr "VPNC (CISCO 3000 和其他 VPN)"
+msgstr "VPNC(CISCO 3000 和其他 VPN)"
 
 msgid "Vendor"
 msgstr "Vendor"
 
 msgid "Vendor Class to send when requesting DHCP"
-msgstr "请求 DHCP 时发送的 Vendor Class"
+msgstr "请求 DHCP 时发送的 Vendor Class 选项"
 
 msgid "Verbose"
 msgstr "详细"
@@ -3542,7 +3557,7 @@ msgid "WDS"
 msgstr "WDS"
 
 msgid "WEP Open System"
-msgstr "WEP 开放认证"
+msgstr "WEP 开放式系统"
 
 msgid "WEP Shared Key"
 msgstr "WEP 共享密钥"
@@ -3551,7 +3566,7 @@ msgid "WEP passphrase"
 msgstr "WEP 密钥"
 
 msgid "WMM Mode"
-msgstr "WMM 多媒体加速"
+msgstr "WMM 模式"
 
 msgid "WPA passphrase"
 msgstr "WPA 密钥"
@@ -3560,18 +3575,18 @@ msgid ""
 "WPA-Encryption requires wpa_supplicant (for client mode) or hostapd (for AP "
 "and ad-hoc mode) to be installed."
 msgstr ""
-"WPA 加密需要安装 wpa_supplicant (客户端模式) 或安装 hostapd (接入点 AP、点对"
-"点 Ad-Hoc 模式)。"
+"WPA 加密需要安装 wpa_supplicant(客户端模式)或安装 hostapd(接入点 AP、点对"
+"点 Ad-Hoc 模式。"
 
 msgid ""
 "Wait for NTP sync that many seconds, seting to 0 disables waiting (optional)"
-msgstr "在 NTP 同步之前等待时间,设置为 0 表示同步之前不等待 (可选)"
+msgstr "NTP 同步前的等待时间,设置为 0 表示不等待(可选)"
 
 msgid "Waiting for changes to be applied..."
 msgstr "正在应用更改..."
 
 msgid "Waiting for command to complete..."
-msgstr "正在执行命令..."
+msgstr "等待命令执行完成..."
 
 msgid "Waiting for device..."
 msgstr "等待设备..."
@@ -3580,10 +3595,10 @@ msgid "Warning"
 msgstr "警告"
 
 msgid "Warning: There are unsaved changes that will get lost on reboot!"
-msgstr "警告: 有一些未保存的配置将在重启后丢失!"
+msgstr "警告一些未保存的配置将在重启后丢失!"
 
 msgid "Whether to create an IPv6 default route over the tunnel"
-msgstr "是否通过隧道创建 IPv6 缺省路由"
+msgstr "是否添加一条通向隧道的 IPv6 默认路由"
 
 msgid "Whether to route only packets from delegated prefixes"
 msgstr "是否仅路由来自分发前缀的数据包"
@@ -3610,10 +3625,10 @@ msgid "Wireless Security"
 msgstr "无线安全"
 
 msgid "Wireless is disabled or not associated"
-msgstr "æ\9cªå¼\80å\90¯æ\88\96æ\9cªå\85³è\81\94æ\97 çº¿"
+msgstr "æ\97 çº¿æ\9cªå¼\80å\90¯æ\88\96æ\9cªå\85³è\81\94"
 
 msgid "Wireless is restarting..."
-msgstr "重启无线中..."
+msgstr "无线重启中..."
 
 msgid "Wireless network is disabled"
 msgstr "无线已禁用"
@@ -3638,20 +3653,20 @@ msgid ""
 "after a device reboot.<br /><strong>Warning: If you disable essential init "
 "scripts like \"network\", your device might become inaccessible!</strong>"
 msgstr ""
-"å\90¯ç\94¨æ\88\96ç¦\81ç\94¨å·²å®\89è£\85ç\9a\84å\90¯å\8a¨è\84\9aæ\9c¬ã\80\82æ\9b´æ\94¹å\9c¨è®¾å¤\87é\87\8då\90¯å\90\8eç\94\9fæ\95\88ã\80\82<br /><strong>è­¦å\91\8a: å¦\82æ\9e\9cç¦\81"
-"用了必要的启动脚本,比如 \"network\",可能会导致设备无法访问!</strong>"
+"å\9c¨æ­¤å\90¯ç\94¨æ\88\96ç¦\81ç\94¨å·²å®\89è£\85ç\9a\84å\90¯å\8a¨è\84\9aæ\9c¬ï¼\8cæ\9b´æ\94¹å\9c¨è®¾å¤\87é\87\8då\90¯å\90\8eç\94\9fæ\95\88ã\80\82<br /><strong>è­¦å\91\8aï¼\9aå¦\82"
+"果禁用了必要的启动脚本,比如 \"network\",可能会导致无法访问设备!</strong>"
 
 msgid ""
 "You must enable JavaScript in your browser or LuCI will not work properly."
-msgstr "LUCI 的正常运行需要开启浏览器的 JavaScript 支持。"
+msgstr "必须开启浏览器的 JavaScript 支持,否则 LuCI 无法正常工作。"
 
 msgid ""
 "Your Internet Explorer is too old to display this page correctly. Please "
 "upgrade it to at least version 7 or use another browser like Firefox, Opera "
 "or Safari."
 msgstr ""
-"你的 Internet Explorer 已经老到无法正常显示这个页面了!请更新到 IE7 及以上或"
-"者使用诸如 Firefox Opera Safari 之类的浏览器。"
+"你的 IE 浏览器太老了,无法正常显示这个页面!请更新到 IE7 及以上或使用其他浏览"
+"器,例如:Chrome、Firefox、Opera、Safari。"
 
 msgid "any"
 msgstr "任意"
@@ -3666,7 +3681,7 @@ msgid "bridged"
 msgstr "桥接的"
 
 msgid "create:"
-msgstr "创建:"
+msgstr "创建"
 
 msgid "creates a bridge over specified interface(s)"
 msgstr "为指定接口创建桥接"
@@ -3689,7 +3704,9 @@ msgstr "过期时间"
 msgid ""
 "file where given <abbr title=\"Dynamic Host Configuration Protocol\">DHCP</"
 "abbr>-leases will be stored"
-msgstr "存放 <abbr title=\"动态主机配置协议\">DHCP</abbr> 租约的文件"
+msgstr ""
+"用于存放已分配的 <abbr title=\"Dynamic Host Configuration Protocol\">DHCP</"
+"abbr> 租约的文件"
 
 msgid "forward"
 msgstr "转发"
@@ -3725,7 +3742,7 @@ msgid "kbit/s"
 msgstr "kbit/s"
 
 msgid "local <abbr title=\"Domain Name System\">DNS</abbr> file"
-msgstr "本地 <abbr title=\"域名服务系统\">DNS</abbr> 解析文件"
+msgstr "本地 <abbr title=\"Domain Name Syste\">DNS</abbr> 解析文件"
 
 msgid "minimum 1280, maximum 1480"
 msgstr "最小值 1280,最大值 1480"
@@ -3773,13 +3790,13 @@ msgid "stateless"
 msgstr "无状态的"
 
 msgid "stateless + stateful"
-msgstr "æ\9c\89ç\8a¶æ\80\81å\92\8cæ\97 状态的"
+msgstr "æ\97 ç\8a¶æ\80\81ç\9a\84 + æ\9c\89状态的"
 
 msgid "tagged"
-msgstr "关联"
+msgstr "å·²å\85³è\81\94"
 
 msgid "time units (TUs / 1.024 ms) [1000-65535]"
-msgstr "时间单位 (TUs / 1.024ms) [1000-65535]"
+msgstr "时间单位(TUs / 1.024ms)[1000-65535]"
 
 msgid "unknown"
 msgstr "未知"
@@ -3791,10 +3808,10 @@ msgid "unspecified"
 msgstr "未指定"
 
 msgid "unspecified -or- create:"
-msgstr "未指定或创建:"
+msgstr "不指定或新建:"
 
 msgid "untagged"
-msgstr "关联"
+msgstr "关联"
 
 msgid "yes"
 msgstr "是"
@@ -3824,10 +3841,10 @@ msgstr "« 后退"
 #~ msgstr "自动"
 
 #~ msgid "AR Support"
-#~ msgstr "AR支持"
+#~ msgstr "AR 支持"
 
 #~ msgid "Atheros 802.11%s Wireless Controller"
-#~ msgstr "Qualcomm/Atheros 802.11%s 无线网卡"
+#~ msgstr "Qualcomm/Atheros 802.11%s 无线控制器"
 
 #~ msgid "Background Scan"
 #~ msgstr "后台搜索"
@@ -3836,7 +3853,7 @@ msgstr "« 后退"
 #~ msgstr "压缩"
 
 #~ msgid "Disable HW-Beacon timer"
-#~ msgstr "停用HW-Beacon计时器"
+#~ msgstr "停用 HW-Beacon 计时器"
 
 #~ msgid "Do not send probe responses"
 #~ msgstr "不回送探测响应"
@@ -3860,19 +3877,19 @@ msgstr "« 后退"
 #~ msgstr "无线网络国家区域"
 
 #~ msgid "Separate WDS"
-#~ msgstr "隔离WDS"
+#~ msgstr "隔离 WDS"
 
 #~ msgid "Static WDS"
-#~ msgstr "静态WDS"
+#~ msgstr "静态 WDS"
 
 #~ msgid "Turbo Mode"
-#~ msgstr "Turbo模式"
+#~ msgstr "Turbo 模式"
 
 #~ msgid "XR Support"
-#~ msgstr "XR支持"
+#~ msgstr "XR 支持"
 
 #~ msgid "Required. Public key of peer."
-#~ msgstr "必须,Peer的公钥。"
+#~ msgstr "必须,Peer 的公钥。"
 
 #~ msgid "An additional network will be created if you leave this checked."
 #~ msgstr "如果选中此复选框,则会创建一个附加网络。"
@@ -3890,7 +3907,7 @@ msgstr "« 后退"
 #~ msgstr "端口 %d"
 
 #~ msgid "Port %d is untagged in multiple VLANs!"
-#~ msgstr "端口 %d 在多个VLAN中均未关联!"
+#~ msgstr "端口 %d 在多个 VLAN 中均未关联!"
 
 #~ msgid "VLAN Interface"
-#~ msgstr "VLAN接口"
+#~ msgstr "VLAN 接口"
index 8915976..7b2792e 100644 (file)
@@ -2319,6 +2319,9 @@ msgstr ""
 msgid "Password successfully changed!"
 msgstr "密碼已變更成功!"
 
+msgid "Password2"
+msgstr ""
+
 msgid "Path to CA-Certificate"
 msgstr "CA-證書的路徑"
 
@@ -3010,7 +3013,7 @@ msgid "Switch VLAN"
 msgstr ""
 
 msgid "Switch protocol"
-msgstr "交換器協定"
+msgstr "切換協定"
 
 msgid "Sync with browser"
 msgstr "同步瀏覽器"