gluon-web: add i18n package namespaces
This commit is contained in:
parent
1a426c3bb9
commit
557565e189
@ -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 <em>Advanced settings</em>.')
|
||||
pkg_i18n.translate('Automatic updates are disabled. They can be enabled in <em>Advanced settings</em>.')
|
||||
)
|
||||
end
|
||||
end
|
||||
|
@ -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 <em>publicly</em> '
|
||||
.. '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"
|
||||
|
@ -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
|
||||
-%>
|
||||
<p>
|
||||
|
@ -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, {
|
||||
|
@ -1,2 +1,4 @@
|
||||
package 'gluon-config-mode-core'
|
||||
|
||||
entry({}, alias("wizard"))
|
||||
entry({"wizard"}, model("gluon-config-mode/wizard"), _("Wizard"), 5)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
||||
|
@ -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"
|
||||
|
@ -1,3 +1,6 @@
|
||||
package 'gluon-web-admin'
|
||||
|
||||
|
||||
local root = node()
|
||||
if not root.target then
|
||||
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
|
||||
]]--
|
||||
|
||||
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",
|
||||
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",
|
||||
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
|
||||
|
@ -1 +1,3 @@
|
||||
package 'gluon-web-autoupdater'
|
||||
|
||||
entry({"admin", "autoupdater"}, model("admin/autoupdater"), _("Automatic updates"), 80)
|
||||
|
@ -1 +1,3 @@
|
||||
package 'gluon-web-logging'
|
||||
|
||||
entry({"admin", "logging"}, model("admin/logging"), _("Logging"), 85)
|
||||
|
@ -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
|
||||
|
@ -1 +1,3 @@
|
||||
package 'gluon-web-mesh-vpn-fastd'
|
||||
|
||||
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 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')
|
||||
|
@ -1 +1,3 @@
|
||||
package 'gluon-web-network'
|
||||
|
||||
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)
|
||||
|
@ -1 +1,3 @@
|
||||
package 'gluon-web-private-wifi'
|
||||
|
||||
entry({"admin", "privatewifi"}, model("admin/privatewifi"), _("Private WLAN"), 30)
|
||||
|
@ -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)
|
||||
%>
|
||||
<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>
|
||||
<%
|
||||
end
|
||||
@ -71,7 +75,7 @@ You may obtain a copy of the License at
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<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>
|
||||
<body>
|
||||
|
||||
@ -88,7 +92,7 @@ You may obtain a copy of the License at
|
||||
<% if #categories > 1 and not hidenav then %>
|
||||
<ul id="topmenu">
|
||||
<% 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 %>
|
||||
</ul>
|
||||
<% end %>
|
||||
@ -110,9 +114,9 @@ You may obtain a copy of the License at
|
||||
</noscript>
|
||||
|
||||
<%
|
||||
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
|
||||
%>
|
||||
|
||||
|
@ -1 +1,3 @@
|
||||
package 'gluon-web-wifi-config'
|
||||
|
||||
entry({"admin", "wifi-config"}, model("admin/wifi-config"), _("WLAN"), 20)
|
||||
|
@ -1,6 +1,6 @@
|
||||
-- Copyright 2008 Steven Barth <steven@midlink.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.
|
||||
|
||||
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,7 +143,20 @@ function dispatch(http, request)
|
||||
url = function(path) return build_url(http, path) end,
|
||||
}, { __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({
|
||||
package = function(name)
|
||||
_pkg = name
|
||||
end,
|
||||
|
||||
node = function(...)
|
||||
return _node({...})
|
||||
end,
|
||||
@ -158,6 +167,7 @@ function dispatch(http, request)
|
||||
c.target = target
|
||||
c.title = title
|
||||
c.order = order
|
||||
c.pkg = _pkg
|
||||
|
||||
return c
|
||||
end,
|
||||
@ -177,17 +187,19 @@ function dispatch(http, request)
|
||||
end,
|
||||
|
||||
template = function(view)
|
||||
local pkg = _pkg
|
||||
return function()
|
||||
renderer.render("layout", {content = view})
|
||||
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)
|
||||
local maps = model.load(name, renderer, pkg)
|
||||
|
||||
for _, map in ipairs(maps) do
|
||||
map:parse(http)
|
||||
@ -199,7 +211,9 @@ function dispatch(http, request)
|
||||
|
||||
renderer.render("layout", {
|
||||
content = "model/wrapper",
|
||||
env = {
|
||||
maps = maps,
|
||||
},
|
||||
hidenav = hidenav,
|
||||
})
|
||||
end
|
||||
@ -210,12 +224,6 @@ function dispatch(http, request)
|
||||
end,
|
||||
}, { __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 })
|
||||
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 =
|
||||
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"
|
||||
"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 =
|
||||
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)")
|
||||
"The called action terminated with an exception:\n" .. tostring(err or "(unknown)"),
|
||||
},
|
||||
pkg = 'gluon-web',
|
||||
})
|
||||
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)
|
||||
|
||||
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
|
||||
|
||||
|
@ -1,39 +1,59 @@
|
||||
-- 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.
|
||||
|
||||
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
|
||||
|
@ -23,15 +23,12 @@
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <fnmatch.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
|
||||
|
||||
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;
|
||||
|
||||
if (ar->data == MAP_FAILED)
|
||||
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)))
|
||||
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);
|
||||
|
||||
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);
|
||||
*out = cat->data + ntohl(e->offset);
|
||||
*outlen = ntohl(e->length);
|
||||
|
||||
if (*out + *outlen > ar->end)
|
||||
continue;
|
||||
if (*out + *outlen > cat->end)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -24,7 +24,21 @@
|
||||
#include <stddef.h>
|
||||
|
||||
|
||||
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
|
||||
|
@ -29,7 +29,7 @@
|
||||
#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)
|
||||
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user