diff --git a/package/gluon-config-mode-autoupdater/luasrc/lib/gluon/config-mode/wizard/0050-autoupdater-info.lua b/package/gluon-config-mode-autoupdater/luasrc/lib/gluon/config-mode/wizard/0050-autoupdater-info.lua index 37b02f64..856938f1 100644 --- a/package/gluon-config-mode-autoupdater/luasrc/lib/gluon/config-mode/wizard/0050-autoupdater-info.lua +++ b/package/gluon-config-mode-autoupdater/luasrc/lib/gluon/config-mode/wizard/0050-autoupdater-info.lua @@ -1,14 +1,16 @@ return function(form, uci) + local pkg_i18n = i18n 'gluon-config-mode-autoupdater' + local enabled = uci:get_bool("autoupdater", "settings", "enabled") if enabled then form:section( Section, nil, - translate('This node will automatically update its firmware when a new version is available.') + pkg_i18n.translate('This node will automatically update its firmware when a new version is available.') ) else form:section( Section, nil, - translate('Automatic updates are disabled. They can be enabled in Advanced settings.') + pkg_i18n.translate('Automatic updates are disabled. They can be enabled in Advanced settings.') ) end end diff --git a/package/gluon-config-mode-contact-info/luasrc/lib/gluon/config-mode/wizard/0500-contact-info.lua b/package/gluon-config-mode-contact-info/luasrc/lib/gluon/config-mode/wizard/0500-contact-info.lua index 4a3f3e81..8314dd7e 100644 --- a/package/gluon-config-mode-contact-info/luasrc/lib/gluon/config-mode/wizard/0500-contact-info.lua +++ b/package/gluon-config-mode-contact-info/luasrc/lib/gluon/config-mode/wizard/0500-contact-info.lua @@ -1,16 +1,18 @@ return function(form, uci) + local pkg_i18n = i18n 'gluon-config-mode-contact-info' + local site = require 'gluon.site' local owner = uci:get_first("gluon-node-info", "owner") - local s = form:section(Section, nil, translate( + local s = form:section(Section, nil, pkg_i18n.translate( 'Please provide your contact information here to ' .. 'allow others to contact you. Note that ' .. 'this information will be visible publicly ' .. 'on the internet together with your node\'s coordinates.' )) - local o = s:option(Value, "contact", translate("Contact info"), translate("e.g. E-mail or phone number")) + local o = s:option(Value, "contact", pkg_i18n.translate("Contact info"), pkg_i18n.translate("e.g. E-mail or phone number")) o.default = uci:get("gluon-node-info", owner, "contact") o.optional = not site.config_mode.owner.obligatory(false) -- without a minimal length, an empty string will be accepted even with "optional = false" diff --git a/package/gluon-config-mode-core/files/lib/gluon/web/view/gluon/config-mode/welcome.html b/package/gluon-config-mode-core/files/lib/gluon/web/view/gluon/config-mode/welcome.html index 22305131..00beaa04 100644 --- a/package/gluon-config-mode-core/files/lib/gluon/web/view/gluon/config-mode/welcome.html +++ b/package/gluon-config-mode-core/files/lib/gluon/web/view/gluon/config-mode/welcome.html @@ -1,7 +1,9 @@ <%- + local site_i18n = i18n 'gluon-site' + local sysconfig = require 'gluon.sysconfig' - local msg = _translate('gluon-config-mode:welcome') + local msg = site_i18n._translate('gluon-config-mode:welcome') if not msg then return end -%>

diff --git a/package/gluon-config-mode-core/luasrc/lib/gluon/config-mode/reboot/0900-msg-reboot.lua b/package/gluon-config-mode-core/luasrc/lib/gluon/config-mode/reboot/0900-msg-reboot.lua index 8cbba849..d73a9e0b 100644 --- a/package/gluon-config-mode-core/luasrc/lib/gluon/config-mode/reboot/0900-msg-reboot.lua +++ b/package/gluon-config-mode-core/luasrc/lib/gluon/config-mode/reboot/0900-msg-reboot.lua @@ -1,3 +1,5 @@ +local site_i18n = i18n 'gluon-site' + local site = require 'gluon.site' local sysconfig = require 'gluon.sysconfig' local pretty_hostname = require 'pretty_hostname' @@ -7,7 +9,7 @@ local uci = require("simple-uci").cursor() local hostname = pretty_hostname.get(uci) local contact = uci:get_first('gluon-node-info', 'owner', 'contact') -local msg = _translate('gluon-config-mode:reboot') +local msg = site_i18n._translate('gluon-config-mode:reboot') if not msg then return end renderer.render_string(msg, { diff --git a/package/gluon-config-mode-core/luasrc/lib/gluon/web/controller/gluon-config-mode/index.lua b/package/gluon-config-mode-core/luasrc/lib/gluon/web/controller/gluon-config-mode/index.lua index 52117ac8..8801948a 100644 --- a/package/gluon-config-mode-core/luasrc/lib/gluon/web/controller/gluon-config-mode/index.lua +++ b/package/gluon-config-mode-core/luasrc/lib/gluon/web/controller/gluon-config-mode/index.lua @@ -1,2 +1,4 @@ +package 'gluon-config-mode-core' + entry({}, alias("wizard")) entry({"wizard"}, model("gluon-config-mode/wizard"), _("Wizard"), 5) diff --git a/package/gluon-config-mode-core/luasrc/lib/gluon/web/model/gluon-config-mode/wizard.lua b/package/gluon-config-mode-core/luasrc/lib/gluon/web/model/gluon-config-mode/wizard.lua index 35b605d2..888a7615 100644 --- a/package/gluon-config-mode-core/luasrc/lib/gluon/web/model/gluon-config-mode/wizard.lua +++ b/package/gluon-config-mode-core/luasrc/lib/gluon/web/model/gluon-config-mode/wizard.lua @@ -26,6 +26,7 @@ f.reset = false local s = f:section(Section) s.template = "gluon/config-mode/welcome" +s.package = "gluon-config-mode-core" local commit = {'gluon-setup-mode'} local run = {} @@ -57,6 +58,7 @@ function f:write() end f.template = "gluon/config-mode/reboot" + f.package = "gluon-config-mode-core" f.hidenav = true if nixio.fork() == 0 then diff --git a/package/gluon-config-mode-domain-select/luasrc/lib/gluon/config-mode/wizard/0200-domain-select.lua b/package/gluon-config-mode-domain-select/luasrc/lib/gluon/config-mode/wizard/0200-domain-select.lua index f82f1fb6..8046e006 100644 --- a/package/gluon-config-mode-domain-select/luasrc/lib/gluon/config-mode/wizard/0200-domain-select.lua +++ b/package/gluon-config-mode-domain-select/luasrc/lib/gluon/config-mode/wizard/0200-domain-select.lua @@ -1,4 +1,6 @@ return function(form, uci) + local site_i18n = i18n 'gluon-site' + local fs = require 'nixio.fs' local json = require 'jsonc' local site = require 'gluon.site' @@ -24,8 +26,8 @@ return function(form, uci) return list end - local s = form:section(Section, nil, translate('gluon-config-mode:domain-select')) - local o = s:option(ListValue, 'domain', translate('gluon-config-mode:domain')) + local s = form:section(Section, nil, site_i18n.translate('gluon-config-mode:domain-select')) + local o = s:option(ListValue, 'domain', site_i18n.translate('gluon-config-mode:domain')) if configured then o.default = selected_domain diff --git a/package/gluon-config-mode-geo-location/luasrc/lib/gluon/config-mode/wizard/0400-geo-location.lua b/package/gluon-config-mode-geo-location/luasrc/lib/gluon/config-mode/wizard/0400-geo-location.lua index 41b346d8..05d9b686 100644 --- a/package/gluon-config-mode-geo-location/luasrc/lib/gluon/config-mode/wizard/0400-geo-location.lua +++ b/package/gluon-config-mode-geo-location/luasrc/lib/gluon/config-mode/wizard/0400-geo-location.lua @@ -1,4 +1,7 @@ return function(form, uci) + local pkg_i18n = i18n 'gluon-config-mode-geo-location' + local site_i18n = i18n 'gluon-site' + local site = require 'gluon.site' local location = uci:get_first("gluon-node-info", "location") @@ -11,25 +14,25 @@ return function(form, uci) return uci:get_bool("gluon-node-info", location, "altitude") end - local text = translate( + local text = pkg_i18n.translate( 'If you want the location of your node to ' .. 'be displayed on the map, you can enter its coordinates here.' ) if show_altitude() then - text = text .. ' ' .. translate("gluon-config-mode:altitude-help") + text = text .. ' ' .. site_i18n.translate("gluon-config-mode:altitude-help") end local s = form:section(Section, nil, text) local o - local share_location = s:option(Flag, "location", translate("Show node on the map")) + local share_location = s:option(Flag, "location", pkg_i18n.translate("Show node on the map")) share_location.default = uci:get_bool("gluon-node-info", location, "share_location") function share_location:write(data) uci:set("gluon-node-info", location, "share_location", data) end - o = s:option(Value, "latitude", translate("Latitude"), translatef("e.g. %s", "53.873621")) + o = s:option(Value, "latitude", pkg_i18n.translate("Latitude"), pkg_i18n.translatef("e.g. %s", "53.873621")) o.default = uci:get("gluon-node-info", location, "latitude") o:depends(share_location, true) o.datatype = "float" @@ -37,7 +40,7 @@ return function(form, uci) uci:set("gluon-node-info", location, "latitude", data) end - o = s:option(Value, "longitude", translate("Longitude"), translatef("e.g. %s", "10.689901")) + o = s:option(Value, "longitude", pkg_i18n.translate("Longitude"), pkg_i18n.translatef("e.g. %s", "10.689901")) o.default = uci:get("gluon-node-info", location, "longitude") o:depends(share_location, true) o.datatype = "float" @@ -46,7 +49,7 @@ return function(form, uci) end if show_altitude() then - o = s:option(Value, "altitude", translate("gluon-config-mode:altitude-label"), translatef("e.g. %s", "11.51")) + o = s:option(Value, "altitude", site_i18n.translate("gluon-config-mode:altitude-label"), pkg_i18n.translatef("e.g. %s", "11.51")) o.default = uci:get("gluon-node-info", location, "altitude") o:depends(share_location, true) o.datatype = "float" diff --git a/package/gluon-config-mode-hostname/luasrc/lib/gluon/config-mode/wizard/0100-hostname.lua b/package/gluon-config-mode-hostname/luasrc/lib/gluon/config-mode/wizard/0100-hostname.lua index 8661d4bd..793c497e 100644 --- a/package/gluon-config-mode-hostname/luasrc/lib/gluon/config-mode/wizard/0100-hostname.lua +++ b/package/gluon-config-mode-hostname/luasrc/lib/gluon/config-mode/wizard/0100-hostname.lua @@ -1,8 +1,10 @@ return function(form, uci) + local pkg_i18n = i18n 'gluon-config-mode-hostname' + local pretty_hostname = require "pretty_hostname" local s = form:section(Section) - local o = s:option(Value, "hostname", translate("Node name")) + local o = s:option(Value, "hostname", pkg_i18n.translate("Node name")) o.default = pretty_hostname.get(uci) function o:write(data) diff --git a/package/gluon-config-mode-mesh-vpn/luasrc/lib/gluon/config-mode/reboot/0100-mesh-vpn.lua b/package/gluon-config-mode-mesh-vpn/luasrc/lib/gluon/config-mode/reboot/0100-mesh-vpn.lua index fc3f8123..8a72bffe 100644 --- a/package/gluon-config-mode-mesh-vpn/luasrc/lib/gluon/config-mode/reboot/0100-mesh-vpn.lua +++ b/package/gluon-config-mode-mesh-vpn/luasrc/lib/gluon/config-mode/reboot/0100-mesh-vpn.lua @@ -1,3 +1,5 @@ +local site_i18n = i18n 'gluon-site' + local uci = require("simple-uci").cursor() local lutil = require "gluon.web.util" local fs = require "nixio.fs" @@ -23,15 +25,15 @@ local msg if has_tunneldigger then local tunneldigger_enabled = uci:get_bool("tunneldigger", "mesh_vpn", "enabled") if not tunneldigger_enabled then - msg = _translate('gluon-config-mode:novpn') + msg = site_i18n._translate('gluon-config-mode:novpn') end elseif has_fastd then local fastd_enabled = uci:get_bool("fastd", "mesh_vpn", "enabled") if fastd_enabled then pubkey = util.trim(lutil.exec("/etc/init.d/fastd show_key mesh_vpn")) - msg = _translate('gluon-config-mode:pubkey') + msg = site_i18n._translate('gluon-config-mode:pubkey') else - msg = _translate('gluon-config-mode:novpn') + msg = site_i18n._translate('gluon-config-mode:novpn') end end diff --git a/package/gluon-config-mode-mesh-vpn/luasrc/lib/gluon/config-mode/wizard/0300-mesh-vpn.lua b/package/gluon-config-mode-mesh-vpn/luasrc/lib/gluon/config-mode/wizard/0300-mesh-vpn.lua index 69a64bd0..1ad50794 100644 --- a/package/gluon-config-mode-mesh-vpn/luasrc/lib/gluon/config-mode/wizard/0300-mesh-vpn.lua +++ b/package/gluon-config-mode-mesh-vpn/luasrc/lib/gluon/config-mode/wizard/0300-mesh-vpn.lua @@ -8,7 +8,9 @@ return function(form, uci) return end - local msg = translate( + local pkg_i18n = i18n 'gluon-config-mode-mesh-vpn' + + local msg = pkg_i18n.translate( 'Your internet connection can be used to establish a ' .. 'VPN connection with other nodes. ' .. 'Enable this option if there are no other nodes reachable ' .. @@ -21,7 +23,7 @@ return function(form, uci) local o - local meshvpn = s:option(Flag, "meshvpn", translate("Use internet connection (mesh VPN)")) + local meshvpn = s:option(Flag, "meshvpn", pkg_i18n.translate("Use internet connection (mesh VPN)")) meshvpn.default = uci:get_bool("fastd", "mesh_vpn", "enabled") or uci:get_bool("tunneldigger", "mesh_vpn", "enabled") function meshvpn:write(data) if has_fastd then @@ -32,7 +34,7 @@ return function(form, uci) end end - local limit = s:option(Flag, "limit_enabled", translate("Limit bandwidth")) + local limit = s:option(Flag, "limit_enabled", pkg_i18n.translate("Limit bandwidth")) limit:depends(meshvpn, true) limit.default = uci:get_bool("simple-tc", "mesh_vpn", "enabled") function limit:write(data) @@ -41,7 +43,7 @@ return function(form, uci) uci:set("simple-tc", "mesh_vpn", "ifname", "mesh-vpn") end - o = s:option(Value, "limit_ingress", translate("Downstream (kbit/s)")) + o = s:option(Value, "limit_ingress", pkg_i18n.translate("Downstream (kbit/s)")) o:depends(limit, true) o.default = uci:get("simple-tc", "mesh_vpn", "limit_ingress") o.datatype = "uinteger" @@ -49,7 +51,7 @@ return function(form, uci) uci:set("simple-tc", "mesh_vpn", "limit_ingress", data) end - o = s:option(Value, "limit_egress", translate("Upstream (kbit/s)")) + o = s:option(Value, "limit_egress", pkg_i18n.translate("Upstream (kbit/s)")) o:depends(limit, true) o.default = uci:get("simple-tc", "mesh_vpn", "limit_egress") o.datatype = "uinteger" diff --git a/package/gluon-web-admin/luasrc/lib/gluon/web/controller/admin/index.lua b/package/gluon-web-admin/luasrc/lib/gluon/web/controller/admin/index.lua index 109f009a..99025c95 100644 --- a/package/gluon-web-admin/luasrc/lib/gluon/web/controller/admin/index.lua +++ b/package/gluon-web-admin/luasrc/lib/gluon/web/controller/admin/index.lua @@ -1,3 +1,6 @@ +package 'gluon-web-admin' + + local root = node() if not root.target then root.target = alias("admin") diff --git a/package/gluon-web-admin/luasrc/lib/gluon/web/controller/admin/upgrade.lua b/package/gluon-web-admin/luasrc/lib/gluon/web/controller/admin/upgrade.lua index a41483d0..98562005 100644 --- a/package/gluon-web-admin/luasrc/lib/gluon/web/controller/admin/upgrade.lua +++ b/package/gluon-web-admin/luasrc/lib/gluon/web/controller/admin/upgrade.lua @@ -9,6 +9,9 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 ]]-- +package 'gluon-web-admin' + + local fs = require 'nixio.fs' local tmpfile = "/tmp/firmware.img" @@ -106,17 +109,23 @@ local function action_upgrade(http, renderer) renderer.render("layout", { content = "admin/upgrade", - bad_image = has_image and not has_support, + env = { + bad_image = has_image and not has_support, + }, + pkg = 'gluon-web-admin', }) -- Step 2: present uploaded file, show checksum, confirmation elseif step == 2 then renderer.render("layout", { content = "admin/upgrade_confirm", - checksum = image_checksum(tmpfile), - filesize = fs.stat(tmpfile).size, - flashsize = storage_size(), - keepconfig = (http:formvalue("keepcfg") == "1"), + env = { + checksum = image_checksum(tmpfile), + filesize = fs.stat(tmpfile).size, + flashsize = storage_size(), + keepconfig = (http:formvalue("keepcfg") == "1"), + }, + pkg = 'gluon-web-admin', }) elseif step == 3 then if http:formvalue("keepcfg") == "1" then @@ -127,6 +136,7 @@ local function action_upgrade(http, renderer) renderer.render("layout", { content = "admin/upgrade_reboot", hidenav = true, + pkg = 'gluon-web-admin', }) end end diff --git a/package/gluon-web-autoupdater/luasrc/lib/gluon/web/controller/admin/autoupdater.lua b/package/gluon-web-autoupdater/luasrc/lib/gluon/web/controller/admin/autoupdater.lua index abf2b782..7071039d 100644 --- a/package/gluon-web-autoupdater/luasrc/lib/gluon/web/controller/admin/autoupdater.lua +++ b/package/gluon-web-autoupdater/luasrc/lib/gluon/web/controller/admin/autoupdater.lua @@ -1 +1,3 @@ +package 'gluon-web-autoupdater' + entry({"admin", "autoupdater"}, model("admin/autoupdater"), _("Automatic updates"), 80) diff --git a/package/gluon-web-logging/luasrc/lib/gluon/web/controller/admin/logging.lua b/package/gluon-web-logging/luasrc/lib/gluon/web/controller/admin/logging.lua index 8dea6228..5704cdac 100644 --- a/package/gluon-web-logging/luasrc/lib/gluon/web/controller/admin/logging.lua +++ b/package/gluon-web-logging/luasrc/lib/gluon/web/controller/admin/logging.lua @@ -1 +1,3 @@ +package 'gluon-web-logging' + entry({"admin", "logging"}, model("admin/logging"), _("Logging"), 85) diff --git a/package/gluon-web-mesh-vpn-fastd/Makefile b/package/gluon-web-mesh-vpn-fastd/Makefile index bdc1828f..7ab759f6 100644 --- a/package/gluon-web-mesh-vpn-fastd/Makefile +++ b/package/gluon-web-mesh-vpn-fastd/Makefile @@ -26,14 +26,14 @@ define Build/Configure endef define Build/Compile - $(call GluonBuildI18N,gluon-mesh-vpn-fastd,i18n) + $(call GluonBuildI18N,gluon-web-mesh-vpn-fastd,i18n) $(call GluonSrcDiet,./luasrc,$(PKG_BUILD_DIR)/luadest/) endef define Package/gluon-web-mesh-vpn-fastd/install $(CP) ./files/* $(1)/ $(CP) $(PKG_BUILD_DIR)/luadest/* $(1)/ - $(call GluonInstallI18N,gluon-mesh-vpn-fastd,$(1)) + $(call GluonInstallI18N,gluon-web-mesh-vpn-fastd,$(1)) endef define Package/gluon-web-mesh-vpn-fastd/postinst diff --git a/package/gluon-web-mesh-vpn-fastd/luasrc/lib/gluon/web/controller/admin/mesh_vpn_fastd.lua b/package/gluon-web-mesh-vpn-fastd/luasrc/lib/gluon/web/controller/admin/mesh_vpn_fastd.lua index 65291fac..1b365956 100644 --- a/package/gluon-web-mesh-vpn-fastd/luasrc/lib/gluon/web/controller/admin/mesh_vpn_fastd.lua +++ b/package/gluon-web-mesh-vpn-fastd/luasrc/lib/gluon/web/controller/admin/mesh_vpn_fastd.lua @@ -1 +1,3 @@ +package 'gluon-web-mesh-vpn-fastd' + entry({"admin", "mesh_vpn_fastd"}, model("admin/mesh_vpn_fastd"), _("Mesh VPN"), 50) diff --git a/package/gluon-web-mesh-vpn-fastd/luasrc/lib/gluon/web/model/admin/mesh_vpn_fastd.lua b/package/gluon-web-mesh-vpn-fastd/luasrc/lib/gluon/web/model/admin/mesh_vpn_fastd.lua index d10bf178..d54b3227 100644 --- a/package/gluon-web-mesh-vpn-fastd/luasrc/lib/gluon/web/model/admin/mesh_vpn_fastd.lua +++ b/package/gluon-web-mesh-vpn-fastd/luasrc/lib/gluon/web/model/admin/mesh_vpn_fastd.lua @@ -6,6 +6,7 @@ local f = Form(translate('Mesh VPN')) local s = f:section(Section) local mode = s:option(Value, 'mode') +mode.package = "gluon-web-mesh-vpn-fastd" mode.template = "gluon/model/mesh-vpn-fastd" local methods = uci:get('fastd', 'mesh_vpn', 'method') diff --git a/package/gluon-web-network/luasrc/lib/gluon/web/controller/admin/network.lua b/package/gluon-web-network/luasrc/lib/gluon/web/controller/admin/network.lua index f490f78e..6d14dc7a 100644 --- a/package/gluon-web-network/luasrc/lib/gluon/web/controller/admin/network.lua +++ b/package/gluon-web-network/luasrc/lib/gluon/web/controller/admin/network.lua @@ -1 +1,3 @@ +package 'gluon-web-network' + entry({"admin", "network"}, model("admin/network"), _("Network"), 40) diff --git a/package/gluon-web-node-role/luasrc/lib/gluon/web/controller/admin/noderole.lua b/package/gluon-web-node-role/luasrc/lib/gluon/web/controller/admin/noderole.lua index c35935d4..b11dc50f 100644 --- a/package/gluon-web-node-role/luasrc/lib/gluon/web/controller/admin/noderole.lua +++ b/package/gluon-web-node-role/luasrc/lib/gluon/web/controller/admin/noderole.lua @@ -1 +1,3 @@ +package 'gluon-web-node-role' + entry({"admin", "noderole"}, model("admin/noderole"), "Node role", 60) diff --git a/package/gluon-web-private-wifi/luasrc/lib/gluon/web/controller/admin/privatewifi.lua b/package/gluon-web-private-wifi/luasrc/lib/gluon/web/controller/admin/privatewifi.lua index 6faeea02..febfb4e4 100644 --- a/package/gluon-web-private-wifi/luasrc/lib/gluon/web/controller/admin/privatewifi.lua +++ b/package/gluon-web-private-wifi/luasrc/lib/gluon/web/controller/admin/privatewifi.lua @@ -1 +1,3 @@ +package 'gluon-web-private-wifi' + entry({"admin", "privatewifi"}, model("admin/privatewifi"), _("Private WLAN"), 30) diff --git a/package/gluon-web-theme/files/lib/gluon/web/view/themes/gluon/layout.html b/package/gluon-web-theme/files/lib/gluon/web/view/themes/gluon/layout.html index f5da5d44..c1915f48 100644 --- a/package/gluon-web-theme/files/lib/gluon/web/view/themes/gluon/layout.html +++ b/package/gluon-web-theme/files/lib/gluon/web/view/themes/gluon/layout.html @@ -33,6 +33,10 @@ You may obtain a copy of the License at return r end + local function title(node) + return i18n(node.pkg).translate(node.title) + end + local function subtree(prefix, node, name, ...) if not node then return end @@ -48,7 +52,7 @@ You may obtain a copy of the License at local active = (v == name) %>

  • - <%=pcdata(translate(child.title))%> + <%=pcdata(title(child))%>
  • <% end @@ -71,7 +75,7 @@ You may obtain a copy of the License at - <%=pcdata( hostname .. ( (rnode and rnode.title) and ' - ' .. translate(rnode.title) or '')) %> + <%=pcdata( hostname .. ((rnode and rnode.title) and ' - ' .. title(rnode) or '')) %> @@ -88,7 +92,7 @@ You may obtain a copy of the License at <% if #categories > 1 and not hidenav then %> <% end %> @@ -110,9 +114,9 @@ You may obtain a copy of the License at <% - ok, err = pcall(include, content) + ok, err = pcall(renderer.render, content, env, pkg) if not ok then - renderer.render('error500', {message = err}) + renderer.render('error500', {message = err}, 'gluon-web') end %> diff --git a/package/gluon-web-wifi-config/luasrc/lib/gluon/web/controller/admin/wifi-config.lua b/package/gluon-web-wifi-config/luasrc/lib/gluon/web/controller/admin/wifi-config.lua index bd76ba4a..4a0b5256 100644 --- a/package/gluon-web-wifi-config/luasrc/lib/gluon/web/controller/admin/wifi-config.lua +++ b/package/gluon-web-wifi-config/luasrc/lib/gluon/web/controller/admin/wifi-config.lua @@ -1 +1,3 @@ +package 'gluon-web-wifi-config' + entry({"admin", "wifi-config"}, model("admin/wifi-config"), _("WLAN"), 20) diff --git a/package/gluon-web/luasrc/usr/lib/lua/gluon/web/dispatcher.lua b/package/gluon-web/luasrc/usr/lib/lua/gluon/web/dispatcher.lua index 59337213..25404fa4 100644 --- a/package/gluon-web/luasrc/usr/lib/lua/gluon/web/dispatcher.lua +++ b/package/gluon-web/luasrc/usr/lib/lua/gluon/web/dispatcher.lua @@ -1,6 +1,6 @@ -- Copyright 2008 Steven Barth -- Copyright 2008-2015 Jo-Philipp Wich --- Copyright 2017 Matthias Schiffer +-- Copyright 2017-2018 Matthias Schiffer -- Licensed to the public under the Apache License 2.0. local fs = require "nixio.fs" @@ -77,7 +77,7 @@ local function set_language(renderer, accept) end for match in accept:gmatch("[^,]+") do - local lang = match:match('^%s*([^%s;-_]+)') + local lang = match:match('^%s*([^%s;_-]+)') local q = tonumber(match:match(';q=(%S+)%s*$') or 1) if lang == '*' then @@ -93,11 +93,7 @@ local function set_language(renderer, accept) return (weights[a] or 0) > (weights[b] or 0) end) - for _, lang in ipairs(langs) do - if renderer.setlanguage(lang) then - return - end - end + renderer.set_language(langs) end @@ -147,68 +143,6 @@ function dispatch(http, request) url = function(path) return build_url(http, path) end, }, { __index = _G })) - local subdisp = setmetatable({ - node = function(...) - return _node({...}) - end, - - entry = function(path, target, title, order) - local c = _node(path, true) - - c.target = target - c.title = title - c.order = order - - return c - end, - - alias = function(...) - local req = {...} - return function() - http:redirect(build_url(http, req)) - end - end, - - call = function(func, ...) - local args = {...} - return function() - func(http, renderer, unpack(args)) - end - end, - - template = function(view) - return function() - renderer.render("layout", {content = view}) - end - end, - - model = function(name) - return function() - local hidenav = false - - local model = require "gluon.web.model" - local maps = model.load(name, renderer) - - for _, map in ipairs(maps) do - map:parse(http) - end - for _, map in ipairs(maps) do - map:handle() - hidenav = hidenav or map.hidenav - end - - renderer.render("layout", { - content = "model/wrapper", - maps = maps, - hidenav = hidenav, - }) - end - end, - - _ = function(text) - return text - end, - }, { __index = _G }) local function createtree() local base = util.libpath() .. "/controller/" @@ -216,6 +150,80 @@ function dispatch(http, request) local function load_ctl(path) local ctl = assert(loadfile(path)) + local _pkg + + local subdisp = setmetatable({ + package = function(name) + _pkg = name + end, + + node = function(...) + return _node({...}) + end, + + entry = function(path, target, title, order) + local c = _node(path, true) + + c.target = target + c.title = title + c.order = order + c.pkg = _pkg + + return c + end, + + alias = function(...) + local req = {...} + return function() + http:redirect(build_url(http, req)) + end + end, + + call = function(func, ...) + local args = {...} + return function() + func(http, renderer, unpack(args)) + end + end, + + template = function(view) + local pkg = _pkg + return function() + renderer.render("layout", {content = view, pkg = pkg}) + end + end, + + model = function(name) + local pkg = _pkg + return function() + local hidenav = false + + local model = require "gluon.web.model" + local maps = model.load(name, renderer, pkg) + + for _, map in ipairs(maps) do + map:parse(http) + end + for _, map in ipairs(maps) do + map:handle() + hidenav = hidenav or map.hidenav + end + + renderer.render("layout", { + content = "model/wrapper", + env = { + maps = maps, + }, + hidenav = hidenav, + }) + end + end, + + _ = function(text) + return text + end, + }, { __index = _G }) + local env = setmetatable({}, { __index = subdisp }) setfenv(ctl, env) @@ -239,9 +247,14 @@ function dispatch(http, request) if not node or not node.target then http:status(404, "Not Found") - renderer.render("layout", { content = "error404", message = - "No page is registered at '/" .. table.concat(request, "/") .. "'.\n" .. - "If this URL belongs to an extension, make sure it is properly installed.\n" + renderer.render("layout", { + content = "error404", + env = { + message = + "No page is registered at '/" .. table.concat(request, "/") .. "'.\n" .. + "If this URL belongs to an extension, make sure it is properly installed.\n", + }, + pkg = 'gluon-web', }) return end @@ -251,9 +264,14 @@ function dispatch(http, request) local ok, err = pcall(node.target) if not ok then http:status(500, "Internal Server Error") - renderer.render("layout", { content = "error500", message = - "Failed to execute dispatcher target for entry '/" .. table.concat(request, "/") .. "'.\n" .. - "The called action terminated with an exception:\n" .. tostring(err or "(unknown)") + renderer.render("layout", { + content = "error500", + env = { + message = + "Failed to execute dispatcher target for entry '/" .. table.concat(request, "/") .. "'.\n" .. + "The called action terminated with an exception:\n" .. tostring(err or "(unknown)"), + }, + pkg = 'gluon-web', }) end end diff --git a/package/gluon-web/luasrc/usr/lib/lua/gluon/web/i18n.lua b/package/gluon-web/luasrc/usr/lib/lua/gluon/web/i18n.lua new file mode 100644 index 00000000..151a9c1c --- /dev/null +++ b/package/gluon-web/luasrc/usr/lib/lua/gluon/web/i18n.lua @@ -0,0 +1,54 @@ +-- Copyright 2018 Matthias Schiffer +-- Licensed to the public under the Apache License 2.0. + +local tparser = require "gluon.web.template.parser" +local util = require "gluon.web.util" +local fs = require "nixio.fs" + + +local i18ndir = util.libpath() .. "/i18n" + + +local function i18n_file(lang, pkg) + return string.format('%s/%s.%s.lmo', i18ndir, pkg, lang) +end + +local function no_translation(key) + return nil +end + +local function load_catalog(lang, pkg) + if pkg then + local file = i18n_file(lang, pkg) + local cat = fs.access(file) and tparser.load_catalog(file) + + if cat then return cat end + end + + return no_translation +end + + +module "gluon.web.i18n" + +function supported(lang) + return lang == 'en' or fs.access(i18n_file(lang, 'gluon-web')) +end + +function load(lang, pkg) + local _translate = load_catalog(lang, pkg) + + local function translate(key) + return _translate(key) or key + end + + local function translatef(key, ...) + return translate(key):format(...) + end + + return { + _translate = _translate, + translate = translate, + translatef = translatef, + } +end diff --git a/package/gluon-web/luasrc/usr/lib/lua/gluon/web/model.lua b/package/gluon-web/luasrc/usr/lib/lua/gluon/web/model.lua index ee841024..a9885ab3 100644 --- a/package/gluon-web/luasrc/usr/lib/lua/gluon/web/model.lua +++ b/package/gluon-web/luasrc/usr/lib/lua/gluon/web/model.lua @@ -4,11 +4,11 @@ module("gluon.web.model", package.seeall) -local util = require("gluon.web.util") +local util = require "gluon.web.util" -local fs = require("nixio.fs") -local datatypes = require("gluon.web.model.datatypes") -local dispatcher = require("gluon.web.dispatcher") +local fs = require "nixio.fs" +local datatypes = require "gluon.web.model.datatypes" +local dispatcher = require "gluon.web.dispatcher" local class = util.class local instanceof = util.instanceof @@ -17,7 +17,7 @@ FORM_VALID = 1 FORM_INVALID = -1 -- Loads a model from given file, creating an environment and returns it -function load(name, renderer) +function load(name, renderer, pkg) local modeldir = util.libpath() .. "/model/" if not fs.access(modeldir..name..".lua") then @@ -26,14 +26,16 @@ function load(name, renderer) local func = assert(loadfile(modeldir..name..".lua")) - local env = { - translate=renderer.translate, - translatef=renderer.translatef, - } + local i18n = setmetatable({ + i18n = renderer.i18n + }, { + __index = renderer.i18n(pkg) + }) - setfenv(func, setmetatable(env, {__index = + + setfenv(func, setmetatable({}, {__index = function(tbl, key) - return _M[key] or _G[key] + return _M[key] or i18n[key] or _G[key] end })) @@ -85,6 +87,7 @@ function Node:__init__(title, description, name) self.name = name self.index = nil self.parent = nil + self.package = 'gluon-web' end function Node:append(obj) @@ -116,7 +119,7 @@ function Node:render(renderer, scope) id = self:id(), scope = scope, }, {__index = scope}) - renderer.render(self.template, env) + renderer.render(self.template, env, self.package) end end diff --git a/package/gluon-web/luasrc/usr/lib/lua/gluon/web/template.lua b/package/gluon-web/luasrc/usr/lib/lua/gluon/web/template.lua index 1a92e576..002b9152 100644 --- a/package/gluon-web/luasrc/usr/lib/lua/gluon/web/template.lua +++ b/package/gluon-web/luasrc/usr/lib/lua/gluon/web/template.lua @@ -1,39 +1,59 @@ -- Copyright 2008 Steven Barth --- Copyright 2017 Matthias Schiffer +-- Copyright 2017-2018 Matthias Schiffer -- Licensed to the public under the Apache License 2.0. local tparser = require "gluon.web.template.parser" +local i18n = require "gluon.web.i18n" local util = require "gluon.web.util" -local fs = require "nixio.fs" -local tostring, setmetatable, setfenv, pcall, assert = tostring, setmetatable, setfenv, pcall, assert +local tostring, ipairs, setmetatable, setfenv = tostring, ipairs, setmetatable, setfenv +local pcall, assert = pcall, assert module "gluon.web.template" local viewdir = util.libpath() .. "/view/" -local i18ndir = util.libpath() .. "/i18n/" function renderer(env) local ctx = {} + local language = 'en' + local catalogs = {} - local function render_template(name, template, scope) + function ctx.set_language(langs) + for _, lang in ipairs(langs) do + if i18n.supported(lang) then + language = lang + catalogs = {} + return + end + end + end + + function ctx.i18n(pkg) + local cat = catalogs[pkg] or i18n.load(language, pkg) + if pkg then catalogs[pkg] = cat end + return cat + end + + local function render_template(name, template, scope, pkg) scope = scope or {} + local t = ctx.i18n(pkg) local locals = { renderer = ctx, - translate = ctx.translate, - translatef = ctx.translatef, - _translate = ctx._translate, + i18n = ctx.i18n, + translate = t.translate, + translatef = t.translatef, + _translate = t._translate, include = function(name) - ctx.render(name, scope) + ctx.render(name, scope, pkg) end, } setfenv(template, setmetatable({}, { __index = function(tbl, key) - return scope[key] or env[key] or locals[key] + return scope[key] or locals[key] or env[key] end })) @@ -46,7 +66,7 @@ function renderer(env) --- Render a certain template. -- @param name Template name -- @param scope Scope to assign to template (optional) - function ctx.render(name, scope) + function ctx.render(name, scope, pkg) local sourcefile = viewdir .. name .. ".html" local template, _, err = tparser.parse(sourcefile) @@ -54,45 +74,19 @@ function renderer(env) "Error while parsing template '" .. sourcefile .. "':\n" .. (err or "Unknown syntax error")) - render_template(name, template, scope) + render_template(name, template, scope, pkg) end --- Render a template from a string. -- @param template Template string -- @param scope Scope to assign to template (optional) - function ctx.render_string(str, scope) + function ctx.render_string(str, scope, pkg) local template, _, err = tparser.parse_string(str) assert(template, "Error while parsing template:\n" .. (err or "Unknown syntax error")) - render_template('(local)', template, scope) - end - - function ctx.setlanguage(lang) - lang = lang:gsub("_", "-") - if not lang then return false end - - if lang ~= 'en' and not fs.access(i18ndir .. "gluon-web." .. lang .. ".lmo") then - return false - end - - return tparser.load_catalog(lang, i18ndir) - end - - -- Returns a translated string, or nil if none is found - function ctx._translate(key) - return (tparser.translate(key)) - end - - -- Returns a translated string, or the original string if none is found - function ctx.translate(key) - return tparser.translate(key) or key - end - - function ctx.translatef(key, ...) - local t = ctx.translate(key) - return t:format(...) + render_template('(local)', template, scope, pkg) end return ctx diff --git a/package/gluon-web/src/template_lmo.c b/package/gluon-web/src/template_lmo.c index 365b0e30..0a86bbc9 100644 --- a/package/gluon-web/src/template_lmo.c +++ b/package/gluon-web/src/template_lmo.c @@ -23,15 +23,12 @@ #include #include -#include #include -#include #include #include #include #include #include -#include struct lmo_entry { @@ -41,28 +38,6 @@ struct lmo_entry { uint32_t length; } __attribute__((packed)); -typedef struct lmo_entry lmo_entry_t; - - -struct lmo_archive { - size_t length; - const lmo_entry_t *index; - char *data; - const char *end; - struct lmo_archive *next; -}; - -typedef struct lmo_archive lmo_archive_t; - - -struct lmo_catalog { - char lang[6]; - struct lmo_archive *archives; - struct lmo_catalog *next; -}; - -typedef struct lmo_catalog lmo_catalog_t; - static inline uint16_t get_le16(const void *data) { const uint8_t *d = data; @@ -122,12 +97,13 @@ static uint32_t sfh_hash(const void *input, size_t len) return hash; } -static lmo_archive_t * lmo_open(const char *file) +bool lmo_load(lmo_catalog_t *cat, const char *file) { int fd = -1; - lmo_archive_t *ar = NULL; struct stat s; + cat->data = MAP_FAILED; + fd = open(file, O_RDONLY|O_CLOEXEC); if (fd < 0) goto err; @@ -135,111 +111,43 @@ static lmo_archive_t * lmo_open(const char *file) if (fstat(fd, &s)) goto err; - if ((ar = calloc(1, sizeof(*ar))) != NULL) { - ar->data = mmap(NULL, s.st_size, PROT_READ, MAP_SHARED, fd, 0); + cat->data = mmap(NULL, s.st_size, PROT_READ, MAP_SHARED, fd, 0); - close(fd); - fd = -1; + close(fd); + fd = -1; - if (ar->data == MAP_FAILED) - goto err; + if (cat->data == MAP_FAILED) + goto err; - ar->end = ar->data + s.st_size; + cat->end = cat->data + s.st_size; - uint32_t idx_offset = get_be32(ar->end - sizeof(uint32_t)); - ar->index = (const lmo_entry_t *)(ar->data + idx_offset); + uint32_t idx_offset = get_be32(cat->end - sizeof(uint32_t)); + cat->index = (const lmo_entry_t *)(cat->data + idx_offset); - if ((const char *)ar->index > (ar->end - sizeof(uint32_t))) - goto err; + if ((const char *)cat->index > (cat->end - sizeof(uint32_t))) + goto err; - ar->length = (ar->end - sizeof(uint32_t) - (const char *)ar->index) / sizeof(lmo_entry_t); + cat->length = (cat->end - sizeof(uint32_t) - (const char *)cat->index) / sizeof(lmo_entry_t); - return ar; - } + return true; err: if (fd >= 0) close(fd); - if (ar != NULL) { - if ((ar->data != NULL) && (ar->data != MAP_FAILED)) - munmap(ar->data, ar->end - ar->data); - - free(ar); - } - - return NULL; -} - - -static lmo_catalog_t *lmo_catalogs; -static lmo_catalog_t *lmo_active_catalog; - -bool lmo_change_catalog(const char *lang) -{ - lmo_catalog_t *cat; - - for (cat = lmo_catalogs; cat; cat = cat->next) { - if (!strncmp(cat->lang, lang, sizeof(cat->lang))) { - lmo_active_catalog = cat; - return true; - } - } + if (cat->data != MAP_FAILED) + munmap(cat->data, cat->end - cat->data); return false; } -bool lmo_load_catalog(const char *lang, const char *dir) +void lmo_unload(lmo_catalog_t *cat) { - DIR *dh = NULL; - char pattern[16]; - char path[PATH_MAX]; - struct dirent *de = NULL; - - lmo_archive_t *ar = NULL; - lmo_catalog_t *cat = NULL; - - if (lmo_change_catalog(lang)) - return true; - - if (!(dh = opendir(dir))) - goto err; - - if (!(cat = calloc(1, sizeof(*cat)))) - goto err; - - snprintf(cat->lang, sizeof(cat->lang), "%s", lang); - snprintf(pattern, sizeof(pattern), "*.%s.lmo", lang); - - while ((de = readdir(dh)) != NULL) { - if (!fnmatch(pattern, de->d_name, 0)) { - snprintf(path, sizeof(path), "%s/%s", dir, de->d_name); - ar = lmo_open(path); - - if (ar) { - ar->next = cat->archives; - cat->archives = ar; - } - } - } - - closedir(dh); - - cat->next = lmo_catalogs; - lmo_catalogs = cat; - - lmo_active_catalog = cat; - - return true; - -err: - if (dh) - closedir(dh); - free(cat); - - return false; + if (cat->data != MAP_FAILED) + munmap(cat->data, cat->end - cat->data); } + static int lmo_compare_entry(const void *a, const void *b) { const lmo_entry_t *ea = a, *eb = b; @@ -253,34 +161,26 @@ static int lmo_compare_entry(const void *a, const void *b) return 0; } -static const lmo_entry_t * lmo_find_entry(const lmo_archive_t *ar, uint32_t hash) +static const lmo_entry_t * lmo_find_entry(const lmo_catalog_t *cat, uint32_t hash) { lmo_entry_t key; key.key_id = htonl(hash); - return bsearch(&key, ar->index, ar->length, sizeof(lmo_entry_t), lmo_compare_entry); + return bsearch(&key, cat->index, cat->length, sizeof(lmo_entry_t), lmo_compare_entry); } -bool lmo_translate(const char *key, size_t keylen, char **out, size_t *outlen) +bool lmo_translate(const lmo_catalog_t *cat, const char *key, size_t keylen, const char **out, size_t *outlen) { - if (!lmo_active_catalog) + uint32_t hash = sfh_hash(key, keylen); + const lmo_entry_t *e = lmo_find_entry(cat, hash); + if (!e) return false; - uint32_t hash = sfh_hash(key, keylen); + *out = cat->data + ntohl(e->offset); + *outlen = ntohl(e->length); - for (const lmo_archive_t *ar = lmo_active_catalog->archives; ar; ar = ar->next) { - const lmo_entry_t *e = lmo_find_entry(ar, hash); - if (!e) - continue; + if (*out + *outlen > cat->end) + return false; - *out = ar->data + ntohl(e->offset); - *outlen = ntohl(e->length); - - if (*out + *outlen > ar->end) - continue; - - return true; - } - - return false; + return true; } diff --git a/package/gluon-web/src/template_lmo.h b/package/gluon-web/src/template_lmo.h index c5682509..4af6cac1 100644 --- a/package/gluon-web/src/template_lmo.h +++ b/package/gluon-web/src/template_lmo.h @@ -24,7 +24,21 @@ #include -bool lmo_load_catalog(const char *lang, const char *dir); -bool lmo_translate(const char *key, size_t keylen, char **out, size_t *outlen); +typedef struct lmo_entry lmo_entry_t; + + +struct lmo_catalog { + size_t length; + const lmo_entry_t *index; + char *data; + const char *end; +}; + +typedef struct lmo_catalog lmo_catalog_t; + + +bool lmo_load(lmo_catalog_t *cat, const char *file); +void lmo_unload(lmo_catalog_t *cat); +bool lmo_translate(const lmo_catalog_t *cat, const char *key, size_t keylen, const char **out, size_t *outlen); #endif diff --git a/package/gluon-web/src/template_lualib.c b/package/gluon-web/src/template_lualib.c index 4af2f777..308011f9 100644 --- a/package/gluon-web/src/template_lualib.c +++ b/package/gluon-web/src/template_lualib.c @@ -29,7 +29,7 @@ #include -#define TEMPLATE_LUALIB_META "gluon.web.template.parser" +#define TEMPLATE_CATALOG "gluon.web.template.parser.catalog" static int template_L_do_parse(lua_State *L, struct template_parser *parser, const char *chunkname) @@ -87,40 +87,64 @@ static int template_L_pcdata(lua_State *L) return 1; } -static int template_L_load_catalog(lua_State *L) { - const char *lang = luaL_optstring(L, 1, "en"); - const char *dir = luaL_checkstring(L, 2); - lua_pushboolean(L, lmo_load_catalog(lang, dir)); - return 1; -} +static int template_L_load_catalog(lua_State *L) +{ + const char *file = luaL_checkstring(L, 1); -static int template_L_translate(lua_State *L) { - size_t len; - char *tr; - size_t trlen; - const char *key = luaL_checklstring(L, 1, &len); + lmo_catalog_t *cat = lua_newuserdata(L, sizeof(*cat)); + if (!lmo_load(cat, file)) { + lua_pop(L, 1); + return 0; + } - if (lmo_translate(key, len, &tr, &trlen)) - lua_pushlstring(L, tr, trlen); - else - lua_pushnil(L); + luaL_getmetatable(L, TEMPLATE_CATALOG); + lua_setmetatable(L, -2); return 1; } +static int template_catalog_call(lua_State *L) +{ + size_t inlen, outlen; + lmo_catalog_t *cat = luaL_checkudata(L, 1, TEMPLATE_CATALOG); + const char *in = luaL_checklstring(L, 2, &inlen), *out; + if (!lmo_translate(cat, in, inlen, &out, &outlen)) + return 0; + + lua_pushlstring(L, out, outlen); + + return 1; +} + +static int template_catalog_gc(lua_State *L) +{ + lmo_catalog_t *cat = luaL_checkudata(L, 1, TEMPLATE_CATALOG); + lmo_unload(cat); + + return 0; +} -/* module table */ static const luaL_reg R[] = { { "parse", template_L_parse }, { "parse_string", template_L_parse_string }, { "pcdata", template_L_pcdata }, { "load_catalog", template_L_load_catalog }, - { "translate", template_L_translate }, + {} +}; + +static const luaL_reg template_catalog_methods[] = { + { "__call", template_catalog_call }, + { "__gc", template_catalog_gc }, {} }; __attribute__ ((visibility("default"))) LUALIB_API int luaopen_gluon_web_template_parser(lua_State *L) { - luaL_register(L, TEMPLATE_LUALIB_META, R); + luaL_register(L, "gluon.web.template.parser", R); + + luaL_newmetatable(L, TEMPLATE_CATALOG); + luaL_register(L, NULL, template_catalog_methods); + lua_pop(L, 1); + return 1; }