gluon-web: add i18n package namespaces
This commit is contained in:
		
							parent
							
								
									1a426c3bb9
								
							
						
					
					
						commit
						557565e189
					
				| @ -1,14 +1,16 @@ | |||||||
| return function(form, uci) | return function(form, uci) | ||||||
|  | 	local pkg_i18n = i18n 'gluon-config-mode-autoupdater' | ||||||
|  | 
 | ||||||
| 	local enabled = uci:get_bool("autoupdater", "settings", "enabled") | 	local enabled = uci:get_bool("autoupdater", "settings", "enabled") | ||||||
| 	if enabled then | 	if enabled then | ||||||
| 		form:section( | 		form:section( | ||||||
| 			Section, nil, | 			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 | 	else | ||||||
| 		form:section( | 		form:section( | ||||||
| 			Section, nil, | 			Section, nil, | ||||||
| 			translate('Automatic updates are disabled. They can be enabled in <em>Advanced settings</em>.') | 			pkg_i18n.translate('Automatic updates are disabled. They can be enabled in <em>Advanced settings</em>.') | ||||||
| 		) | 		) | ||||||
| 	end | 	end | ||||||
| end | end | ||||||
|  | |||||||
| @ -1,16 +1,18 @@ | |||||||
| return function(form, uci) | return function(form, uci) | ||||||
|  | 	local pkg_i18n = i18n 'gluon-config-mode-contact-info' | ||||||
|  | 
 | ||||||
| 	local site = require 'gluon.site' | 	local site = require 'gluon.site' | ||||||
| 
 | 
 | ||||||
| 	local owner = uci:get_first("gluon-node-info", "owner") | 	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 ' | 		'Please provide your contact information here to ' | ||||||
| 		.. 'allow others to contact you. Note that ' | 		.. 'allow others to contact you. Note that ' | ||||||
| 		.. 'this information will be visible <em>publicly</em> ' | 		.. 'this information will be visible <em>publicly</em> ' | ||||||
| 		.. 'on the internet together with your node\'s coordinates.' | 		.. '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.default = uci:get("gluon-node-info", owner, "contact") | ||||||
| 	o.optional = not site.config_mode.owner.obligatory(false) | 	o.optional = not site.config_mode.owner.obligatory(false) | ||||||
| 	-- without a minimal length, an empty string will be accepted even with "optional = false" | 	-- without a minimal length, an empty string will be accepted even with "optional = false" | ||||||
|  | |||||||
| @ -1,7 +1,9 @@ | |||||||
| <%- | <%- | ||||||
|  | 	local site_i18n = i18n 'gluon-site' | ||||||
|  | 
 | ||||||
| 	local sysconfig = require 'gluon.sysconfig' | 	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 | 	if not msg then return end | ||||||
| -%> | -%> | ||||||
| <p> | <p> | ||||||
|  | |||||||
| @ -1,3 +1,5 @@ | |||||||
|  | local site_i18n = i18n 'gluon-site' | ||||||
|  | 
 | ||||||
| local site = require 'gluon.site' | local site = require 'gluon.site' | ||||||
| local sysconfig = require 'gluon.sysconfig' | local sysconfig = require 'gluon.sysconfig' | ||||||
| local pretty_hostname = require 'pretty_hostname' | local pretty_hostname = require 'pretty_hostname' | ||||||
| @ -7,7 +9,7 @@ local uci = require("simple-uci").cursor() | |||||||
| local hostname = pretty_hostname.get(uci) | local hostname = pretty_hostname.get(uci) | ||||||
| local contact = uci:get_first('gluon-node-info', 'owner', 'contact') | 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 | if not msg then return end | ||||||
| 
 | 
 | ||||||
| renderer.render_string(msg, { | renderer.render_string(msg, { | ||||||
|  | |||||||
| @ -1,2 +1,4 @@ | |||||||
|  | package 'gluon-config-mode-core' | ||||||
|  | 
 | ||||||
| entry({}, alias("wizard")) | entry({}, alias("wizard")) | ||||||
| entry({"wizard"}, model("gluon-config-mode/wizard"), _("Wizard"), 5) | entry({"wizard"}, model("gluon-config-mode/wizard"), _("Wizard"), 5) | ||||||
|  | |||||||
| @ -26,6 +26,7 @@ f.reset = false | |||||||
| 
 | 
 | ||||||
| local s = f:section(Section) | local s = f:section(Section) | ||||||
| s.template = "gluon/config-mode/welcome" | s.template = "gluon/config-mode/welcome" | ||||||
|  | s.package = "gluon-config-mode-core" | ||||||
| 
 | 
 | ||||||
| local commit = {'gluon-setup-mode'} | local commit = {'gluon-setup-mode'} | ||||||
| local run = {} | local run = {} | ||||||
| @ -57,6 +58,7 @@ function f:write() | |||||||
| 	end | 	end | ||||||
| 
 | 
 | ||||||
| 	f.template = "gluon/config-mode/reboot" | 	f.template = "gluon/config-mode/reboot" | ||||||
|  | 	f.package = "gluon-config-mode-core" | ||||||
| 	f.hidenav = true | 	f.hidenav = true | ||||||
| 
 | 
 | ||||||
| 	if nixio.fork() == 0 then | 	if nixio.fork() == 0 then | ||||||
|  | |||||||
| @ -1,4 +1,6 @@ | |||||||
| return function(form, uci) | return function(form, uci) | ||||||
|  | 	local site_i18n = i18n 'gluon-site' | ||||||
|  | 
 | ||||||
| 	local fs = require 'nixio.fs' | 	local fs = require 'nixio.fs' | ||||||
| 	local json = require 'jsonc' | 	local json = require 'jsonc' | ||||||
| 	local site = require 'gluon.site' | 	local site = require 'gluon.site' | ||||||
| @ -24,8 +26,8 @@ return function(form, uci) | |||||||
| 		return list | 		return list | ||||||
| 	end | 	end | ||||||
| 
 | 
 | ||||||
| 	local s = form:section(Section, nil, translate('gluon-config-mode:domain-select')) | 	local s = form:section(Section, nil, site_i18n.translate('gluon-config-mode:domain-select')) | ||||||
| 	local o = s:option(ListValue, 'domain', translate('gluon-config-mode:domain')) | 	local o = s:option(ListValue, 'domain', site_i18n.translate('gluon-config-mode:domain')) | ||||||
| 
 | 
 | ||||||
| 	if configured then | 	if configured then | ||||||
| 		o.default = selected_domain | 		o.default = selected_domain | ||||||
|  | |||||||
| @ -1,4 +1,7 @@ | |||||||
| return function(form, uci) | 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 site = require 'gluon.site' | ||||||
| 
 | 
 | ||||||
| 	local location = uci:get_first("gluon-node-info", "location") | 	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") | 		return uci:get_bool("gluon-node-info", location, "altitude") | ||||||
| 	end | 	end | ||||||
| 
 | 
 | ||||||
| 	local text = translate( | 	local text = pkg_i18n.translate( | ||||||
| 		'If you want the location of your node to ' .. | 		'If you want the location of your node to ' .. | ||||||
| 		'be displayed on the map, you can enter its coordinates here.' | 		'be displayed on the map, you can enter its coordinates here.' | ||||||
| 	) | 	) | ||||||
| 	if show_altitude() then | 	if show_altitude() then | ||||||
| 		text = text .. ' ' .. translate("gluon-config-mode:altitude-help") | 		text = text .. ' ' .. site_i18n.translate("gluon-config-mode:altitude-help") | ||||||
| 	end | 	end | ||||||
| 
 | 
 | ||||||
| 	local s = form:section(Section, nil, text) | 	local s = form:section(Section, nil, text) | ||||||
| 
 | 
 | ||||||
| 	local o | 	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") | 	share_location.default = uci:get_bool("gluon-node-info", location, "share_location") | ||||||
| 	function share_location:write(data) | 	function share_location:write(data) | ||||||
| 		uci:set("gluon-node-info", location, "share_location", data) | 		uci:set("gluon-node-info", location, "share_location", data) | ||||||
| 	end | 	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.default = uci:get("gluon-node-info", location, "latitude") | ||||||
| 	o:depends(share_location, true) | 	o:depends(share_location, true) | ||||||
| 	o.datatype = "float" | 	o.datatype = "float" | ||||||
| @ -37,7 +40,7 @@ return function(form, uci) | |||||||
| 		uci:set("gluon-node-info", location, "latitude", data) | 		uci:set("gluon-node-info", location, "latitude", data) | ||||||
| 	end | 	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.default = uci:get("gluon-node-info", location, "longitude") | ||||||
| 	o:depends(share_location, true) | 	o:depends(share_location, true) | ||||||
| 	o.datatype = "float" | 	o.datatype = "float" | ||||||
| @ -46,7 +49,7 @@ return function(form, uci) | |||||||
| 	end | 	end | ||||||
| 
 | 
 | ||||||
| 	if show_altitude() then | 	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.default = uci:get("gluon-node-info", location, "altitude") | ||||||
| 		o:depends(share_location, true) | 		o:depends(share_location, true) | ||||||
| 		o.datatype = "float" | 		o.datatype = "float" | ||||||
|  | |||||||
| @ -1,8 +1,10 @@ | |||||||
| return function(form, uci) | return function(form, uci) | ||||||
|  | 	local pkg_i18n = i18n 'gluon-config-mode-hostname' | ||||||
|  | 
 | ||||||
| 	local pretty_hostname = require "pretty_hostname" | 	local pretty_hostname = require "pretty_hostname" | ||||||
| 
 | 
 | ||||||
| 	local s = form:section(Section) | 	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) | 	o.default = pretty_hostname.get(uci) | ||||||
| 
 | 
 | ||||||
| 	function o:write(data) | 	function o:write(data) | ||||||
|  | |||||||
| @ -1,3 +1,5 @@ | |||||||
|  | local site_i18n = i18n 'gluon-site' | ||||||
|  | 
 | ||||||
| local uci = require("simple-uci").cursor() | local uci = require("simple-uci").cursor() | ||||||
| local lutil = require "gluon.web.util" | local lutil = require "gluon.web.util" | ||||||
| local fs = require "nixio.fs" | local fs = require "nixio.fs" | ||||||
| @ -23,15 +25,15 @@ local msg | |||||||
| if has_tunneldigger then | if has_tunneldigger then | ||||||
| 	local tunneldigger_enabled = uci:get_bool("tunneldigger", "mesh_vpn", "enabled") | 	local tunneldigger_enabled = uci:get_bool("tunneldigger", "mesh_vpn", "enabled") | ||||||
| 	if not tunneldigger_enabled then | 	if not tunneldigger_enabled then | ||||||
| 		msg = _translate('gluon-config-mode:novpn') | 		msg = site_i18n._translate('gluon-config-mode:novpn') | ||||||
| 	end | 	end | ||||||
| elseif has_fastd then | elseif has_fastd then | ||||||
| 	local fastd_enabled = uci:get_bool("fastd", "mesh_vpn", "enabled") | 	local fastd_enabled = uci:get_bool("fastd", "mesh_vpn", "enabled") | ||||||
| 	if fastd_enabled then | 	if fastd_enabled then | ||||||
| 		pubkey = util.trim(lutil.exec("/etc/init.d/fastd show_key mesh_vpn")) | 		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 | 	else | ||||||
| 		msg = _translate('gluon-config-mode:novpn') | 		msg = site_i18n._translate('gluon-config-mode:novpn') | ||||||
| 	end | 	end | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -8,7 +8,9 @@ return function(form, uci) | |||||||
| 		return | 		return | ||||||
| 	end | 	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 ' .. | 		'Your internet connection can be used to establish a ' .. | ||||||
| 	        'VPN connection with other nodes. ' .. | 	        'VPN connection with other nodes. ' .. | ||||||
| 	        'Enable this option if there are no other nodes reachable ' .. | 	        'Enable this option if there are no other nodes reachable ' .. | ||||||
| @ -21,7 +23,7 @@ return function(form, uci) | |||||||
| 
 | 
 | ||||||
| 	local o | 	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") | 	meshvpn.default = uci:get_bool("fastd", "mesh_vpn", "enabled") or uci:get_bool("tunneldigger", "mesh_vpn", "enabled") | ||||||
| 	function meshvpn:write(data) | 	function meshvpn:write(data) | ||||||
| 		if has_fastd then | 		if has_fastd then | ||||||
| @ -32,7 +34,7 @@ return function(form, uci) | |||||||
| 		end | 		end | ||||||
| 	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:depends(meshvpn, true) | ||||||
| 	limit.default = uci:get_bool("simple-tc", "mesh_vpn", "enabled") | 	limit.default = uci:get_bool("simple-tc", "mesh_vpn", "enabled") | ||||||
| 	function limit:write(data) | 	function limit:write(data) | ||||||
| @ -41,7 +43,7 @@ return function(form, uci) | |||||||
| 		uci:set("simple-tc", "mesh_vpn", "ifname", "mesh-vpn") | 		uci:set("simple-tc", "mesh_vpn", "ifname", "mesh-vpn") | ||||||
| 	end | 	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:depends(limit, true) | ||||||
| 	o.default = uci:get("simple-tc", "mesh_vpn", "limit_ingress") | 	o.default = uci:get("simple-tc", "mesh_vpn", "limit_ingress") | ||||||
| 	o.datatype = "uinteger" | 	o.datatype = "uinteger" | ||||||
| @ -49,7 +51,7 @@ return function(form, uci) | |||||||
| 		uci:set("simple-tc", "mesh_vpn", "limit_ingress", data) | 		uci:set("simple-tc", "mesh_vpn", "limit_ingress", data) | ||||||
| 	end | 	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:depends(limit, true) | ||||||
| 	o.default = uci:get("simple-tc", "mesh_vpn", "limit_egress") | 	o.default = uci:get("simple-tc", "mesh_vpn", "limit_egress") | ||||||
| 	o.datatype = "uinteger" | 	o.datatype = "uinteger" | ||||||
|  | |||||||
| @ -1,3 +1,6 @@ | |||||||
|  | package 'gluon-web-admin' | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| local root = node() | local root = node() | ||||||
| if not root.target then | if not root.target then | ||||||
| 	root.target = alias("admin") | 	root.target = alias("admin") | ||||||
|  | |||||||
| @ -9,6 +9,9 @@ You may obtain a copy of the License at | |||||||
| 	http://www.apache.org/licenses/LICENSE-2.0 | 	http://www.apache.org/licenses/LICENSE-2.0 | ||||||
| ]]-- | ]]-- | ||||||
| 
 | 
 | ||||||
|  | package 'gluon-web-admin' | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| local fs = require 'nixio.fs' | local fs = require 'nixio.fs' | ||||||
| 
 | 
 | ||||||
| local tmpfile = "/tmp/firmware.img" | local tmpfile = "/tmp/firmware.img" | ||||||
| @ -106,17 +109,23 @@ local function action_upgrade(http, renderer) | |||||||
| 
 | 
 | ||||||
| 		renderer.render("layout", { | 		renderer.render("layout", { | ||||||
| 			content = "admin/upgrade", | 			content = "admin/upgrade", | ||||||
|  | 			env = { | ||||||
| 				bad_image = has_image and not has_support, | 				bad_image = has_image and not has_support, | ||||||
|  | 			}, | ||||||
|  | 			pkg = 'gluon-web-admin', | ||||||
| 		}) | 		}) | ||||||
| 
 | 
 | ||||||
| 	-- Step 2: present uploaded file, show checksum, confirmation | 	-- Step 2: present uploaded file, show checksum, confirmation | ||||||
| 	elseif step == 2 then | 	elseif step == 2 then | ||||||
| 		renderer.render("layout", { | 		renderer.render("layout", { | ||||||
| 			content = "admin/upgrade_confirm", | 			content = "admin/upgrade_confirm", | ||||||
|  | 			env = { | ||||||
| 				checksum   = image_checksum(tmpfile), | 				checksum   = image_checksum(tmpfile), | ||||||
| 				filesize   = fs.stat(tmpfile).size, | 				filesize   = fs.stat(tmpfile).size, | ||||||
| 				flashsize  = storage_size(), | 				flashsize  = storage_size(), | ||||||
| 				keepconfig = (http:formvalue("keepcfg") == "1"), | 				keepconfig = (http:formvalue("keepcfg") == "1"), | ||||||
|  | 			}, | ||||||
|  | 			pkg = 'gluon-web-admin', | ||||||
| 		}) | 		}) | ||||||
| 	elseif step == 3 then | 	elseif step == 3 then | ||||||
| 		if http:formvalue("keepcfg") == "1" then | 		if http:formvalue("keepcfg") == "1" then | ||||||
| @ -127,6 +136,7 @@ local function action_upgrade(http, renderer) | |||||||
| 		renderer.render("layout", { | 		renderer.render("layout", { | ||||||
| 			content = "admin/upgrade_reboot", | 			content = "admin/upgrade_reboot", | ||||||
| 			hidenav = true, | 			hidenav = true, | ||||||
|  | 			pkg = 'gluon-web-admin', | ||||||
| 		}) | 		}) | ||||||
| 	end | 	end | ||||||
| end | end | ||||||
|  | |||||||
| @ -1 +1,3 @@ | |||||||
|  | package 'gluon-web-autoupdater' | ||||||
|  | 
 | ||||||
| entry({"admin", "autoupdater"}, model("admin/autoupdater"), _("Automatic updates"), 80) | entry({"admin", "autoupdater"}, model("admin/autoupdater"), _("Automatic updates"), 80) | ||||||
|  | |||||||
| @ -1 +1,3 @@ | |||||||
|  | package 'gluon-web-logging' | ||||||
|  | 
 | ||||||
| entry({"admin", "logging"}, model("admin/logging"), _("Logging"), 85) | entry({"admin", "logging"}, model("admin/logging"), _("Logging"), 85) | ||||||
|  | |||||||
| @ -26,14 +26,14 @@ define Build/Configure | |||||||
| endef | endef | ||||||
| 
 | 
 | ||||||
| define Build/Compile | 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/) | 	$(call GluonSrcDiet,./luasrc,$(PKG_BUILD_DIR)/luadest/) | ||||||
| endef | endef | ||||||
| 
 | 
 | ||||||
