aboutsummaryrefslogtreecommitdiff
path: root/advtrains/poconvert.lua
diff options
context:
space:
mode:
authorY. Wang <yw05@forksworld.de>2023-10-04 20:07:24 +0200
committerY. Wang <y5nw@protonmail.com>2024-11-04 17:17:51 +0100
commit425b0993d355b9f45ddd400bd4925f9f1a5bd34d (patch)
tree77a105e5bbf28c03e31c5c30e5866387d618e174 /advtrains/poconvert.lua
parentbed66e0f901ae9f8e21675035f174292120fea4f (diff)
downloadadvtrains-425b0993d355b9f45ddd400bd4925f9f1a5bd34d.tar.gz
advtrains-425b0993d355b9f45ddd400bd4925f9f1a5bd34d.tar.bz2
advtrains-425b0993d355b9f45ddd400bd4925f9f1a5bd34d.zip
Autogenerate .tr files from .po files
Diffstat (limited to 'advtrains/poconvert.lua')
-rw-r--r--advtrains/poconvert.lua185
1 files changed, 185 insertions, 0 deletions
diff --git a/advtrains/poconvert.lua b/advtrains/poconvert.lua
new file mode 100644
index 0000000..74f962e
--- /dev/null
+++ b/advtrains/poconvert.lua
@@ -0,0 +1,185 @@
+local unescape_string
+do
+ local schartbl = { -- https://en.wikipedia.org/wiki/Escape_sequences_in_C
+ a = "\a",
+ b = "\b",
+ e = string.char(0x1b),
+ f = "\f",
+ n = "\n",
+ r = "\r",
+ t = "\t",
+ v = "\v",
+ }
+ local function replace_single(pfx, c)
+ local pl = #pfx
+ if pl % 2 == 0 then
+ return string.sub(pfx, 1, pl/2) .. c
+ end
+ return string.sub(pfx, 1, math.floor(pl/2)) .. (schartbl[c] or c)
+ end
+ unescape_string = function(str)
+ return string.gsub(str, [[(\+)([abefnrtv'"?])]], replace_single)
+ end
+end
+
+local function readstring_aux(str, pos)
+ local _, spos = string.find(str, [[^%s*"]], pos)
+ if not spos then
+ return nil
+ end
+ local ipos = spos
+ while true do
+ local _, epos, m = string.find(str, [[(\*)"]], ipos+1)
+ if not epos then
+ return error("String extends beyond the end of input")
+ end
+ ipos = epos
+ if #m % 2 == 0 then
+ return unescape_string(string.sub(str, spos+1, epos-1)), epos+1
+ end
+ end
+end
+
+local function readstring(str, pos)
+ local st = {}
+ local nxt = pos
+ while true do
+ local s, npos = readstring_aux(str, nxt)
+ if not s then
+ if not st[1] then
+ return nil, nxt
+ else
+ return table.concat(st), nxt
+ end
+ end
+ nxt = npos
+ table.insert(st, s)
+ end
+end
+
+local function readtoken(str, pos)
+ local _, epos, tok = string.find(str, [[^%s*(%S+)]], pos)
+ if epos == nil then
+ return nil, pos
+ end
+ return tok, epos+1
+end
+
+local function readcomment_add_flags(flags, s)
+ for flag in string.gmatch(s, ",%s*([^,]+)") do
+ flags[flag] = true
+ end
+end
+
+local function readcomment_aux(str, pos)
+ local _, epos, sval = string.find(str, "^\n*#([^\n]*)", pos)
+ if not epos then
+ return nil
+ end
+ return sval, epos+1
+end
+
+local function readcomment(str, pos)
+ local st = {}
+ local nxt = pos
+ local flags = {}
+ while true do
+ local s, npos = readcomment_aux(str, nxt)
+ if not npos then
+ local t = {
+ comment = table.concat(st, "\n"),
+ flags = flags,
+ }
+ return t, nxt
+ end
+ if string.sub(s, 1, 1) == "," then
+ readcomment_add_flags(flags, s)
+ end
+ table.insert(st, s)
+ nxt = npos
+ end
+end
+
+local function readpo(str)
+ local st = {}
+ local pos = 1
+ while true do
+ local entry, nxt = readcomment(str, pos)
+ local msglines = 0
+ while true do
+ local tok, npos = readtoken(str, nxt)
+ if tok == nil or string.sub(tok, 1, 1) == "#" then
+ break
+ elseif string.sub(tok, 1, 3) ~= "msg" then
+ return error("Invalid token: " .. tok)
+ elseif entry[tok] ~= nil then
+ break
+ else
+ local value, npos = readstring(str, npos)
+ assert(value ~= nil, "No string provided for " .. tok)
+ entry[tok] = value
+ nxt = npos
+ msglines = msglines+1
+ end
+ end
+ if msglines == 0 then
+ return st
+ elseif entry.msgid ~= "" then
+ assert(entry.msgid ~= nil, "Missing untranslated string")
+ assert(entry.msgstr ~= nil, "Missing translated string")
+ table.insert(st, entry)
+ end
+ pos = nxt
+ end
+end
+
+local escape_lookup = {
+ ["="] = "@=",
+ ["\n"] = "@n"
+}
+local function escape_string(st)
+ return (string.gsub(st, "[=\n]", escape_lookup))
+end
+
+local function convert_po_string(textdomain, str)
+ local st = {string.format("# textdomain: %s", textdomain)}
+ for _, entry in ipairs(readpo(str)) do
+ local line = ("%s=%s"):format(escape_string(entry.msgid), escape_string(entry.msgstr))
+ if entry.flags.fuzzy then
+ line = "#" .. line
+ end
+ table.insert(st, line)
+ end
+ return table.concat(st, "\n")
+end
+
+local function convert_po_file(textdomain, inpath, outpath)
+ local f, err = io.open(inpath, "rb")
+ assert(f, err)
+ local str = convert_po_string(textdomain, f:read("*a"))
+ f:close()
+ minetest.safe_file_write(outpath, str)
+ return str
+end
+
+local function convert_flat_po_directory(textdomain, modpath)
+ assert(textdomain, "No textdomain specified for po file conversion")
+ local mp = modpath or minetest.get_modpath(textdomain)
+ assert(mp ~= nil, "No path to write for " .. textdomain)
+ local popath = mp .. "/po"
+ local trpath = mp .. "/locale"
+ for _, infile in pairs(minetest.get_dir_list(popath, false)) do
+ local lang = string.match(infile, [[^([^%.]+)%.po$]])
+ if lang then
+ local inpath = popath .. "/" .. infile
+ local outpath = ("%s/%s.%s.tr"):format(trpath, textdomain, lang)
+ convert_po_file(textdomain, inpath, outpath)
+ end
+ end
+end
+
+return {
+ from_string = convert_po_string,
+ from_file = convert_po_file,
+ from_flat = convert_flat_po_directory,
+}