webmcp
view framework/bin/langtool.lua @ 569:5b19007574de
New argument active_link_attr for env.ui.paginate{...}
| author | jbe | 
|---|---|
| date | Wed Oct 13 17:21:44 2021 +0200 (2021-10-13) | 
| parents | 6141ab127861 | 
| 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
