aboutsummaryrefslogtreecommitdiff
path: root/advtrains/poconvert.lua
blob: 74f962ee9eea9db01067588df980a095488969f3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
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,
}