diff options
author | Y. Wang <yw05@forksworld.de> | 2023-10-04 20:07:24 +0200 |
---|---|---|
committer | Y. Wang <y5nw@protonmail.com> | 2024-11-04 17:17:51 +0100 |
commit | 425b0993d355b9f45ddd400bd4925f9f1a5bd34d (patch) | |
tree | 77a105e5bbf28c03e31c5c30e5866387d618e174 /advtrains/poconvert.lua | |
parent | bed66e0f901ae9f8e21675035f174292120fea4f (diff) | |
download | advtrains-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.lua | 185 |
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, +} |