webmcp

view framework/bin/langtool.lua @ 564:6141ab127861

Fixed handling of derived translation tables
author jbe
date Sun Mar 07 17:21:12 2021 +0100 (2021-03-07)
parents 2c31275322db
children
line source
1 #!/usr/bin/env lua
3 if not pcall(
4 function()
5 extos = require "extos"
6 end
7 ) then
8 io.stderr:write('Could not load library "extos".\n')
9 io.stderr:write('Hint: Set LUA_CPATH="/path_to_extos_library/?.so;;"\n')
10 end
13 local args = {...}
15 if #args == 0 then
16 print()
17 print("This program creates translation files by traversing source directories.")
18 print()
19 print("Two formats are supported: lua files and po files.")
20 print("At runtime a lua file is needed.")
21 print("For use with po-file editors you may want to create po files first though.")
22 print()
23 print("Create or update a lua file: langtool.lua dir1/ dir2/ ... <basename>.lua")
24 print("Create or update a po file: langtool.lua dir1/ dir2/ ... <basename>.po")
25 print("Convert po file to lua file: langtool.lua <basename>.po <basename>.lua")
26 print("Convert lua file to po file: langtool.lua <basename>.lua <basename>.po")
27 print()
28 end
30 local in_filename, in_filetype, out_filename, out_filetype
31 local directories = {}
33 for arg_num, arg in ipairs(args) do
34 local function arg_error(msg)
35 error("Illegal command line argument #" .. arg_num .. ": " .. msg)
36 end
37 local po = string.match(arg, "^po:(.*)$") or string.match(arg, "^(.*%.po)$")
38 local lua = string.match(arg, "^lua:(.*)$") or string.match(arg, "^(.*%.lua)$")
39 local filetype
40 if po and not lua then filetype = "po"
41 filetype = "po"
42 elseif lua and not po then filetype = "lua"
43 filetype = "lua"
44 else
45 filetype = "path"
46 end
47 if filetype == "path" then
48 table.insert(directories, arg)
49 elseif filetype == "po" or filetype == "lua" then
50 if not out_filename then
51 out_filename = arg
52 out_filetype = filetype
53 elseif not in_filename then
54 in_filename = out_filename
55 in_filetype = out_filetype
56 out_filename = arg
57 out_filetype = filetype
58 else
59 arg_error("Only two language files (one input and one output file) can be specified.")
60 end
61 else
62 -- should not happen, as default type is "path"
63 arg_error("Type not recognized")
64 end
65 end
67 if #directories > 0 and not extos.listdir then
68 io.stderr:write('Fatal: Cannot traverse directories without "extos" library -> Abort\n')
69 os.exit(1)
70 end
72 if out_filename and not in_filename then
73 local file = io.open(out_filename, "r")
74 if file then
75 in_filename = out_filename
76 in_filetype = out_filetype
77 file:close()
78 end
79 end
81 local translations = { }
83 local function traverse(path)
84 local filenames = extos.listdir(path)
85 if not filenames then return false end
86 for num, filename in ipairs(filenames) do
87 if not string.find(filename, "^%.") then
88 if string.find(filename, "%.lua$") then
89 for line in io.lines(path .. "/" .. filename) do
90 -- TODO: exact parsing of comments and escape characters
91 for key in string.gmatch(line, "_%(?'([^']+)'") do
92 if
93 key ~= "([^" and
94 (not string.find(key, "^%s*%.%.[^%.]")) and
95 (not string.find(key, "^%s*,[^,]"))
96 then
97 local key = key:gsub("\\n", "\n")
98 translations[key] = false
99 end
100 end
101 for key in string.gmatch(line, '_%(?"([^"]+)"') do
102 if
103 key ~= "([^" and
104 (not string.find(key, "^%s*%.%.[^%.]")) and
105 (not string.find(key, "^%s*,[^,]"))
106 then
107 local key = key:gsub("\\n", "\n")
108 translations[key] = false
109 end
110 end
111 end
112 end
113 traverse(path .. "/" .. filename)
114 end
115 end
116 return true
117 end
118 for num, directory in ipairs(directories) do
119 io.stderr:write('Parsing files in directory "', directory, '".\n')
120 if not traverse(directory) then
121 error('Could not read directory "' .. directory .. '".')
122 end
123 end
125 local updates
127 if in_filetype == "po" then
128 updates = {}
129 io.stderr:write('Reading translations from po file "', in_filename, '".\n')
130 local next_line = io.lines(in_filename)
131 for line in next_line do
132 if not line then break end
133 local key = string.match(line, '^msgid%s*"(.*)"%s*$')
134 if key then
135 local line = next_line()
136 local value = string.match(line, '^msgstr%s*"(.*)"%s*$')
137 if not value then
138 error("Expected msgstr line in po file.")
139 end
140 if translations[key] then
141 error("Duplicate key '" .. key .. "' in po file.")
142 end
143 if value == "" then value = false end
144 updates[key] = value
145 end
146 end
147 elseif in_filetype == "lua" then
148 io.stderr:write('Reading translations from lua file "', in_filename, '".\n')
149 local func
150 if _ENV then
151 func = assert(loadfile(in_filename, "t", {}))
152 else
153 func = assert(loadfile(in_filename))
154 setfenv(func, {})
155 end
156 updates = func()
157 end
159 if updates.__parent then
160 for key in pairs(updates) do
161 if translations[key] == nil and key ~= "__parent" then
162 updates[key] = nil
163 end
164 end
165 translations = updates
166 else
167 for key, value in pairs(updates) do
168 if translations[key] ~= nil or #directories == 0 then
169 translations[key] = value
170 end
171 end
172 end
174 local translation_keys = {}
175 for key in pairs(translations) do
176 table.insert(translation_keys, key)
177 end
178 table.sort(translation_keys)
180 if out_filetype == "po" then
181 io.stderr:write('Writing translations to po file "', out_filename, '".\n')
182 local file = assert(io.open(out_filename, "w"))
183 for num, key in ipairs(translation_keys) do
184 local value = translations[key]
185 file:write('msgid "', key, '"\nmsgstr "', value or "", '"\n\n')
186 end
187 io.close(file)
188 elseif out_filetype == "lua" then
189 io.stderr:write('Writing translations to lua file "', out_filename, '".\n')
190 local file = assert(io.open(out_filename, "w"))
191 file:write("#!/usr/bin/env lua\n", "return {\n")
192 for num, key in ipairs(translation_keys) do
193 local value = translations[key]
194 if value then
195 file:write((string.format("[%q] = %q;\n", key, value):gsub("\\\n", "\\n"))) -- double () important to hide second result of gsub
196 else
197 file:write((string.format("[%q] = false;\n", key):gsub("\\\n", "\\n"))) -- double () important to hide second result of gsub
198 end
199 end
200 file:write("}\n")
201 io.close(file)
202 end

Impressum / About Us