| define Package/gluon-web-mesh-vpn-fastd/install | define Package/gluon-web-mesh-vpn-fastd/install | ||||||
| 	$(CP) ./files/* $(1)/ | 	$(CP) ./files/* $(1)/ | ||||||
| 	$(CP) $(PKG_BUILD_DIR)/luadest/* $(1)/ | 	$(CP) $(PKG_BUILD_DIR)/luadest/* $(1)/ | ||||||
| 	$(call GluonInstallI18N,gluon-mesh-vpn-fastd,$(1)) | 	$(call GluonInstallI18N,gluon-web-mesh-vpn-fastd,$(1)) | ||||||
| endef | endef | ||||||
| 
 | 
 | ||||||
| define Package/gluon-web-mesh-vpn-fastd/postinst | define Package/gluon-web-mesh-vpn-fastd/postinst | ||||||
|  | |||||||
| @ -1 +1,3 @@ | |||||||
|  | package 'gluon-web-mesh-vpn-fastd' | ||||||
|  | 
 | ||||||
| entry({"admin", "mesh_vpn_fastd"}, model("admin/mesh_vpn_fastd"), _("Mesh VPN"), 50) | entry({"admin", "mesh_vpn_fastd"}, model("admin/mesh_vpn_fastd"), _("Mesh VPN"), 50) | ||||||
|  | |||||||
| @ -6,6 +6,7 @@ local f = Form(translate('Mesh VPN')) | |||||||
| local s = f:section(Section) | local s = f:section(Section) | ||||||
| 
 | 
 | ||||||
| local mode = s:option(Value, 'mode') | local mode = s:option(Value, 'mode') | ||||||
|  | mode.package = "gluon-web-mesh-vpn-fastd" | ||||||
| mode.template = "gluon/model/mesh-vpn-fastd" | mode.template = "gluon/model/mesh-vpn-fastd" | ||||||
| 
 | 
 | ||||||
| local methods = uci:get('fastd', 'mesh_vpn', 'method') | local methods = uci:get('fastd', 'mesh_vpn', 'method') | ||||||
|  | |||||||
| @ -1 +1,3 @@ | |||||||
|  | package 'gluon-web-network' | ||||||
|  | 
 | ||||||
| entry({"admin", "network"}, model("admin/network"), _("Network"), 40) | entry({"admin", "network"}, model("admin/network"), _("Network"), 40) | ||||||
|  | |||||||
| @ -1 +1,3 @@ | |||||||
|  | package 'gluon-web-node-role' | ||||||
|  | 
 | ||||||
| entry({"admin", "noderole"}, model("admin/noderole"), "Node role", 60) | entry({"admin", "noderole"}, model("admin/noderole"), "Node role", 60) | ||||||
|  | |||||||
| @ -1 +1,3 @@ | |||||||
|  | package 'gluon-web-private-wifi' | ||||||
|  | 
 | ||||||
| entry({"admin", "privatewifi"}, model("admin/privatewifi"), _("Private WLAN"), 30) | entry({"admin", "privatewifi"}, model("admin/privatewifi"), _("Private WLAN"), 30) | ||||||
|  | |||||||
| @ -33,6 +33,10 @@ You may obtain a copy of the License at | |||||||
| 		return r | 		return r | ||||||
| 	end | 	end | ||||||
| 
 | 
 | ||||||
|  | 	local function title(node) | ||||||
|  | 		return i18n(node.pkg).translate(node.title) | ||||||
|  | 	end | ||||||
|  | 
 | ||||||
| 	local function subtree(prefix, node, name, ...) | 	local function subtree(prefix, node, name, ...) | ||||||
| 		if not node then return end | 		if not node then return end | ||||||
| 
 | 
 | ||||||
| @ -48,7 +52,7 @@ You may obtain a copy of the License at | |||||||
| 					local active = (v == name) | 					local active = (v == name) | ||||||
| 			%> | 			%> | ||||||
| 			<li class="tabmenu-item-<%=v%><% if active then %> active<% end %>"> | 			<li class="tabmenu-item-<%=v%><% if active then %> active<% end %>"> | ||||||
| 				<a href="<%=url(append(prefix, v))%>"><%=pcdata(translate(child.title))%></a> | 				<a href="<%=url(append(prefix, v))%>"><%=pcdata(title(child))%></a> | ||||||
| 			</li> | 			</li> | ||||||
| 			<% | 			<% | ||||||
| 				end | 				end | ||||||
| @ -71,7 +75,7 @@ You may obtain a copy of the License at | |||||||
| 	<head> | 	<head> | ||||||
| 		<meta charset="UTF-8" /> | 		<meta charset="UTF-8" /> | ||||||
| 		<link rel="stylesheet" type="text/css" media="screen" href="<%=media%>/cascade.css" /> | 		<link rel="stylesheet" type="text/css" media="screen" href="<%=media%>/cascade.css" /> | ||||||
| 		<title><%=pcdata( hostname .. ( (rnode and rnode.title) and ' - ' .. translate(rnode.title) or '')) %></title> | 		<title><%=pcdata( hostname .. ((rnode and rnode.title) and ' - ' .. title(rnode) or '')) %></title> | ||||||
| 	</head> | 	</head> | ||||||
| 	<body> | 	<body> | ||||||
| 
 | 
 | ||||||
| @ -88,7 +92,7 @@ You may obtain a copy of the License at | |||||||
| 		<% if #categories > 1 and not hidenav then %> | 		<% if #categories > 1 and not hidenav then %> | ||||||
| 			<ul id="topmenu"> | 			<ul id="topmenu"> | ||||||
| 				<% for i, r in ipairs(categories) do %> | 				<% for i, r in ipairs(categories) do %> | ||||||
| 					<li><a class="topcat<% if request[1] == r then %> active<%end%>" href="<%=url({r})%>"><%=pcdata(translate(root.nodes[r].title))%></a></li> | 					<li><a class="topcat<% if request[1] == r then %> active<%end%>" href="<%=url({r})%>"><%=pcdata(title(root.nodes[r]))%></a></li> | ||||||
| 				<% end %> | 				<% end %> | ||||||
| 			</ul> | 			</ul> | ||||||
| 		<% end %> | 		<% end %> | ||||||
| @ -110,9 +114,9 @@ You may obtain a copy of the License at | |||||||
| 			</noscript> | 			</noscript> | ||||||
| 
 | 
 | ||||||
| 			<% | 			<% | ||||||
| 				ok, err = pcall(include, content) | 				ok, err = pcall(renderer.render, content, env, pkg) | ||||||
| 				if not ok then | 				if not ok then | ||||||
| 					renderer.render('error500', {message = err}) | 					renderer.render('error500', {message = err}, 'gluon-web') | ||||||
| 				end | 				end | ||||||
| 			%> | 			%> | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1 +1,3 @@ | |||||||
|  | package 'gluon-web-wifi-config' | ||||||
|  | 
 | ||||||
| entry({"admin", "wifi-config"}, model("admin/wifi-config"), _("WLAN"), 20) | entry({"admin", "wifi-config"}, model("admin/wifi-config"), _("WLAN"), 20) | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| -- Copyright 2008 Steven Barth <steven@midlink.org> | -- Copyright 2008 Steven Barth <steven@midlink.org> | ||||||
| -- Copyright 2008-2015 Jo-Philipp Wich <jow@openwrt.org> | -- Copyright 2008-2015 Jo-Philipp Wich <jow@openwrt.org> | ||||||
| -- Copyright 2017 Matthias Schiffer <mschiffer@universe-factory.net> | -- Copyright 2017-2018 Matthias Schiffer <mschiffer@universe-factory.net> | ||||||
| -- Licensed to the public under the Apache License 2.0. | -- Licensed to the public under the Apache License 2.0. | ||||||
| 
 | 
 | ||||||
| local fs = require "nixio.fs" | local fs = require "nixio.fs" | ||||||
| @ -77,7 +77,7 @@ local function set_language(renderer, accept) | |||||||
| 	end | 	end | ||||||
| 
 | 
 | ||||||
| 	for match in accept:gmatch("[^,]+") do | 	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) | 		local q = tonumber(match:match(';q=(%S+)%s*$') or 1) | ||||||
| 
 | 
 | ||||||
| 		if lang == '*' then | 		if lang == '*' then | ||||||
| @ -93,11 +93,7 @@ local function set_language(renderer, accept) | |||||||
| 		return (weights[a] or 0) > (weights[b] or 0) | 		return (weights[a] or 0) > (weights[b] or 0) | ||||||
| 	end) | 	end) | ||||||
| 
 | 
 | ||||||
| 	for _, lang in ipairs(langs) do | 	renderer.set_language(langs) | ||||||
| 		if renderer.setlanguage(lang) then |  | ||||||
| 			return |  | ||||||
| 		end |  | ||||||
| 	end |  | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -147,7 +143,20 @@ function dispatch(http, request) | |||||||
| 		url         = function(path) return build_url(http, path) end, | 		url         = function(path) return build_url(http, path) end, | ||||||
| 	}, { __index = _G })) | 	}, { __index = _G })) | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | 	local function createtree() | ||||||
|  | 		local base = util.libpath() .. "/controller/" | ||||||
|  | 
 | ||||||
|  | 		local function load_ctl(path) | ||||||
|  | 			local ctl = assert(loadfile(path)) | ||||||
|  | 
 | ||||||
|  | 			local _pkg | ||||||
|  | 
 | ||||||
| 			local subdisp = setmetatable({ | 			local subdisp = setmetatable({ | ||||||
|  | 				package = function(name) | ||||||
|  | 					_pkg = name | ||||||
|  | 				end, | ||||||
|  | 
 | ||||||
| 				node = function(...) | 				node = function(...) | ||||||
| 					return _node({...}) | 					return _node({...}) | ||||||
| 				end, | 				end, | ||||||
| @ -158,6 +167,7 @@ function dispatch(http, request) | |||||||
| 					c.target = target | 					c.target = target | ||||||
| 					c.title  = title | 					c.title  = title | ||||||
| 					c.order  = order | 					c.order  = order | ||||||
|  | 					c.pkg    = _pkg | ||||||
| 
 | 
 | ||||||
| 					return c | 					return c | ||||||
| 				end, | 				end, | ||||||
| @ -177,17 +187,19 @@ function dispatch(http, request) | |||||||
| 				end, | 				end, | ||||||
| 
 | 
 | ||||||
| 				template = function(view) | 				template = function(view) | ||||||
|  | 					local pkg = _pkg | ||||||
| 					return function() | 					return function() | ||||||
| 				renderer.render("layout", {content = view}) | 						renderer.render("layout", {content = view, pkg = pkg}) | ||||||
| 					end | 					end | ||||||
| 				end, | 				end, | ||||||
| 
 | 
 | ||||||
| 				model = function(name) | 				model = function(name) | ||||||
|  | 					local pkg = _pkg | ||||||
| 					return function() | 					return function() | ||||||
| 						local hidenav = false | 						local hidenav = false | ||||||
| 
 | 
 | ||||||
| 						local model = require "gluon.web.model" | 						local model = require "gluon.web.model" | ||||||
| 				local maps = model.load(name, renderer) | 						local maps = model.load(name, renderer, pkg) | ||||||
| 
 | 
 | ||||||
| 						for _, map in ipairs(maps) do | 						for _, map in ipairs(maps) do | ||||||
| 							map:parse(http) | 							map:parse(http) | ||||||
| @ -199,7 +211,9 @@ function dispatch(http, request) | |||||||
| 
 | 
 | ||||||
| 						renderer.render("layout", { | 						renderer.render("layout", { | ||||||
| 							content = "model/wrapper", | 							content = "model/wrapper", | ||||||
|  | 							env = { | ||||||
| 								maps = maps, | 								maps = maps, | ||||||
|  | 							}, | ||||||
| 							hidenav = hidenav, | 							hidenav = hidenav, | ||||||
| 						}) | 						}) | ||||||
| 					end | 					end | ||||||
| @ -210,12 +224,6 @@ function dispatch(http, request) | |||||||
| 				end, | 				end, | ||||||
| 			}, { __index = _G }) | 			}, { __index = _G }) | ||||||
| 
 | 
 | ||||||
| 	local function createtree() |  | ||||||
| 		local base = util.libpath() .. "/controller/" |  | ||||||
| 
 |  | ||||||
| 		local function load_ctl(path) |  | ||||||
| 			local ctl = assert(loadfile(path)) |  | ||||||
| 
 |  | ||||||
| 			local env = setmetatable({}, { __index = subdisp }) | 			local env = setmetatable({}, { __index = subdisp }) | ||||||
| 			setfenv(ctl, env) | 			setfenv(ctl, env) | ||||||
| 
 | 
 | ||||||
| @ -239,9 +247,14 @@ function dispatch(http, request) | |||||||
| 
 | 
 | ||||||
| 	if not node or not node.target then | 	if not node or not node.target then | ||||||
| 		http:status(404, "Not Found") | 		http:status(404, "Not Found") | ||||||
| 		renderer.render("layout", { content = "error404", message = | 		renderer.render("layout", { | ||||||
|  | 			content = "error404", | ||||||
|  | 			env = { | ||||||
|  | 				message = | ||||||
| 					"No page is registered at '/" .. table.concat(request, "/") .. "'.\n" .. | 					"No page is registered at '/" .. table.concat(request, "/") .. "'.\n" .. | ||||||
| 		        "If this URL belongs to an extension, make sure it is properly installed.\n" | 				        "If this URL belongs to an extension, make sure it is properly installed.\n", | ||||||
|  | 			}, | ||||||
|  | 			pkg = 'gluon-web', | ||||||
| 		}) | 		}) | ||||||
| 		return | 		return | ||||||
| 	end | 	end | ||||||
| @ -251,9 +264,14 @@ function dispatch(http, request) | |||||||
| 	local ok, err = pcall(node.target) | 	local ok, err = pcall(node.target) | ||||||
| 	if not ok then | 	if not ok then | ||||||
| 		http:status(500, "Internal Server Error") | 		http:status(500, "Internal Server Error") | ||||||
| 		renderer.render("layout", { content = "error500", message = | 		renderer.render("layout", { | ||||||
|  | 			content = "error500", | ||||||
|  | 			env = { | ||||||
|  | 				message = | ||||||
| 					"Failed to execute dispatcher target for entry '/" .. table.concat(request, "/") .. "'.\n" .. | 					"Failed to execute dispatcher target for entry '/" .. table.concat(request, "/") .. "'.\n" .. | ||||||
| 			"The called action terminated with an exception:\n" .. tostring(err or "(unknown)") | 					"The called action terminated with an exception:\n" .. tostring(err or "(unknown)"), | ||||||
|  | 			}, | ||||||
|  | 			pkg = 'gluon-web', | ||||||
| 		}) | 		}) | ||||||
| 	end | 	end | ||||||
| end | end | ||||||
|  | |||||||
							
								
								
									
										54
									
								
								package/gluon-web/luasrc/usr/lib/lua/gluon/web/i18n.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								package/gluon-web/luasrc/usr/lib/lua/gluon/web/i18n.lua
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,54 @@ | |||||||
|  | -- Copyright 2018 Matthias Schiffer <mschiffer@universe-factory.net> | ||||||
|  | -- 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 | ||||||
| @ -4,11 +4,11 @@ | |||||||
| 
 | 
 | ||||||
| module("gluon.web.model", package.seeall) | module("gluon.web.model", package.seeall) | ||||||
| 
 | 
 | ||||||
| local util = require("gluon.web.util") | local util = require "gluon.web.util" | ||||||
| 
 | 
 | ||||||
| local fs         = require("nixio.fs") | local fs         = require "nixio.fs" | ||||||
| local datatypes  = require("gluon.web.model.datatypes") | local datatypes  = require "gluon.web.model.datatypes" | ||||||
| local dispatcher = require("gluon.web.dispatcher") | local dispatcher = require "gluon.web.dispatcher" | ||||||
| local class      = util.class | local class      = util.class | ||||||
| local instanceof = util.instanceof | local instanceof = util.instanceof | ||||||
| 
 | 
 | ||||||
| @ -17,7 +17,7 @@ FORM_VALID   =  1 | |||||||
| FORM_INVALID = -1 | FORM_INVALID = -1 | ||||||
| 
 | 
 | ||||||
| -- Loads a model from given file, creating an environment and returns it | -- 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/" | 	local modeldir = util.libpath() .. "/model/" | ||||||
| 
 | 
 | ||||||
| 	if not fs.access(modeldir..name..".lua") then | 	if not fs.access(modeldir..name..".lua") then | ||||||
| @ -26,14 +26,16 @@ function load(name, renderer) | |||||||
| 
 | 
 | ||||||
| 	local func = assert(loadfile(modeldir..name..".lua")) | 	local func = assert(loadfile(modeldir..name..".lua")) | ||||||
| 
 | 
 | ||||||
| 	local env = { | 	local i18n = setmetatable({ | ||||||
| 		translate=renderer.translate, | 		i18n = renderer.i18n | ||||||
| 		translatef=renderer.translatef, | 	}, { | ||||||
| 	} | 		__index = renderer.i18n(pkg) | ||||||
|  | 	}) | ||||||
| 
 | 
 | ||||||
| 	setfenv(func, setmetatable(env, {__index = | 
 | ||||||
|  | 	setfenv(func, setmetatable({}, {__index = | ||||||
| 		function(tbl, key) | 		function(tbl, key) | ||||||
| 			return _M[key] or _G[key] | 			return _M[key] or i18n[key] or _G[key] | ||||||
| 		end | 		end | ||||||
| 	})) | 	})) | ||||||
| 
 | 
 | ||||||
| @ -85,6 +87,7 @@ function Node:__init__(title, description, name) | |||||||
| 	self.name = name | 	self.name = name | ||||||
| 	self.index = nil | 	self.index = nil | ||||||
| 	self.parent = nil | 	self.parent = nil | ||||||
|  | 	self.package = 'gluon-web' | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| function Node:append(obj) | function Node:append(obj) | ||||||
| @ -116,7 +119,7 @@ function Node:render(renderer, scope) | |||||||
| 			id  = self:id(), | 			id  = self:id(), | ||||||
| 			scope = scope, | 			scope = scope, | ||||||
| 		}, {__index = scope}) | 		}, {__index = scope}) | ||||||
| 		renderer.render(self.template, env) | 		renderer.render(self.template, env, self.package) | ||||||
| 	end | 	end | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,39 +1,59 @@ | |||||||
| -- Copyright 2008 Steven Barth <steven@midlink.org> | -- Copyright 2008 Steven Barth <steven@midlink.org> | ||||||
| -- Copyright 2017 Matthias Schiffer <mschiffer@universe-factory.net> | -- Copyright 2017-2018 Matthias Schiffer <mschiffer@universe-factory.net> | ||||||
| -- Licensed to the public under the Apache License 2.0. | -- Licensed to the public under the Apache License 2.0. | ||||||
| 
 | 
 | ||||||
| local tparser = require "gluon.web.template.parser" | local tparser = require "gluon.web.template.parser" | ||||||
|  | local i18n = require "gluon.web.i18n" | ||||||
| local util = require "gluon.web.util" | 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" | module "gluon.web.template" | ||||||
| 
 | 
 | ||||||
| local viewdir = util.libpath() .. "/view/" | local viewdir = util.libpath() .. "/view/" | ||||||
| local i18ndir = util.libpath() .. "/i18n/" |  | ||||||
| 
 | 
 | ||||||
| function renderer(env) | function renderer(env) | ||||||
| 	local ctx = {} | 	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 {} | 		scope = scope or {} | ||||||
|  | 		local t = ctx.i18n(pkg) | ||||||
| 
 | 
 | ||||||
| 		local locals = { | 		local locals = { | ||||||
| 			renderer = ctx, | 			renderer = ctx, | ||||||
| 			translate = ctx.translate, | 			i18n = ctx.i18n, | ||||||
| 			translatef = ctx.translatef, | 			translate = t.translate, | ||||||
| 			_translate = ctx._translate, | 			translatef = t.translatef, | ||||||
|  | 			_translate = t._translate, | ||||||
| 			include = function(name) | 			include = function(name) | ||||||
| 				ctx.render(name, scope) | 				ctx.render(name, scope, pkg) | ||||||
| 			end, | 			end, | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		setfenv(template, setmetatable({}, { | 		setfenv(template, setmetatable({}, { | ||||||
| 			__index = function(tbl, key) | 			__index = function(tbl, key) | ||||||
| 				return scope[key] or env[key] or locals[key] | 				return scope[key] or locals[key] or env[key] | ||||||
| 			end | 			end | ||||||
| 		})) | 		})) | ||||||
| 
 | 
 | ||||||
| @ -46,7 +66,7 @@ function renderer(env) | |||||||
| 	--- Render a certain template. | 	--- Render a certain template. | ||||||
| 	-- @param name		Template name | 	-- @param name		Template name | ||||||
| 	-- @param scope		Scope to assign to template (optional) | 	-- @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 sourcefile = viewdir .. name .. ".html" | ||||||
| 		local template, _, err = tparser.parse(sourcefile) | 		local template, _, err = tparser.parse(sourcefile) | ||||||
| 
 | 
 | ||||||
| @ -54,45 +74,19 @@ function renderer(env) | |||||||
| 			"Error while parsing template '" .. sourcefile .. "':\n" .. | 			"Error while parsing template '" .. sourcefile .. "':\n" .. | ||||||
| 			(err or "Unknown syntax error")) | 			(err or "Unknown syntax error")) | ||||||
| 
 | 
 | ||||||
| 		render_template(name, template, scope) | 		render_template(name, template, scope, pkg) | ||||||
| 	end | 	end | ||||||
| 
 | 
 | ||||||
| 	--- Render a template from a string. | 	--- Render a template from a string. | ||||||
| 	-- @param template	Template string | 	-- @param template	Template string | ||||||
| 	-- @param scope		Scope to assign to template (optional) | 	-- @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) | 		local template, _, err = tparser.parse_string(str) | ||||||
| 
 | 
 | ||||||
| 		assert(template, "Error while parsing template:\n" .. | 		assert(template, "Error while parsing template:\n" .. | ||||||
| 			(err or "Unknown syntax error")) | 			(err or "Unknown syntax error")) | ||||||
| 
 | 
 | ||||||
| 		render_template('(local)', template, scope) | 		render_template('(local)', template, scope, pkg) | ||||||
| 	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(...) |  | ||||||
| 	end | 	end | ||||||
| 
 | 
 | ||||||
| 	return ctx | 	return ctx | ||||||
|  | |||||||
| @ -23,15 +23,12 @@ | |||||||
| #include <sys/mman.h> | #include <sys/mman.h> | ||||||
| 
 | 
 | ||||||
| #include <arpa/inet.h> | #include <arpa/inet.h> | ||||||
| #include <dirent.h> |  | ||||||
| #include <fcntl.h> | #include <fcntl.h> | ||||||
| #include <fnmatch.h> |  | ||||||
| #include <stdio.h> | #include <stdio.h> | ||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
| #include <unistd.h> | #include <unistd.h> | ||||||
| #include <limits.h> |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| struct lmo_entry { | struct lmo_entry { | ||||||
| @ -41,28 +38,6 @@ struct lmo_entry { | |||||||
| 	uint32_t length; | 	uint32_t length; | ||||||
| } __attribute__((packed)); | } __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) { | static inline uint16_t get_le16(const void *data) { | ||||||
| 	const uint8_t *d = data; | 	const uint8_t *d = data; | ||||||
| @ -122,12 +97,13 @@ static uint32_t sfh_hash(const void *input, size_t len) | |||||||
| 	return hash; | 	return hash; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static lmo_archive_t * lmo_open(const char *file) | bool lmo_load(lmo_catalog_t *cat, const char *file) | ||||||
| { | { | ||||||
| 	int fd = -1; | 	int fd = -1; | ||||||
| 	lmo_archive_t *ar = NULL; |  | ||||||
| 	struct stat s; | 	struct stat s; | ||||||
| 
 | 
 | ||||||
|  | 	cat->data = MAP_FAILED; | ||||||
|  | 
 | ||||||
| 	fd = open(file, O_RDONLY|O_CLOEXEC); | 	fd = open(file, O_RDONLY|O_CLOEXEC); | ||||||
| 	if (fd < 0) | 	if (fd < 0) | ||||||
| 		goto err; | 		goto err; | ||||||
| @ -135,110 +111,42 @@ static lmo_archive_t * lmo_open(const char *file) | |||||||
| 	if (fstat(fd, &s)) | 	if (fstat(fd, &s)) | ||||||
| 		goto err; | 		goto err; | ||||||
| 
 | 
 | ||||||
| 	if ((ar = calloc(1, sizeof(*ar))) != NULL) { | 	cat->data = mmap(NULL, s.st_size, PROT_READ, MAP_SHARED, fd, 0); | ||||||
| 		ar->data = mmap(NULL, s.st_size, PROT_READ, MAP_SHARED, fd, 0); |  | ||||||
| 
 | 
 | ||||||
| 	close(fd); | 	close(fd); | ||||||
| 	fd = -1; | 	fd = -1; | ||||||
| 
 | 
 | ||||||
| 		if (ar->data == MAP_FAILED) | 	if (cat->data == MAP_FAILED) | ||||||
| 		goto err; | 		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)); | 	uint32_t idx_offset = get_be32(cat->end - sizeof(uint32_t)); | ||||||
| 		ar->index = (const lmo_entry_t *)(ar->data + idx_offset); | 	cat->index = (const lmo_entry_t *)(cat->data + idx_offset); | ||||||
| 
 | 
 | ||||||
| 		if ((const char *)ar->index > (ar->end - sizeof(uint32_t))) | 	if ((const char *)cat->index > (cat->end - sizeof(uint32_t))) | ||||||
| 		goto err; | 		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: | err: | ||||||
| 	if (fd >= 0) | 	if (fd >= 0) | ||||||
| 		close(fd); | 		close(fd); | ||||||
| 
 | 
 | ||||||
| 	if (ar != NULL) { | 	if (cat->data != MAP_FAILED) | ||||||
| 		if ((ar->data != NULL) && (ar->data != MAP_FAILED)) | 		munmap(cat->data, cat->end - cat->data); | ||||||
| 			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; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	return false; | 	return false; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool lmo_load_catalog(const char *lang, const char *dir) | void lmo_unload(lmo_catalog_t *cat) | ||||||
| { | { | ||||||
| 	DIR *dh = NULL; | 	if (cat->data != MAP_FAILED) | ||||||
| 	char pattern[16]; | 		munmap(cat->data, cat->end - cat->data); | ||||||
| 	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; |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| static int lmo_compare_entry(const void *a, const void *b) | static int lmo_compare_entry(const void *a, const void *b) | ||||||
| { | { | ||||||
| @ -253,34 +161,26 @@ static int lmo_compare_entry(const void *a, const void *b) | |||||||
| 		return 0; | 		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; | 	lmo_entry_t key; | ||||||
| 	key.key_id = htonl(hash); | 	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; | 		return false; | ||||||
| 
 | 
 | ||||||
| 	uint32_t hash = sfh_hash(key, keylen); | 	*out = cat->data + ntohl(e->offset); | ||||||
| 
 |  | ||||||
| 	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; |  | ||||||
| 
 |  | ||||||
| 		*out = ar->data + ntohl(e->offset); |  | ||||||
| 	*outlen = ntohl(e->length); | 	*outlen = ntohl(e->length); | ||||||
| 
 | 
 | ||||||
| 		if (*out + *outlen > ar->end) | 	if (*out + *outlen > cat->end) | ||||||
| 			continue; | 		return false; | ||||||
| 
 | 
 | ||||||
| 	return true; | 	return true; | ||||||
| } | } | ||||||
| 
 |  | ||||||
| 	return false; |  | ||||||
| } |  | ||||||
|  | |||||||
| @ -24,7 +24,21 @@ | |||||||
| #include <stddef.h> | #include <stddef.h> | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| bool lmo_load_catalog(const char *lang, const char *dir); | typedef struct lmo_entry lmo_entry_t; | ||||||
| bool lmo_translate(const char *key, size_t keylen, char **out, size_t *outlen); | 
 | ||||||
|  | 
 | ||||||
|  | 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 | #endif | ||||||
|  | |||||||
| @ -29,7 +29,7 @@ | |||||||
| #include <string.h> | #include <string.h> | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| #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) | 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; | 	return 1; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static int template_L_load_catalog(lua_State *L) { | static int template_L_load_catalog(lua_State *L) | ||||||
| 	const char *lang = luaL_optstring(L, 1, "en"); | { | ||||||
| 	const char *dir  = luaL_checkstring(L, 2); | 	const char *file = luaL_checkstring(L, 1); | ||||||
| 	lua_pushboolean(L, lmo_load_catalog(lang, dir)); | 
 | ||||||
| 	return 1; | 	lmo_catalog_t *cat = lua_newuserdata(L, sizeof(*cat)); | ||||||
|  | 	if (!lmo_load(cat, file)) { | ||||||
|  | 		lua_pop(L, 1); | ||||||
|  | 		return 0; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| static int template_L_translate(lua_State *L) { |         luaL_getmetatable(L, TEMPLATE_CATALOG); | ||||||
| 	size_t len; |         lua_setmetatable(L, -2); | ||||||
| 	char *tr; |  | ||||||
| 	size_t trlen; |  | ||||||
| 	const char *key = luaL_checklstring(L, 1, &len); |  | ||||||
| 
 |  | ||||||
| 	if (lmo_translate(key, len, &tr, &trlen)) |  | ||||||
| 		lua_pushlstring(L, tr, trlen); |  | ||||||
| 	else |  | ||||||
| 		lua_pushnil(L); |  | ||||||
| 
 | 
 | ||||||
| 	return 1; | 	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[] = { | static const luaL_reg R[] = { | ||||||
| 	{ "parse",          template_L_parse }, | 	{ "parse",          template_L_parse }, | ||||||
| 	{ "parse_string",   template_L_parse_string }, | 	{ "parse_string",   template_L_parse_string }, | ||||||
| 	{ "pcdata",         template_L_pcdata }, | 	{ "pcdata",         template_L_pcdata }, | ||||||
| 	{ "load_catalog",   template_L_load_catalog }, | 	{ "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"))) | __attribute__ ((visibility("default"))) | ||||||
| LUALIB_API int luaopen_gluon_web_template_parser(lua_State *L) { | 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; | 	return 1; | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user