diff --git a/package/luci-compat/files/luci/i18n/smartdns.zh-cn.po b/package/luci-compat/files/luci/i18n/smartdns.zh-cn.po index cd1ca6a..cf6670f 100644 --- a/package/luci-compat/files/luci/i18n/smartdns.zh-cn.po +++ b/package/luci-compat/files/luci/i18n/smartdns.zh-cn.po @@ -22,7 +22,7 @@ msgstr "自动设置为Dnsmasq的上游服务器" msgid "Cache Size" msgstr "缓存大小" -msgid "Collecting data ..." +msgid "Collecting data..." msgstr "正在收集数据..." msgid "" @@ -30,12 +30,27 @@ msgid "" "DNS server." msgstr "配置需要从指定域名服务器结果过滤的IP黑名单。" +msgid "Configure block domain list." +msgstr "配置屏蔽域名列表" + +msgid "Configure forwarding domain name list." +msgstr "配置分流域名列表" + msgid "Custom Settings" msgstr "自定义设置" +msgid "DNS Block Setting" +msgstr "域名屏蔽设置" + +msgid "DNS Forwarding Setting" +msgstr "域名分流设置" + msgid "DNS Server Name" msgstr "DNS服务器名称" +msgid "DNS Server group belongs to, such as office, home." +msgstr "配置归属服务器组,例如office, home" + msgid "" "DNS Server group belongs to, used with nameserver, such as office, home." msgstr "DNS服务器所属组, 配合nameserver使用,例如:office,home。" @@ -52,8 +67,8 @@ msgstr "协议类型" msgid "DNS domain result cache size" msgstr "缓存DNS的结果,缓存大小,配置零则不缓存" -msgid "Dnsmasq Forwared To Smartdns Failure" -msgstr "重定向dnsmasq到smartdns失败" +msgid "Description" +msgstr "描述" msgid "Do not check certificate." msgstr "不校验证书的合法性。" @@ -64,6 +79,18 @@ msgstr "禁用测速。" msgid "Domain Address" msgstr "域名地址" +msgid "Domain List" +msgstr "域名列表" + +msgid "Domain List File" +msgstr "域名列表文件" + +msgid "Domain Rules" +msgstr "域名规则" + +msgid "Domain Rules Settings" +msgstr "域名规则设置" + msgid "Domain TTL" msgstr "域名TTL" @@ -82,12 +109,26 @@ msgstr "捐助" msgid "Donate to smartdns" msgstr "捐助smartdns项目" +msgid "Download Files" +msgstr "下载文件" + +msgid "Download Files Setting" +msgstr "下载文件设置" + +msgid "" +"Download domain list files for domain-rule and include config files, please " +"refresh the page after download to take effect." +msgstr "下载域名文件列表,下载后刷新页面生效" + msgid "Dual-stack IP Selection" msgstr "双栈IP优选" msgid "Enable" msgstr "启用" +msgid "Enable Auto Update" +msgstr "启用自动更新" + msgid "Enable IP selection between IPV4 and IPV6" msgstr "启用 IPV4 和 IPV6 间的 IP 优选策略" @@ -97,6 +138,9 @@ msgstr "启用IPV6服务器" msgid "Enable TCP DNS Server" msgstr "启用TCP服务器" +msgid "Enable daily auto update." +msgstr "启用每日自动更新。" + msgid "Enable domain prefetch, accelerate domain response speed." msgstr "启用域名预加载,加速域名响应速度。" @@ -106,6 +150,18 @@ msgstr "是否启用第二DNS服务器。" msgid "Enable or disable smartdns server" msgstr "启用或禁用SmartDNS服务" +msgid "Exclude DNS Server from default group." +msgstr "从default默认服务器组中排除" + +msgid "Exclude Default Group" +msgstr "从默认服务器组排除" + +msgid "File Name" +msgstr "文件名" + +msgid "File Type" +msgstr "文件类型" + msgid "Filtering IP with blacklist" msgstr "使用IP黑名单过滤" @@ -133,9 +189,6 @@ msgid "" msgstr "" "当smartdns异常时生成coredump文件,coredump文件在/tmp/smartdns.xxx.core." -msgid "Grant access to LuCI app smartdns" -msgstr "授予访问 LuCI 应用 smartdns 的权限" - msgid "HTTP Host" msgstr "HTTP主机" @@ -148,9 +201,26 @@ msgstr "IP黑名单过滤" msgid "IPV6 Server" msgstr "IPV6服务器" +msgid "IPset Name" +msgstr "IPSet名称" + +msgid "IPset name." +msgstr "IPSet名称。" + msgid "If you like this software, please buy me a cup of coffee." msgstr "如果本软件对你有帮助,请给作者加个蛋。" +msgid "Include Config Files
/etc/smartdns/conf.d" +msgstr "包含配置文件" + +msgid "" +"Include other config files from /etc/smartdns/conf.d or custom path, can be " +"downloaded from the download page." +msgstr "包含配置文件,路径为/etc/smartdns/conf.d,或自定义配置文件路径,可以从下载页" + +msgid "List of files to download." +msgstr "下载的文件列表。" + msgid "Local Port" msgstr "本地端口" @@ -160,6 +230,15 @@ msgstr "所有域名的最大 TTL 值。" msgid "Minimum TTL for all domain result." msgstr "所有域名的最小 TTL 值。" +msgid "NFTset Name" +msgstr "NFTSet名称" + +msgid "NFTset name format error, format: [#[4|6]:[family#table#set]]" +msgstr "NFTSet名称格式错误,格式:[#[4|6]:[family#table#set]]" + +msgid "NFTset name, format: [#[4|6]:[family#table#set]]" +msgstr "NFTSet名称,格式:[#[4|6]:[family#table#set]]" + msgid "NOT RUNNING" msgstr "未运行" @@ -184,6 +263,12 @@ msgstr "解析本地主机名" msgid "Resolve local hostnames by reading Dnsmasq lease file." msgstr "读取Dnsmasq的租约文件解析本地主机名。" +msgid "Restart" +msgstr "重启" + +msgid "Restart smartdns" +msgstr "重启服务" + msgid "Second Server Settings" msgstr "第二DNS服务器" @@ -193,6 +278,9 @@ msgstr "缓存过期服务" msgid "Server Group" msgstr "服务器组" +msgid "Server Group not exists" +msgstr "服务器组不存在" + msgid "Server Name" msgstr "服务器名称" @@ -275,7 +363,8 @@ msgstr "SmartDNS本地服务端口" msgid "" "Smartdns local server port, smartdns will be automatically set as main dns " "when the port is 53." -msgstr "SmartDNS本地服务端口,当端口号设置为53时,smartdns将会自动配置为主dns。" +msgstr "" +"SmartDNS本地服务端口,当端口号设置为53时,smartdns将会自动配置为主dns。" msgid "Smartdns server name" msgstr "SmartDNS的服务器名称,默认为smartdns,留空为主机名" @@ -306,6 +395,38 @@ msgstr "设置所有域名的 TTL 值。" msgid "Technical Support" msgstr "技术支持" +msgid "URL" +msgstr "URL" + +msgid "URL format error, format: http:// or https://" +msgstr "URL格式错误,格式:http://或https://" + +msgid "Update Files" +msgstr "更新文件" + +msgid "Upload Config File" +msgstr "上传域名列表文件" + +msgid "Upload Domain List File" +msgstr "上传域名列表文件" + +msgid "Upload domain list file to /etc/smartdns/domain-set" +msgstr "上传域名列表文件到/etc/smartdns/domain-set目录" + +msgid "" +"Upload domain list file, or configure auto download from Download File " +"Setting page." +msgstr "上传域名列表文件,或在下载文件设置页面设置自动下载。" + +msgid "Upload domain list file." +msgstr "上传域名列表文件。" + +msgid "Upload smartdns config file to /etc/smartdns/conf.d" +msgstr "上传配置文件到/etc/smartdns/conf.d目录" + +msgid "Upstream DNS Server Configuration" +msgstr "上游服务器配置" + msgid "Upstream Servers" msgstr "上游服务器" @@ -324,6 +445,9 @@ msgstr "" "用于校验 TLS 服务器的有效性,数值为 Base64 编码的 SPKI 指纹,留空表示不验证 " "TLS 的合法性。" +msgid "domain list (/etc/smartdns/domain-set)" +msgstr "域名列表(/etc/smartdns/domain-set)" + msgid "https" msgstr "https" @@ -336,6 +460,9 @@ msgstr "打开网站" msgid "port" msgstr "端口" +msgid "smartdns config (/etc/smartdns/conf.d)" +msgstr "配置文件(/etc/smartdns/conf.d)" + msgid "smartdns custom settings" msgstr "smartdns 自定义设置,具体配置参数参考指导" @@ -350,3 +477,6 @@ msgstr "类型" msgid "udp" msgstr "udp" + +msgid "update domain list files" +msgstr "更新列表文件" diff --git a/package/luci-compat/files/luci/model/cbi/smartdns/smartdns.lua b/package/luci-compat/files/luci/model/cbi/smartdns/smartdns.lua index 9937c70..45b9479 100644 --- a/package/luci-compat/files/luci/model/cbi/smartdns/smartdns.lua +++ b/package/luci-compat/files/luci/model/cbi/smartdns/smartdns.lua @@ -19,6 +19,8 @@ require ("luci.http") require ("luci.dispatcher") require ("nixio.fs") +local uci = require "luci.model.uci".cursor() + m = Map("smartdns") m.title = translate("SmartDNS Server") m.description = translate("SmartDNS is a local high-performance DNS server, supports finding fastest IP, supports ad filtering, and supports avoiding DNS poisoning.") @@ -30,6 +32,7 @@ s = m:section(TypedSection, "smartdns", translate("Settings"), translate("Genera s.anonymous = true s:tab("settings", translate("General Settings")) +s:tab("advanced", translate('Advanced Settings')) s:tab("seconddns", translate("Second Server Settings")) s:tab("custom", translate("Custom Settings")) @@ -69,7 +72,7 @@ o.cfgvalue = function(...) end ---- Support DualStack ip selection -o = s:taboption("settings", Flag, "dualstack_ip_selection", translate("Dual-stack IP Selection"), translate("Enable IP selection between IPV4 and IPV6")) +o = s:taboption("advanced", Flag, "dualstack_ip_selection", translate("Dual-stack IP Selection"), translate("Enable IP selection between IPV4 and IPV6")) o.rmempty = false o.default = o.enabled o.cfgvalue = function(...) @@ -77,7 +80,7 @@ o.cfgvalue = function(...) end ---- Domain prefetch load -o = s:taboption("settings", Flag, "prefetch_domain", translate("Domain prefetch"), translate("Enable domain prefetch, accelerate domain response speed.")) +o = s:taboption("advanced", Flag, "prefetch_domain", translate("Domain prefetch"), translate("Enable domain prefetch, accelerate domain response speed.")) o.rmempty = false o.default = o.disabled o.cfgvalue = function(...) @@ -85,7 +88,7 @@ o.cfgvalue = function(...) end ---- Domain Serve expired -o = s:taboption("settings", Flag, "serve_expired", translate("Serve expired"), +o = s:taboption("advanced", Flag, "serve_expired", translate("Serve expired"), translate("Attempts to serve old responses from cache with a TTL of 0 in the response without waiting for the actual resolution to finish.")) o.rmempty = false o.default = o.enabled @@ -94,11 +97,11 @@ o.cfgvalue = function(...) end ---- cache-size -o = s:taboption("settings", Value, "cache_size", translate("Cache Size"), translate("DNS domain result cache size")) +o = s:taboption("advanced", Value, "cache_size", translate("Cache Size"), translate("DNS domain result cache size")) o.rempty = true -- cache-size -o = s:taboption("settings", Flag, "resolve_local_hostnames", translate("Resolve Local Hostnames"), translate("Resolve local hostnames by reading Dnsmasq lease file.")); +o = s:taboption("advanced", Flag, "resolve_local_hostnames", translate("Resolve Local Hostnames"), translate("Resolve local hostnames by reading Dnsmasq lease file.")) o.rmempty = false o.default = o.enabled o.cfgvalue = function(...) @@ -106,7 +109,7 @@ o.cfgvalue = function(...) end -- Automatically Set Dnsmasq -o = s:taboption("settings", Flag, "auto_set_dnsmasq", translate("Automatically Set Dnsmasq"), translate("Automatically set as upstream of dnsmasq when port changes.")); +o = s:taboption("advanced", Flag, "auto_set_dnsmasq", translate("Automatically Set Dnsmasq"), translate("Automatically set as upstream of dnsmasq when port changes.")) o.rmempty = false o.default = o.enabled o.cfgvalue = function(...) @@ -114,7 +117,7 @@ o.cfgvalue = function(...) end -- Force AAAA SOA -o = s:taboption("settings", Flag, "force_aaaa_soa", translate("Force AAAA SOA"), translate("Force AAAA SOA.")); +o = s:taboption("advanced", Flag, "force_aaaa_soa", translate("Force AAAA SOA"), translate("Force AAAA SOA.")) o.rmempty = false o.default = o.enabled o.cfgvalue = function(...) @@ -122,7 +125,7 @@ o.cfgvalue = function(...) end -- Force HTTPS SOA -o = s:taboption("settings", Flag, "force_https_soa", translate("Force HTTPS SOA"), translate("Force HTTPS SOA.")); +o = s:taboption("advanced", Flag, "force_https_soa", translate("Force HTTPS SOA"), translate("Force HTTPS SOA.")) o.rmempty = false o.default = o.enabled o.cfgvalue = function(...) @@ -130,24 +133,35 @@ o.cfgvalue = function(...) end ---- rr-ttl -o = s:taboption("settings", Value, "rr_ttl", translate("Domain TTL"), translate("TTL for all domain result.")) +o = s:taboption("advanced", Value, "rr_ttl", translate("Domain TTL"), translate("TTL for all domain result.")) o.rempty = true ---- rr-ttl-min -o = s:taboption("settings", Value, "rr_ttl_min", translate("Domain TTL Min"), translate("Minimum TTL for all domain result.")) +o = s:taboption("advanced", Value, "rr_ttl_min", translate("Domain TTL Min"), translate("Minimum TTL for all domain result.")) o.rempty = true o.placeholder = "600" o.default = 600 o.optional = true ---- rr-ttl-max -o = s:taboption("settings", Value, "rr_ttl_max", translate("Domain TTL Max"), translate("Maximum TTL for all domain result.")) +o = s:taboption("advanced", Value, "rr_ttl_max", translate("Domain TTL Max"), translate("Maximum TTL for all domain result.")) o.rempty = true ---- rr-ttl-reply-max -o = s:taboption("settings", Value, "rr_ttl_reply_max", translate("Reply Domain TTL Max"), translate("Reply maximum TTL for all domain result.")) +o = s:taboption("advanced", Value, "rr_ttl_reply_max", translate("Reply Domain TTL Max"), translate("Reply maximum TTL for all domain result.")) o.rempty = true +o = s:taboption("advanced", DynamicList, "conf_files", translate("Include Config Files
/etc/smartdns/conf.d"), + translate("Include other config files from /etc/smartdns/conf.d or custom path, can be downloaded from the download page.")); +uci:foreach("smartdns", "download-file", function(section) + local filetype = section.type + if (filetype ~= 'config') then + return + end + + o:value(section.name); +end) + ---- second dns server ---- Eanble o = s:taboption("seconddns", Flag, "seconddns_enabled", translate("Enable"), translate("Enable or disable second DNS server.")) @@ -305,11 +319,122 @@ o:value("https", translate("https")) o.default = "udp" o.rempty = false -s = m:section(TypedSection, "smartdns", translate("Advanced Settings"), translate("Advanced Settings")); -s.anonymous = true; +---- domain rules; +s = m:section(TypedSection, "domain-rule", translate("Domain Rules"), translate("Domain Rules Settings")) +s.anonymous = true +s.nodescriptions = true -s:tab("domain-address", translate("Domain Address"), translate("Set Specific domain ip address.")); -s:tab("blackip-list", translate("IP Blacklist"), translate("Set Specific ip blacklist.")); +s:tab("forwarding", translate('DNS Forwarding Setting')) +s:tab("block", translate("DNS Block Setting")) +s:tab("domain-address", translate("Domain Address"), translate("Set Specific domain ip address.")) +s:tab("blackip-list", translate("IP Blacklist"), translate("Set Specific ip blacklist.")) + +---- domain forwarding; +o = s:taboption("forwarding", Value, "server_group", translate("Server Group"), translate("DNS Server group belongs to, such as office, home.")) +o.rmempty = true +o.placeholder = "default" +o.datatype = "hostname" +o.rempty = true +uci:foreach("smartdns", "server", function(section) + local server_group = section.server_group + o:value(server_group); +end) + +function o.validate (section_id, value) + if (value == "") then + return value + end + + local exists = false + uci:foreach("smartdns", "server", function(section) + local server_group = section.server_group + if (exists == true) then + return + end + + if (value == server_group) then + exists = true + end + end) + + if (exists == false) then + return nil, translate('Server Group not exists') + end + + return value; + +end + +o = s:taboption("forwarding", Flag, "no_speed_check", translate("Skip Speed Check"), + translate("Do not check speed.")) +o.rmempty = false +o.default = o.disabled + +o = s:taboption("forwarding", Flag, "force_aaaa_soa", translate("Force AAAA SOA"), translate("Force AAAA SOA.")) +o.rmempty = false +o.default = o.disabled + +o = s:taboption("forwarding", Value, "ipset_name", translate("IPset Name"), translate("IPset name.")) +o.rmempty = true +o.datatype = "hostname" +o.rempty = true + +o = s:taboption("forwarding", Value, "nftset_name", translate("NFTset Name"), translate("NFTset name, format: [#[4|6]:[family#table#set]]")) +o.rmempty = true +o.datatype = "string" +o.rempty = true +function o.validate(self, value) + if (value == "") then + return value + end + + if (value:match("#[4|6]:[a-zA-Z0-9%-_]+#[a-zA-Z0-9%-_]+#[a-zA-Z0-9%-_]+$")) then + return value + end + + return nil, translate("NFTset name format error, format: [#[4|6]:[family#table#set]]") +end + +o = s:taboption("forwarding", FileUpload, "forwarding_domain_set_file", translate("Domain List File"), + translate("Upload domain list file, or configure auto download from Download File Setting page.")) +o.rmempty = true +o.datatype = "file" +o.rempty = true +o.editable = true +o.root_directory = "/etc/smartdns/domain-set" + +o = s:taboption("forwarding", TextValue, "domain_forwarding_list", + translate("Domain List"), translate("Configure forwarding domain name list.")) +o.rows = 10 +o.cols = 64 +o.monospace = true +function o.cfgvalue(self, section) + return nixio.fs.readfile("/etc/smartdns/domain-forwarding.list") +end +function o.write(self, section, value) + value = value:gsub("\r\n?", "\n") + nixio.fs.writefile("/etc/smartdns/domain-forwarding.list", value) +end + +---- domain block; +o = s:taboption("block", FileUpload, "block_domain_set_file", translate("Domain List File"), translate("Upload domain list file.")) +o.rmempty = true +o.datatype = "file" +o.rempty = true +o.editable = true +o.root_directory = "/etc/smartdns/domain-set" + +o = s:taboption("block", TextValue, "domain_block_list", + translate("Domain List"), translate("Configure block domain list.")) +o.rows = 10 +o.cols = 64 +function o.cfgvalue(self, section) + return nixio.fs.readfile("/etc/smartdns/domain-block.list") +end +function o.write(self, section, value) + value = value:gsub("\r\n?", "\n") + nixio.fs.writefile("/etc/smartdns/domain-block.list", value) +end -- Doman addresss addr = s:taboption("domain-address", Value, "address", @@ -345,6 +470,77 @@ function addr.write(self, section, value) nixio.fs.writefile("/etc/smartdns/blacklist-ip.conf", value) end +s = m:section(TypedSection, "smartdns", translate("Download Files Setting"), translate("Download domain list files for domain-rule and include config files, please refresh the page after download to take effect.")) +s.anonymous = true + +---- download Files Settings +o = s:option(Flag, "enable_auto_update", translate("Enable Auto Update"), translate("Enable daily auto update.")) +o.rmempty = false +o.default = o.disabled +o.rempty = true + +o = s:option(FileUpload, "upload_conf_file", translate("Upload Config File"), + translate("Upload smartdns config file to /etc/smartdns/conf.d")) +o.rmempty = true +o.datatype = "file" +o.rempty = true +o.editable = true +o.root_directory = "/etc/smartdns/conf.d" + +o = s:option(FileUpload, "upload_list_file", translate("Upload Domain List File"), + translate("Upload domain list file to /etc/smartdns/domain-set")) +o.rmempty = true +o.datatype = "file" +o.rempty = true +o.editable = true +o.root_directory = "/etc/smartdns/domain-set" + +o = s:option(Button, "_updateate") +o.title = translate("Update Files") +o.inputtitle = translate("update domain list files") +o.inputstyle = "apply" +o.write = function() + luci.sys.call("/etc/init.d/smartdns updatefiles >/dev/null 2>&1") +end + +s = m:section(TypedSection, "download-file", translate("Download Files"), translate("List of files to download.")) +s.anonymous = true +s.addremove = true +s.template = "cbi/tblsection" + +o = s:option(Value, 'name', translate('File Name'), translate('File Name')) +o.rmempty = false +o.datatype = 'string' + +o = s:option(Value, 'url', translate('URL'), translate('URL')) +o.rmempty = false +o.datatype = 'string' +function o.validate(self, value, section) + if value == "" then + return nil + end + + if value.find(value, "http://") then + return value + end + + if value.find(value, "https://") then + return value + end + + return nil, translate("URL format error, format: http:// or https://") +end + +o = s:option(ListValue, "type", translate("type"), translate("File Type")) +o:value("list", translate("domain list (/etc/smartdns/domain-set)")) +o:value("config", translate("smartdns config (/etc/smartdns/conf.d)")) +o.default = "list" +o.rempty = false + +o = s:option(Value, 'desc', translate('Description'), translate('Description')) +o.rmempty = true +o.datatype = 'string' + -- Technical Support s = m:section(TypedSection, "smartdns", translate("Technical Support"), translate("If you like this software, please buy me a cup of coffee.")) @@ -366,5 +562,13 @@ o.write = function() luci.http.redirect("https://pymumu.github.io/smartdns/#donate") end +o = s:option(Button, "Restart") +o.title = translate("Restart smartdns") +o.inputtitle = translate("Restart") +o.inputstyle = "apply" +o.write = function() + luci.sys.call("/etc/init.d/smartdns restart >/dev/null 2>&1") +end + return m diff --git a/package/luci-compat/files/luci/model/cbi/smartdns/upstream.lua b/package/luci-compat/files/luci/model/cbi/smartdns/upstream.lua index 1bb612f..49faf11 100644 --- a/package/luci-compat/files/luci/model/cbi/smartdns/upstream.lua +++ b/package/luci-compat/files/luci/model/cbi/smartdns/upstream.lua @@ -62,6 +62,13 @@ o.placeholder = "default" o.datatype = "hostname" o.rempty = true +---- exclude default group +o = s:option(Flag, "exclude_default_group", translate("Exclude Default Group"), translate("Exclude DNS Server from default group.")) +o.rmempty = false +o.default = o.disabled +o.editable = true +o.modalonly = true + ---- blacklist_ip o = s:option(Flag, "blacklist_ip", translate("IP Blacklist Filtering"), translate("Filtering IP with blacklist")) o.rmempty = false diff --git a/package/luci-compat/files/usr/share/rpcd/acl.d/luci-app-smartdns.json b/package/luci-compat/files/usr/share/rpcd/acl.d/luci-app-smartdns.json new file mode 100644 index 0000000..8c4cd01 --- /dev/null +++ b/package/luci-compat/files/usr/share/rpcd/acl.d/luci-app-smartdns.json @@ -0,0 +1,22 @@ +{ + "luci-app-smartdns": { + "description": "Grant access to LuCI app smartdns", + "read": { + "file": { + "/etc/smartdns/*": [ "read" ] + }, + "ubus": { + "service": [ "list" ] + }, + "uci": [ "smartdns" ] + }, + "write": { + "file": { + "/etc/smartdns/*": [ "write" ], + "/etc/init.d/smartdns restart": [ "exec" ], + "/etc/init.d/smartdns updatefiles": [ "exec" ] + }, + "uci": [ "smartdns" ] + } + } +} diff --git a/package/luci-compat/make.sh b/package/luci-compat/make.sh index 1cbce90..3b87850 100755 --- a/package/luci-compat/make.sh +++ b/package/luci-compat/make.sh @@ -54,6 +54,7 @@ build() build_tool mkdir $ROOT/root/usr/lib/lua/ -p cp $ROOT/files/luci $ROOT/root/usr/lib/lua/ -af + cp $ROOT/files/usr $ROOT/root/ -af #Generate Language $PO2LMO $ROOT/files/luci/i18n/smartdns.zh-cn.po $ROOT/root/usr/lib/lua/luci/i18n/smartdns.zh-cn.lmo