webmcp
view framework/bin/langtool.lua @ 104:290b6b10b160
Bugfix: Removed wrong "r" parameter from io.lines(...) call
author | jbe |
---|---|
date | Fri Oct 19 19:21:49 2012 +0200 (2012-10-19) |
parents | a54cc7dcabf1 |
children | 92d9218140f7 |
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 function update_translation(key, value)
126 if #directories > 0 then
127 if translations[key] ~= nil then translations[key] = value end
128 else
129 translations[key] = value
130 end
131 end
133 if in_filetype == "po" then
134 io.stderr:write('Reading translations from po file "', in_filename, '".\n')
135 local next_line = io.lines(in_filename)
136 for line in next_line do
137 if not line then break end
138 local key = string.match(line, '^msgid%s*"(.*)"%s*$')
139 if key then
140 local line = next_line()
141 local value = string.match(line, '^msgstr%s*"(.*)"%s*$')
142 if not value then
143 error("Expected msgstr line in po file.")
144 end
145 if translations[key] then
146 error("Duplicate key '" .. key .. "' in po file.")
147 end
148 if value == "" then value = false end
149 update_translation(key, value)
150 end
151 end
152 elseif in_filetype == "lua" then
153 io.stderr:write('Reading translations from lua file "', in_filename, '".\n')
154 local func
155 if _ENV then
156 func = assert(loadfile(in_filename, "t", {}))
157 else
158 func = assert(loadfile(in_filename))
159 setfenv(func, {})
160 end
161 local updates = func()
162 for key, value in pairs(updates) do
163 update_translation(key, value)
164 end
165 end
167 local translation_keys = {}
168 for key in pairs(translations) do
169 table.insert(translation_keys, key)
170 end
171 table.sort(translation_keys)
173 if out_filetype == "po" then
174 io.stderr:write('Writing translations to po file "', out_filename, '".\n')
175 local file = assert(io.open(out_filename, "w"))
176 for num, key in ipairs(translation_keys) do
177 local value = translations[key]
178 file:write('msgid "', key, '"\nmsgstr "', value or "", '"\n\n')
179 end
180 io.close(file)
181 elseif out_filetype == "lua" then
182 io.stderr:write('Writing translations to lua file "', out_filename, '".\n')
183 local file = assert(io.open(out_filename, "w"))
184 file:write("#!/usr/bin/env lua\n", "return {\n")
185 for num, key in ipairs(translation_keys) do
186 local value = translations[key]
187 if value then
188 file:write((string.format("[%q] = %q;\n", key, value):gsub("\\\n", "\\n"))) -- double () important to hide second result of gsub
189 else
190 file:write((string.format("[%q] = false;\n", key):gsub("\\\n", "\\n"))) -- double () important to hide second result of gsub
191 end
192 end
193 file:write("}\n")
194 io.close(file)
195 end