Module:Multilang module tools

Documentation for this module may be created at Module:Multilang module tools/doc

local p = {}
--Version 2020-07-10

local SA = require "Module:SimpleArgs"
local SD = require "Module:SimpleDebug"
local WD = require "Module:Wikidades"

----------------------------------------

p.prior_key = ''
function p.ChkFunc (func_name, params)
	--Used in debug of a function, where:
	--params is a parameter table, where each item is {name, value, type}
	  --the type can be any of the following in the table and integer ("i") or non-empty string ("x")  
	  --adding "r", then the parameter is required.
	local types = {
		["b"] = 'boolean',
		["f"] = 'function',
		["n"] = 'number',
		["s"] = 'string', --string with a values or not
		["t"] = 'table',
	}
	for i, par in ipairs(params) do
		local req = false
		if (par[1] == '') or (par[3] == '') then
			error ('Incomplete call to ChkFunc in #'..i..' parameter of '..func_name)
		end	
		if string.len (par[3]) == 2 then
			par[3] = string.sub (par[3], 1, 1)
			req = true
		end	
		local typ = par[3]
		function mess_begin ()
			return 'In a call to "'..func_name..'" parameter #'..i..' (= "'..par[1]..'")'
		end	
		if typ == 'a' then --any
			if req and (par[2] == nil) then
				error (mess_begin ()..' must have a value', 0)
			end	
		elseif typ == 'k' then -- a key = string with a value
			local key = par[2]
			if (key == nil) or (key == '') or (type(key) ~= 'string') then
				if p.prior_key == '' then
					error (mess_begin()..', not set (or correctly typed) a key, perhaps, the call from first key') --Don't translate, for debug
				else	
					error (mess_begin()..', not set (or correctly typed) a key after the key "'..p.prior_key..'", or, perhaps, the call from the first key') --Don't translate, for debug
				end	
			else
				p.prior_key = key
			end	
		elseif typ == 'x' then -- a string with a value
			if par[2] == nil then
				if req then
					error (mess_begin ()..' must have a string', 0)
				end	
			elseif par[2] == "" then
				error (mess_begin ()..' must have a non-empty string', 0)
			end	
		else	
			if typ == 'i' then
				typ = 'n'
			end	
			local t = types[typ]
			function error_mes (mes)
				error (mess_begin ()..' must be a '..t..', but '..mes, 0)
			end	
			if req and (par[2] == nil) then
				error_mes ('not found a value')
			elseif par[2] ~= nil then
				if type(par[2]) ~= t then
					SD.vtos (par[2])
					error_mes ('found '..SD.s)
				elseif par[3] == 'i' then
					if par[2] ~= math.floor(par[2]) then
						t = 'integer number'
						error_mes ('found '..par[2])
					end	
				end	
			end	
		end	
	end	
end -- ChkFunc	

----------------------------------------

local RS = {
	DuplicateArgs = 'DuplicateArgs',
}
local i18n = {
	[RS.DuplicateArgs]= "Duplicate parameters:",
}
i18n = SA.loadI18n ('Multilang module tools', i18n)

p.arg = {
	demo = 'demo',
	item = 'item',
	lang = 'lang',
}
function p.DemoItemLang()
	return {p.arg.demo, p.arg.item, p.arg.lang}
end	

p.k = { --labels to define translatable strings
	Args		= 'Args',	--arguments or parameters
	Labels		= 'Labels',	--text to display usually
	MsgError	= 'MsgError',	--warning or information or error messages contains the next
	Msgs		= 'Msgs',	--warning or information messages
	Errors		= 'Errors',	--error messages
}
SA.OKEmptyVal = false  -- Empty values ("") = nil.
----------------------------------------
--Language functions
----------------------------------------

p.lang = nil -- Used for demos for other languages.
p.compare_lang = 'en'
p.LangIsRTL = nil
local LangsDifDirect = false
local WDirect = 'ltr'

function p.SetLang (args)
	local lang_now = args[p.arg.lang]
	if lang_now ~= nil then
		SA.lang_to_use = SA.get_lang (lang_now)
	end	
end --SetLang

function p.LangsDifWriteDirect ()
	return LangsDifDirect
end

function p.WriteDirect ()
	return WDirect
end

local function _IsRTL (lang)
	return mw.getLanguage(lang):getDir() == 'rtl'
end

function p.IsRTL (frame)
	local args, NArgs = SA.GetArgs (frame)
	local lang = SA.RStr_Par (args, 1)
	return _IsRTL (lang)
end --IsRTL

function p.IniLang (frame, args)
	p.lang = SA.Str_Par (args, p.arg.lang)
	local CurrLang = mw.language.getContentLanguage().code
	if p.lang == nil or (p.lang == CurrLang) then
		p.LangIsRTL = _IsRTL (CurrLang)
	elseif (p.lang ~= nil) and (p.lang ~= CurrLang) then
		p.LangIsRTL = _IsRTL (p.lang)
		local ContentLangIsRTL = _IsRTL (CurrLang)
		if p.LangIsRTL then
			WDirect = 'rtl'
		end	
		LangsDifDirect = p.LangIsRTL ~= ContentLangIsRTL
	end
end --IniLang

----------------------------------------
local error_t = {}
local dupli_args = {}
----------------------------------------

function p.SendErrors (errors)
    if errors ~= nil then
    	for _, i in ipairs(errors) do
    		table.insert (error_t, i)
    	end	
    end	
end --SendErrors

function p.CheckParams (args, ArgsPoss, OtherArgs, SetArgs, ArgNamesIn1)
	local errors, dupli
	if SetArgs then
		ArgsPoss2 = {}
		for i, v in ipairs(ArgsPoss) do
			if ArgsPoss[i][p.k.Args] == nil then
				SD.vtos (ArgsPoss)
				error ('Expected table without "'..p.k.Args..'":</br>'..SD.s)
			end	
			table.insert (ArgsPoss2, ArgsPoss[i][p.k.Args])
		end
		errors, dupli = SA.CheckParamsM (args, ArgsPoss2, OtherArgs, ArgNamesIn1)
	else	
		errors, dupli = SA.CheckParamsM (args, ArgsPoss, OtherArgs, ArgNamesIn1)
	end	
	if SA.Error.handle then
		p.SendErrors (errors)
	    if dupli ~= nil then
	    	for _, i in ipairs(dupli) do
	    		table.insert (dupli_args, i)
	    	end	
	    end
    end
end	--CheckParams

----------------------------------------

function p.MN (ModuleName)
	return '[[Module:'..ModuleName..']]'
end

function p.MNi18n (ModuleName)
	return p.MN (ModuleName..'/i18n')
end	

function p.MNi (ModuleName)
	return p.MN (ModuleName..'/items')
end	

----------------------------------------

local function exists_in_tab_main (ModuleName, key, tab_main, what)
	if tab_main[key] == nil then
		error ('Not found "'..key..'" in items table (of '..p.MN(ModuleName)..'), as '..what, 0) --for debug, don't translate
	end	
end --exists_in_tab_main

local function correct_tab_main (ModuleName, key, tab_main, n, for_what)
	if (n == 1) and (type(tab_main[key]) == 'table') then
		SD.vtos (tab_main[key])
		error ('Invalid items table (from '..p.MN(ModuleName)..') for key "'..key..'". No tables allowed'..for_what..' (now = '..SD.s..')', 0) 
	elseif (n == 2) and (type(tab_main[key]) ~= 'table')	then
		error ('Invalid items table (from '..p.MN(ModuleName)..') for key "'..key..'". Table is required'..for_what..' (only = '..tab_main[key]..')', 0) 
	end
end --correct_tab_main

local function exists_in_tab_main_1_to_1or2 (ModuleName, key, tab_main, tab_lang, n) --for debug, don't translate
	local for_what = ' for a label or message!'
	exists_in_tab_main (ModuleName, key, tab_main, 'label or message key')
	if type(tab_lang[key]) == 'table' then
		SD.vtos (tab_lang[key])
		error ('Invalid items table (from '..p.MNi18n(ModuleName)..') for key '..key..'. No tables allowed'..for_what..' (now = '..SD.s..')', 0) 
	else
		correct_tab_main (ModuleName, key, tab_main, n, for_what)
	end	
end	--exists_in_tab_main_1_to_1or2

local function exists_in_tab_main_1or2 (ModuleName, key, tab_main, n) --for debug, don't translate
	exists_in_tab_main (ModuleName, key, tab_main, 'parameter key')
end

local function tableMergeReplace (ModuleName, tab_main, tab_lang) 
	for k, v in pairs(tab_lang) do
		exists_in_tab_main_1_to_1or2 (ModuleName, k, tab_main, tab_lang, 1)
		if v ~= '' then
			tab_main[k] = v
		end	
	end
	return tab_main
end --tableMergeReplace

local function tableMergeReplace1 (ModuleName, tab_main, tab_lang) 
	for k, v in pairs(tab_lang) do
		exists_in_tab_main_1_to_1or2 (ModuleName, k, tab_main, tab_lang, 2)
		if v ~= '' then
			tab_main[k][1] = v
		end	
	end
	return tab_main
end --tableMergeReplace1

local function tableMergeAppend (ModuleName, tab_main, tab_lang)
	for k, v in pairs(tab_lang) do
		exists_in_tab_main_1or2 (ModuleName, k, tab_main, 1)	
		if v == '' then
		else
			found = {}
			local tab = {}
			if type(v) == "table" then
				tab = v
				for kk, vv in ipairs(v) do
					found[vv] = 0
				end	
			else
				table.insert (tab, v)
				found[v] = 0
			end
			local n = tab_main[k]
			function perhapsAdd (s)
				if found[s] == nil then
					found[s] = 0
					table.insert (tab, s)
				end	
			end
			if type(n) == "table" then
				for _,vv in pairs(n) do
					perhapsAdd (vv)
				end
			else
				perhapsAdd (n)
			end 
			tab_main[k] = tab
		end
	end
	return tab_main
end --tableMergeAppend

local function tableMergeAppend1 (ModuleName, tab_main, tab_lang)
	for k, v in pairs(tab_lang) do
		exists_in_tab_main_1or2 (ModuleName, k, tab_main, 2)	
		if v == '' then
		else
			found = {}
			local tab = {}
			if type(v) == "table" then
				tab = v
				for kk, vv in ipairs(v) do
					found[vv] = 0
				end	
			else
				table.insert (tab, v)
				found[v] = 0
			end
			local n = tab_main[k][1]
			function perhapsAdd (s)
				if found[s] == nil then
					found[s] = 0
					table.insert (tab, s)
				end	
			end
			if type(n) == "table" then
				for _,vv in pairs(n) do
					perhapsAdd (vv)
				end
			else
				perhapsAdd (n)
			end
			tab_main[k][1] = tab
		end
	end
	return tab_main
end --tableMergeAppend1

function p.tableMerge (ModuleName, tab_main, tab_lang, TargetFirst)
	if p.lang == nil then
		local z1 = SA.deep_copy_table (tab_main)
		local z2 = SA.deep_copy_table (tab_lang)
		if z1[p.k.Args][SA.HasChild] then
			for x, v in pairs(tab_lang[p.k.Args]) do
				if x ~= SA.HasChild then
					if TargetFirst == true then
						z1[p.k.Args][x] = tableMergeAppend1 (ModuleName, z1[p.k.Args][x], z2[p.k.Args][x])
					else	
						z1[p.k.Args][x] = tableMergeAppend (ModuleName, z1[p.k.Args][x], z2[p.k.Args][x])
					end	
				end	
			end	
		else
			if TargetFirst == true then
				z1[p.k.Args] = tableMergeAppend1 (ModuleName, z1[p.k.Args], z2[p.k.Args])
			else
				z1[p.k.Args] = tableMergeAppend (ModuleName, z1[p.k.Args], z2[p.k.Args])
			end	
		end	
		for _, wd in pairs(p.k) do  --Labels, Msgs and Errors
			if wd ~= p.k.Args then
				if (z1[wd] ~= nil) and (z2[wd] ~= nil) then
					if wd == p.k.MsgError then
						for _, v in pairs(tab_lang[p.k.MsgError]) do
							z1[p.k.MsgError][v] = tableMergeReplace (ModuleName, z1[p.k.MsgError][v], z2[p.k.MsgError][v])
						end	
					end
					z1[wd] = tableMergeReplace (ModuleName, z1[wd], z2[wd])
					--[[]
					if (wd == p.k.Labels) and (TargetFirst == true) then
						z1[wd] = tableMergeReplace1 (ModuleName, z1[wd], z2[wd])
					else	
						z1[wd] = tableMergeReplace (ModuleName, z1[wd], z2[wd])
					end	
					--]]
				end
			end	
		end
		return z1
	else
		return tab_main
	end	
end --tableMerge

function p.SetAttribToArgs (tab, tab_lims) 
	local tab2
	function GetArgType (key)
		if key == nil then
			return nil
		end	
		if tab2 ~= nil then
			for iv, v in ipairs(tab2) do
				if v[1] == key then
					table.remove (tab2, iv)
					return v[2]
				end	
			end	
		end	
		return p.IT.s
	end --GetArgType
	
	function For1(tab_h)
		local Args = {}
		for j, k in pairs(tab_h) do
			local item = {k}
			table.insert (item, GetArgType(j))
			Args[j] = item
		end
		return Args
	end
	
	local new_tab = {}
	if tab[p.k.Args][SA.HasChild] == nil then
		tab2 = SA.deep_copy_table (tab_lims)
		local Args = For1(tab[p.k.Args])
		new_tab[p.k.Args] = Args
	else
		local Args = {}
		for j, k in pairs(tab[p.k.Args]) do
			if j == SA.HasChild then
				Args[SA.HasChild] = true -- = Args[j] = k
			else	
				tab2 = SA.deep_copy_table (tab_lims[j])
				Args[j] = For1(k)
			end	
		end
		new_tab[p.k.Args] = Args
	end	
	new_tab[p.k.Labels] = tab[p.k.Labels]
	new_tab[p.k.Msgs] = tab[p.k.Msgs]
	new_tab[p.k.Errors] = tab[p.k.Errors]
	return new_tab
end	--SetAttribToArgs

function p.SetAttribToArgsGr (tab, tab_lims)
--Warning: not to be confused with "Multilang infotable tools.SetAttribToArgsGr"
	local new_tab = {}
	for j, k in pairs(tab) do
		new_tab[j] = p.SetAttribToArgs (tab[j], tab_lims[j])
	end
	return new_tab
end	--SetAttribToArgsGr

----------------------------------------

local function CheckKeyInTab (tab, key) --Only for debug
	if tab[p.k.Labels] == nil then
		error ('Table not has "'..p.k.Labels..'"')
	elseif tab[p.k.Labels][key] == nil then
		error ('Not found in table "'..p.k.Labels..'" the key "'..key..'"')
	end	
end --CheckKeyInTab

function p.GetLabelL (tab, key, lang)
	p.ChkFunc ("GetLabelL", {{"tab",tab,"tr"},{"key",key,"k"},{"lang",lang,"s"},})
	CheckKeyInTab (tab, key)
	key = SA.CheckIsStr (tab[p.k.Labels][key], key)
	if string.sub(key,1,1) == '_' then  --uses properties (_Pn) or qualifiers (_Qn) from Wikidata to define labels
		key = string.sub(key,2)
		return WD.getLabel({key, ['lang']=lang}), key
	else	
		return key, ''
	end	
end	--GetLabelL

function p.GetLabel (tab, key)
	return p.GetLabelL (tab, key, p.lang)
end	

function p.GetLabelX (mod_abbrev, key, tab_main, tab_lang)
	local wd_main, lnk = p.GetLabelL (tab_main, key, p.compare_lang)
	local wd_lang = tab_lang[p.k.Labels][key]
	return mod_abbrev, key, wd_main, wd_lang, lnk
end	--GetLabelX

function p.GetLabelL3 (tab, key, lang)
	p.ChkFunc ("GetLabelL3", {{"tab",tab,"tr"},{"key",key,"k"},{"lang",lang,"s"},})
	local val = tab[p.k.Labels][key]
	if val == nil then
		return '', ''
	else	
		key = SA.CheckIsStr (tab[p.k.Labels][key], key)
		if string.sub(key,1,1) == '_' then  --uses properties (_Pn) or qualifiers (_Qn) from Wikidata to define labels
			key = string.sub(key,2)
			return WD.getLabel({key, ['lang']=lang}), key
		else	
			return key, ''
		end	
	end	
end	--GetLabelL3

function p.GetLabelL2 (wd_main_l, key)
    local key = SA.CheckIsStr (wd_main_l, key)
    if string.sub(key,1,1) == '_' then  --uses properties (_Pn) or qualifiers (_Qn) from Wikidata to define labels
        key = string.sub(key,2)
        return WD.getLabel({key, ['lang']=SA.lang_to_use}), key
    else	
        return key, ''
    end	
end	--GetLabelL2

function p.GetMsgErrorX (option, mod_abbrev, key, tab_main, tab_lang)
	p.ChkFunc ("GetMsgErrorX", {{"option",option,"xr"},{"mod_abbrev",mod_abbrev,"xr"},
		{"key",key,"k"},{"tab_main",tab_main,"tr"},{"tab_lang",tab_lang,"tr"},})
	local wd_main = tab_main[option][key]
	local wd_lang = tab_lang[option][key]
	return mod_abbrev, wd_main, wd_lang
end	--GetMsgError

----------------------------------------

function p.IsReserv (key)
	p.ChkFunc ("IsReserv", {{"key",key,"xr"},})
	return string.sub (key, 1, 3) == 'rs_' 
end

function p.GetRequired (key, tab)
	if tab ~= nil then
		for _, v in ipairs(tab) do
			if v == key then
				return true
			end	
		end	
	end	
	return false
end --GetRequired

function p.GetRequiredGrp (grp, key, tab)
	return p.GetRequired (key, tab[grp])
end	

p.IT = { --argument types
	s		= 's',	--string
	i		= 'i',	--integer
	ipos	= 'i+',	--positive integer
	n		= 'n',	--number
	npos	= 'n+',	--positive number
	d		= 'd',	--date
	b		= 'b',	--boolean
	sz		= 'sz',	--size
	a		= 'a',	--array
}

local function AlreadyExists (tab, key)
--It cheks if a "key" already exist in tab['Args'] 	
    for _, v in pairs(tab[p.k.Args]) do
        if type(v[1]) == 'table' then
            for _, j in ipairs(v[1]) do
                if j == key then
                    return true
                end
            end
        elseif v[1] == key then
            return true
        end
    end
    return false
end --AlreadyExists

function p.split_item_type_req_vals (item_type_req)
	if item_type_req == nil then
		return p.IT.s, false, nil
	else	
		local item_type = item_type_req
		local values = nil
		if type(item_type) == 'table' then  --when item_type = {'a', {'AM',PM'}} or {'i', {1,10}}
			values = item_type[2] 
			item_type = item_type[1] 
		end	
		if item_type == nil then
			SD.vtos (item_type_req)
			error (SD.s)
		end	
		local required = string.sub (item_type,-1) == 'r'
		if required then
			item_type = string.sub (item_type, 1, string.len(item_type)-1)
		end	
		return item_type, required, values
	end	
end --split_item_type_req_vals

function p.split_item_type_req_vals2 (lims, key)
	if lims == nil then
		return p.IT.s, false, nil
	else
		return p.split_item_type_req_vals (lims[key])
	end
end --split_item_type_req_vals2	

function p.split_item_type_req_vals_forList (item_type_req)
	item_type, required, values = p.split_item_type_req_vals (item_type_req)
	if values ~= nil  then
		if item_type == p.IT.a then
			sep = ', '
		else
			sep = '-'
		end	
		values = '<small>('..table.concat (values, sep)..')</small>'
	end		
	return item_type, required, values
end --split_item_type_req_vals_forList

function p.item_type_req_vals_prep (item_type_req)
	item_type, required, values = p.split_item_type_req_vals_forList (item_type_req)
	return p.IT_required_vals (item_type, required, values)
end

function p.split_item_type_req (item_type_req)
	local item_type = item_type_req
	if type(item_type) == 'table' then  --when item_type = {'a', {'AM',PM'}} or {'i', {1,10}}
		item_type = item_type[1]
	end	
	local required = string.sub (item_type,-1) == 'r'
	if required then
		item_type = string.sub (item_type, 1, string.len(item_type)-1)
	end	
	return item_type, required
end --split_item_type_req

function p.InvalidType (type_req)
--It cheks if "type_req" is valid
--if i.g. type_req=='i+r' first it gets 'i+', and second it checks if 'i+' is in "p.IT"
	type_req = p.split_item_type_req (type_req)
    for _, i in pairs(p.IT) do
        if i == type_req then
            return false -- = is valid type
        end
    end
    return true
end --InvalidType

function p.ParamVal (args, ParId, arg_type, required, DefVal, params)
--it read a parameter "ParId" (i.g. {'name1',Name1}) of an "arg_type" (according "p.IT") and "required"
--if the parameter isn't found a defaul values is returned
--if there is an error, it is added to the "error_t"
	val = nil
	if required == true then
		if (arg_type == p.IT.s) or (arg_type == p.IT.d) then	
			val = SA.RStr_Par (args, ParId)
		elseif arg_type == p.IT.i then
			if params == nil then
				val = SA.RInt_Par (args, ParId)
			else
				val = SA.RInt_Par (args, ParId, params[1], params[2])
			end	
		elseif arg_type == p.IT.ipos then
			val = SA.RPosInt_Par (args, ParId)
		elseif arg_type == p.IT.n then	
			if params == nil then
				val = SA.RNum_Par (args, ParId)
			else
				val = SA.RNum_Par (args, ParId, params[1], params[2])
			end	
		elseif arg_type == p.IT.npos then	
			val = SA.RPosNum_Par (args, ParId)
		elseif arg_type == p.IT.b then	
			val = SA.RBool_Par (args, ParId)
		elseif arg_type == p.IT.sz then	
			val = SA.RSize_Par (args, ParId)
		elseif arg_type == p.IT.a then	
			val = SA.RStrChkTab_Par (args, ParId, params)
		end	
	else
		if (arg_type == p.IT.s) or (arg_type == p.IT.d) then	
			val = SA.Str_Par (args, ParId, DefVal)
		elseif arg_type == p.IT.i then
			if params == nil then
				val = SA.Int_Par (args, ParId)
			else
				val = SA.Int_Par (args, ParId, nil, params[1], params[2])
			end	
		elseif arg_type == p.IT.ipos then
			val = SA.PosInt_Par (args, ParId, DefVal)
		elseif arg_type == p.IT.n then
			if params == nil then
				val = SA.Num_Par (args, ParId, DefVal)
			else
				val = SA.Num_Par (args, ParId, nil, params[1], params[2])
			end	
		elseif arg_type == p.IT.npos then
			val = SA.PosNum_Par (args, ParId, DefVal)
		elseif arg_type == p.IT.b then	
			val = SA.Bool_Par (args, ParId, DefVal)
		elseif arg_type == p.IT.sz then	
			val = SA.Size_Par (args, ParId, DefVal)
		elseif arg_type == p.IT.a then	
			val = SA.StrChkTab_Par (args, ParId, params)
		end	
	end	
	if SA.Error.yes then
		table.insert (error_t, SA.GetAndClearError())
	end 
	return val
end --ParamVal

function p.ErrorMsgs (frame, args, tab)
--if (exists parameter errors) and (in Show Preview mode of infotable)
--it prepare the messages to display them at infobox table
	local res_2 = nil
	if ((#error_t > 0) or (#dupli_args > 0)) and (frame:preprocess( "{{REVISIONID}}" ) == "") then
		local error_str = table.concat (error_t, '</br>')
		local dupli_str = ''
	    if #dupli_args > 0 then
	    	dupli_str = i18n[RS.DuplicateArgs]..' '..table.concat (dupli_args, ', ')
	    end
		local res = {}
	    if error_str ~= '' then
	    	table.insert (res, error_str)
	    end
	    if dupli_str ~= '' then
	    	table.insert (res, dupli_str)
	    end
	    res_2 =  table.concat (res, '</br>')
	end
	return res_2, #error_t > 0, #dupli_args > 0
end	--ErrorMsgs

----------------------------------------
-- List options and functions 
----------------------------------------

local AT_TD = { --TemplateData strings according argument types
	[p.IT.s] = 'line',
	[p.IT.n] = 'number',
	[p.IT.d] = 'date',
	[p.IT.b] = 'boolean',
}

p.AT_Read = { --Displayed argument types
	[p.IT.s]	= 'string',
	[p.IT.i]	= 'integer',
	[p.IT.ipos] = 'positive integer',
	[p.IT.n]	= 'number',
	[p.IT.npos] = 'positive number',
	[p.IT.d]	= 'date (=string)',
	[p.IT.b]	= 'boolean',
	[p.IT.sz]	= 'size',
	[p.IT.a]	= 'array',
}

local function ConvToTemplateDate (arg)
	-- return a valid type for TemplateData
	if (arg == p.IT.i) or (arg == p.IT.ipos) or (arg == p.IT.npos) then
		return p.IT.n
	elseif (arg == p.IT.sz) or (arg == p.IT.a) then
		return p.IT.s
	else
		return arg
	end	
end --ConvToTemplateDate

local all_items = "allitems"
p.LOpt = { --Display list options, id p.l
	list		= 'list',
	params		= 'params',
	template	= 'template', --TemplateData
	labels		= 'labels',
	msgs		= 'msgs',
	errors		= 'errors',
}

p.with_pos_col = false --if true displays a position column.
p.uses_QP_for_labels = false --if true displays in labels list a column with Qualifiers or Properties of Wikidata for labels
p.with_arg_prop = false --if true displays in labels list a last columns with used properties of Wikidata for load the values.  

function p.all_items_opt (args, opt)
	return SA.StrChkTab_Par (args, all_items, opt)
end

p.tab_items = {}

local arg_types_used = {}
p.mod_used = {} --modules used, each item: {id, name}
local mod_used_in_class = {} --modules used in arguments or label lists

function p.add_used (tab, wd)
	if wd ~= '' then
		local found = false
		for _, i in ipairs(tab) do
			if i == wd then
				found = true
				break
			end	
		end
		if not found then
			table.insert (tab, wd)
		end
	end	
	return tab
end	--p.add_used

function p.add_mod_used (abbrev, name)
	table.insert (p.mod_used, {abbrev, name})
end

function p.add_arg_types_used (item_type)
	arg_types_used = p.add_used (arg_types_used, item_type)
end

function p.add_mod_used_in_class (item_type)
	mod_used_in_class = p.add_used (mod_used_in_class, item_type)
end

local function perhaps_mod_used ()
	function lnk (m)
		return ' [[Module:'..m[2]..'/i18n]]'
	end	
	if #p.mod_used > 0 then
		local S = 'You can change the localizated text in:'
		if #p.mod_used == 1 then
			S = S..lnk (p.mod_used[1])
		else	
			S = S..'<ul>'
			for _, m in ipairs(p.mod_used) do
				found = false
				for _, m2 in ipairs(mod_used_in_class) do
					if m[1] == m2 then
						found = true
						break
					end	
				end
				if found then
					S = S..'<li>'..m[1]..':'..lnk(m)..'</li>'
				end	
			end
			S = S..'</ul>'
		end
		table.insert (p.tab_items, S)
	end	
end --perhaps_mod_used	

function p.begin_headers ()
	local ini = ''
	if #p.mod_used > 1 then
		ini = '<th>M.</th>'
	end	
	if p.with_pos_col then
		ini = ini..'<th>P.</th>'
	end	
	return ini..'<th>Key</th>'
end --p.begin_headers

function p.append_mod_item (mod_abbrev, res)
	if #p.mod_used > 1 then
		return '<td>'..mod_abbrev..'</td>'..res
	else
		return res
	end	
end --p.append_mod_item

function p.ins_tab_row (S)
	table.insert (p.tab_items, '<tr>'..S..'</tr>')
end

local pos_for_item = 1
function p.add_pos (res)
	if p.with_pos_col then
		res = '<td>'..pos_for_item..'</td>'
	end
	pos_for_item = pos_for_item + 1
	return res
end --p.add_pos

local function begin_foot (in_labels)
    table.insert (p.tab_items, '</table>')
	res = {}
	if #p.mod_used > 1 then
		table.insert (res, 'M.: Abbreviation of the used module/s')
	end	
	if p.with_pos_col then
		table.insert (res, 'P.: Position according to the appearance order in the infobox')
	end
	if in_labels and p.uses_QP_for_labels then
		table.insert (res, 'WD: Wikidata')
	end	
	if #res > 0 then
		table.insert (p.tab_items, table.concat (res, '. ')..'</br>')
	end
end	--begin_foot

local has_required = false
function p.IT_required_vals (item_type, required, vals)
    if (item_type ~= '') and required then
        item_type = item_type..'-R'
        has_required = true
    end
    if SA.HasValue (vals) then
    	item_type = item_type..' '..vals
    end	
    return item_type
end --p.IT_required_vals

function p.IsHeader (key)
	p.ChkFunc ("IsHeader", {{"key",key,"xr"},})
	if string.find (key,'hd_') == 1 then
		return true
	else
		return false
	end	
end	--IsHeader

function p.PerhapsDsplKeyMode (key)    
    if p.IsHeader (key) then
    	return '<b>'..key..'</b>'
    else
    	return key
    end	
end	--PerhapsDsplKeyMode

local tab = '&#9;'
local function add_arg_item (option, mod_abbrev, key, wd_main, wd_lang, prop, item_type_req)
	if p.IsReserv (key) then return end
	if key == nil then
		error ('Not set key') --for debug
	end	
	if wd_main == nil then
		error ('Not wd_main for key = "'..key..'"') --for debug
	end	
	if wd_lang == nil then
		wd_lang = ''
	end	
	local item_type, required, vals = p.split_item_type_req_vals_forList (item_type_req)	
	if option == p.LOpt.params then
		function concat_wd (A)
			if type(A) == 'table' then
				for i, j in pairs(A) do
					if type(j) == 'table' then
						SD.vtos (j)
						error ('Error in table design and key = "'..key..'":'..SD.s..' must be a string') --for debug
					end	
				end	
				A = table.concat (A, ', ')
			end	
			if A == nil then
				error ('Not set variable for key = "'..key..'"') --for debug
			end	
			return A
		end
		p.add_mod_used_in_class (mod_abbrev)
		local row = ''
		row = p.add_pos (row)
		row = row..'<td>'..p.PerhapsDsplKeyMode(key)..'</td><td>'..concat_wd(wd_main)..'</td><td>'..concat_wd(wd_lang)..'</td>'
		if p.with_arg_prop then
			if SA.HasValue (prop) then 
				if prop == true then
					row = row..'<td>true</td><td></td>'
				elseif prop == false then	
					row = row..'<td>false</td><td></td>'
				elseif type(prop) == 'table' then
					local props = prop["property"]
					if props == nil then
						--Prop contains a pattern to format a manual value
						row = row..'<td></td><td></td>'
					else	
						local sep = ''
						if string.find(props, ' OR ') then
							sep = ' OR '
							props = mw.text.split (props,sep)
							sep = '<small>'..sep..'</small>'
						elseif string.find(props, '/') then	
							sep = '/'
							props = mw.text.split (props,sep)
						end	
						if type(props) == 'table' then
							local trans = {}
							for k, w in ipairs(props) do
								props[k] = '[[:wikidata:Property:'..w..'|'..w..']]'
								local wd = WD.getLabel({w, ['lang']=p.lang})
								if wd ~= nil then
									table.insert (trans, wd)
								end	
							end	
							row = row..'<td><span style="font-size:50%">WD </span>'..table.concat(props,sep)..'</td><td>'..table.concat(trans,',&#10;')..'</td>'
						else
							local wd = WD.getLabel({props, ['lang']=p.lang})
							row = row..'<td><span style="font-size:50%">WD </span>'..props..'</td><td>'..wd..'</td>'
						end	
					end	
				elseif string.sub (prop,1,1) == '_' then
					local props = mw.text.split(string.sub (prop,2), '-')
					local trans = {}
					for k, w in ipairs(props) do
						if string.sub (w,1,1) == 'Q' then 
							props[k] = '[[:wikidata:'..w..'|'..w..']]'
						else
							props[k] = '[[:wikidata:Property:'..w..'|'..w..']]'
						end	
						local wd = WD.getLabel({w, ['lang']=p.lang})
						if wd ~= nil then
							table.insert (trans, wd)
						end	
					end	
					row = row..'<td><span style="font-size:50%">WD </span>'..table.concat(props,',')..'</td><td>'..table.concat(trans,',&#10;')..'</td>'
				elseif string.sub (prop,1,5) == 'Color:' then
					local color = string.sub (prop,6)
					row = row..'<td><span style="background-color:'..color..'">'..color..'</span></td><td></td>'
				else	
					row = row..'<td>'..prop..'</td><td></td>'
				end	
			else	
				row = row..'<td></td><td></td>'
			end	
		end		
		row = p.append_mod_item (mod_abbrev, row)
        p.add_arg_types_used (item_type)
        item_type = p.IT_required_vals (item_type, required, vals)
		p.ins_tab_row (row..'<td>'..item_type..'</td>')
	elseif option == p.LOpt.template then
		item_type = tab..tab..tab..'"type": "'..AT_TD[ConvToTemplateDate(item_type)]..'"'
		local wd = ''
		local alias = {}
		function get_wd_alias (A)
			if type(A) == 'table' then
				wd = A[1]
				for i = 2, #A do
					table.insert (alias, A[i])
				end
			else
				wd = A
			end	
		end	
		if wd_lang == '' then
			get_wd_alias (wd_main)
		else
			get_wd_alias (wd_lang)
			if type(wd_main) == 'table' then
				for _, w in ipairs(wd_main) do
					table.insert (alias, w)
				end
			else
				table.insert (alias, wd_main)
			end	
		end
		if #alias > 0 then
			table.insert (p.tab_items, tab..tab..'"'..wd..'": {')
			table.insert (p.tab_items, tab..tab..tab..'"aliases": [')
			function PresAlias (S)
				local N = tonumber(S)
				if not N then
					return '"'..S..'"'
				else
					return '["'..S..'"]'
				end	
			end	
			for i = 1, (#alias-1) do
				table.insert (p.tab_items, tab..tab..tab..tab..PresAlias(alias[i])..',')
			end	
			table.insert (p.tab_items, tab..tab..tab..tab..PresAlias (alias[#alias]))
			table.insert (p.tab_items, tab..tab..tab..'],')
		else	
			table.insert (p.tab_items, tab..tab..'"'..wd..'": {')
		end	
		if required then
			item_type = item_type..','
		end	
		table.insert (p.tab_items, item_type)
		if required then
			table.insert (p.tab_items, tab..tab..tab..'"required": true')
		end	
		table.insert (p.tab_items, tab..tab..'},')
	end	
end --add_arg_item	

function p.GetITRFromLims (lims, key)
	if lims == nil then
		return p.IT.s
	else
		return lims[key]
	end	
end --GetITRFromLims

function p.GetITRFromLimsGr (lims, grp, key)
	if lims[grp] == nil then
		return p.IT.s
	else
		return lims[grp][key]
	end	
end --GetITRFromLimsGr

function p.add_arg_item (option, mod_abbrev, key, wd_main, wd_lang, lims)
	local prop = nil
	local item_type_req = p.GetITRFromLims (lims, key)
	if p.with_arg_prop then
		prop = wd_main[p.k.Args][key][2]
		wd_main = wd_main[p.k.Args][key][1]
	else	
		wd_main = wd_main[p.k.Args][key][1]
	end	
	wd_lang = wd_lang[p.k.Args][key]
	add_arg_item (option, mod_abbrev, key, wd_main, wd_lang, prop, item_type_req)
end --add_arg_item

function p.add_arg_item_grp (option, mod_abbrev, grp, key, wd_main, wd_lang, lims)
	local prop = nil
	local item_type_req = p.GetITRFromLims (lims, grp, key)
	if p.with_arg_prop then
		prop = wd_main[p.k.Args][grp][key][2]
		wd_main = wd_main[p.k.Args][grp][key][1]
	else	
		wd_main = wd_main[p.k.Args][grp][key][1]
	end	
	wd_lang = wd_lang[p.k.Args][grp][key]
	add_arg_item (option, mod_abbrev, grp..'.'..key, wd_main, wd_lang, prop, item_type_req)
end --add_arg_item_grp

local function CheckIdx0 (tab)
	if tab == nil then
		error ('List not assigned') --Don't translate, for debug
	elseif not SA.TableWithItems(tab) then
		error ('List without items') --Don't translate, for debug
	end	
end --CheckIdx0	

function p.CheckIdx (tab)
	--checks that table of {'a','b'..} is assigned, contents items.
	CheckIdx0 (tab)
	local s = #tab
	local pos = 0
	local old_val = "" 
	for _, key in ipairs(tab) do
		pos = pos + 1
		old_val = key
	end
	if pos ~= #tab then
		error ('List error in the key after key "'..old_val..'"') --Don't translate, for debug
	end	
end	--CheckIdx

function p.CheckLims0 (ModuleName, tab)
	local s = #tab
	for key, lims in pairs(tab) do
		local found = false
		local tt
		local k
		for t, i in pairs(p.IT) do
			k = lims
			if type(k) == 'table' then
				k = k[1]
			end	
			if i == k then
				found = true
				tt = t
				break
			end	
		end	
		function key_lims ()
			return '"'..key..'" (from '..ModuleName..')'
		end	
		if not found then
			error ('Invalid argument type ("'..k..'") for '..key_lims())
		end	
		if type(lims) == 'table' then
			function CheckKeyN ()
				if #lims ~= 2 then
					error ('Two parameters (now '..#lims..') are required for '..key_lims())
				end	
			end
			if lims[1] == 'a' then --array
				CheckKeyN ()
				local count = 0
				for _,i in ipairs(lims[2]) do
					if type(i) ~= 'string' then
						SD.vtos (i)
						error ('An array item ('..SD.s..') is not a string. For '..key_lims())
					end	
					count = count + 1
				end	
				if count < 2 then
					error ('More than 1 items ara required for '..lims[1]..' '..key_lims())
				end	
			else --numeric with limits	
				function ParFor (n)
					return 'Parameter for '..lims[1]..' '..key_lims()
				end	
				function CheckOrder ()
					if tonumber(lims[2][1]) > tonumber(lims[2][2]) then
						error ('Invalid order for limits for '..key_lims())
					end	
				end	
				if tt == p.IT.i then
					CheckKeyN ()
					SA.CheckSIsInt (lims[2][1], ParFor(2))
					SA.CheckSIsInt (lims[2][2], ParFor(3))
					CheckOrder ()
				elseif tt == p.IT.n then
					CheckKeyN ()
					SA.CheckSIsNum (lims[2][1], ParFor(2))
					SA.CheckSIsNum (lims[2][2], ParFor(3))
					CheckOrder ()
				else	
					error ('No parameters allowed for argument type "'..tt..'" for '..key_lims())
				end
			end	
		end	
	end
end	--CheckLims0

function p.CheckLims (ModuleName, tab)
	p.CheckLims0 (ModuleName, tab)
end 

function p.foot_for_args ()
    begin_foot (false)
    perhaps_mod_used ()
    if (#arg_types_used > 1) or has_required then
        local item_types = {}
        for _, i in ipairs(arg_types_used) do
            table.insert (item_types, i..' ('..p.AT_Read[i]..')')
        end
        local row = 'Used argument types: '..table.concat(item_types,', ')
        if has_required then
            row = row..'. "-R" indicates required arguments.'
        end
        table.insert (p.tab_items, row)
    end	
    return  table.concat (p.tab_items, '')
end --foot_for_args    

function p.show_arg_items (option, idx, func, tab_modname_func)
	if option == p.LOpt.template then
		table.insert (p.tab_items, '<pre>')
		table.insert (p.tab_items, '<templatedata>')
		table.insert (p.tab_items, '{')
		table.insert (p.tab_items, tab..'"params": {')
	else
		table.insert (p.tab_items, '<table class="wikitable sortable">')
		row = p.begin_headers()..'<th>Default names</th><th>Localized names (i18n)</th>'
		if p.with_arg_prop then
			row = row..'<th>Values</th><th>WD content ('..SA.lang_to_use..')</th>'
		end	
		p.ins_tab_row (row..'<th>T.</th>')
	end	
	p.CheckIdx (idx)
	function WhenMod (k)
		for _, j in ipairs (tab_modname_func) do
			if (j[1] == '') or (j[1] == k) then
				j[2] (option, k)
				break
			end	
		end	
	end --WhenMod
	function WhenMain (k)
		local mod_abbrev, wd_main, wd_lang, lims = func (k)
		p.add_arg_item (option, mod_abbrev, k, wd_main, wd_lang, lims)
	end	
	function WhenModGr (grp, k)
		for _, j in ipairs (tab_modname_func) do
			if (j[1] == '') or (j[1] == k) then
				j[2] (option, grp, k)
				break
			end	
		end	
	end --WhenModGr
	function WhenMainGr (grp, k)
        local mod_abbrev, wd_main, wd_lang, lims = func (grp, k)
        p.add_arg_item_grp (option, mod_abbrev, grp, k, wd_main, wd_lang, lims)        
	end	
	if idx[SA.HasChild] == nil then
		for _, key in ipairs(idx) do
			if type(key) == 'table' then -- key = {type, key}; when type, any value of {'al', 'a', 'l', '-'}
	            if key[1] == '-' then
					WhenMod (key[2])
				elseif string.find (key[1],'a') then
					WhenMain (key[2])
				end	
			else
				if string.sub (key,1,1) == '_' then
					WhenMod (string.sub (key,2))
				else
					WhenMain (key)
				end	
			end	
		end
	else
		for _, grp in ipairs(idx) do
			for _, key in ipairs(idx[grp]) do
                if type(key) == 'table' then -- key = {type, key}; when type, any value of {'al', 'a', 'l', '-'}
                    if key[1] == '-' then
                        WhenModGr (grp, key[2])
                    elseif string.find (key[1],'a') then --'a' of argument
                        WhenMainGr (grp, key[2])
                    end	
                else
                    if string.sub (key,1,1) == '_' then
                        WhenModGr (grp, string.sub (key,2))
                    else
                        WhenMainGr (grp, key)
                    end	
                end	
			end
		end
	end
	if option == p.LOpt.template then
		local S = p.tab_items[#p.tab_items]
		p.tab_items[#p.tab_items] = string.sub (S, 1, string.len(S)-1)
		table.insert (p.tab_items, tab..'},')
		table.insert (p.tab_items, tab..'"format": "block"')
		table.insert (p.tab_items, '}')
		table.insert (p.tab_items, '</templatedata>')
		table.insert (p.tab_items, '</pre>')
		return table.concat (p.tab_items, '</br>')
	else
        return p.foot_for_args ()
	end	
end --show_arg_items	

local function add_lab_item (mod_abbrev, key, wd_main, wd_lang, lnk)
	p.add_mod_used_in_class (mod_abbrev)
	if wd_lang == nil then --not translated
		wd_lang = ''
	end	
	local row = ''
	row = p.add_pos (row)
	row = row..'<td>'..p.PerhapsDsplKeyMode(key)..'</td><td>'..wd_main..'</td>'
	row = p.append_mod_item (mod_abbrev, row)
	if p.uses_QP_for_labels then
		if lnk ~= '' then
			local lnk1 = lnk
			if string.sub (lnk,1,1) == 'P' then
				lnk1 = 'Property:'..lnk1
			end
			row = row..'<td>[[:wikidata:'..lnk1..'|'..lnk..']]</td><td>'..WD.getLabel({lnk, ['lang']=p.lang})..'</td>'
		else	
			row = row..'<td></td><td></td>'
		end	
	end
	row = row..'<td>'..wd_lang..'</td>'
	p.ins_tab_row (row)
end	--add_lab_item

function p.add_lab_item (mod_abbrev, key, wd_main, wd_lang, lnk)
	add_lab_item (mod_abbrev, key, wd_main, wd_lang, lnk)
end

function p.add_lab_item_grp (mod_abbrev, grp, key, wd_main, wd_lang, lnk)
	add_lab_item (mod_abbrev, grp..'.'..key, wd_main, wd_lang, lnk)
end

function p.show_lab_items (idx, func, tab_modname_func)
	table.insert (p.tab_items, '<table class="wikitable sortable">')
	local row = ''
	if p.uses_QP_for_labels then
		row = '<th>Source/WD content (en)</th>'
	else	
		row = '<th>Source</th>'
	end	
	row = p.begin_headers()..row
	if p.uses_QP_for_labels then
		row = row..'<th>WD</th><th>WD content ('..SA.lang_to_use..')</th>'
	end	
	row = row..'<th>Localization (i18n)</th>'
	p.ins_tab_row (row)
	p.CheckIdx (idx)
	function WhenMod (key)
		if tab_modname_func ~= nil then
			for _, j in ipairs (tab_modname_func) do
				if (j[1] == "") or (j[1] == key) then
					j[2](key)
					break
				end	
			end
		end	
	end --WhenMod
	function WhenMain (k)
		local mod_abbrev, key, wd_main, wd_lang, lnk = func (k)
		p.add_lab_item (mod_abbrev, k, wd_main, wd_lang, lnk)
	end	
	function WhenModGr (grp, key)
		if tab_modname_func ~= nil then
			for _, j in ipairs (tab_modname_func) do
				if (j[1] == "") or (j[1] == key) then
					j[2](grp, key)
					break
				end	
			end	
		end	
	end --WhenModGr
	function WhenMainGr (grp, k)
		local mod_abbrev, key, wd_main, wd_lang, lnk = func (grp, k)
		p.add_lab_item_grp (mod_abbrev, grp, k, wd_main, wd_lang, lnk)
	end	
	function HasLabel (key)
		return (string.find (key,'l')) or (key == 'L')
	end	
	if idx[SA.HasChild] == nil then
		for _, key in ipairs(idx) do
			if type(key) == 'table' then -- key = {type, key}; when type, any value of {'al', 'a', 'l', '-'}
	            if key[1] == '-' then
					WhenMod (key[2])
				elseif HasLabel (key[1]) then 
					WhenMain (key[2])
				end	
			else
				if string.sub (key,1,1) == '_' then
					WhenMod (string.sub (key,2))
				else
					WhenMain (key)
				end	
			end	
		end
	else
		for _, grp in ipairs(idx) do
			for _, key in ipairs(idx[grp]) do
                if type(key) == 'table' then -- key = {type, key}; when type, any value of {'al', 'a', 'l', '-'}
                    if key[1] == '-' then
                        WhenModGr (grp, key[2])
                    elseif HasLabel (key[1]) then
                        WhenMainGr (grp, key[2])
                    end	
                else
                    if string.sub (key,1,1) == '_' then
                        WhenModGr (grp, string.sub (key,2))
                    else
                        WhenMainGr (grp, key)
                    end	
                end	
			end
		end
	end
	begin_foot (true)
	perhaps_mod_used ()
	return table.concat (p.tab_items, '')
end --show_lab_items

local function add_msg_error_item (mod_abbrev, key, wd_main, wd_lang)
	if wd_lang == nil then
		wd_lang = ''
	end	
	p.add_mod_used_in_class (mod_abbrev)
	local any = '<td>'..key..'</td><td>'..wd_main..'</td><td>'..wd_lang..'</td>'
	any = p.append_mod_item (mod_abbrev, any)
	p.ins_tab_row (any)
end --add_msg_error_item	

function p.add_msg_error_item (mod_abbrev, key, wd_main, wd_lang)
	add_msg_error_item (mod_abbrev, key, wd_main, wd_lang)
end

function p.add_msg_error_item_grp (mod_abbrev, grp, key, wd_main, wd_lang)
	add_msg_error_item (mod_abbrev, grp..'.'..key, wd_main, wd_lang)
end

function p.show_msgs_errors (idx, func, tab_modname_func)
	table.insert (p.tab_items, '<table class="wikitable sortable">')
	local row = '<th>Source</th><th>Translation</th>'
	p.ins_tab_row (p.begin_headers()..row)
	p.CheckIdx (idx)
	if idx[SA.HasChild] == nil then
		for _, key in ipairs(idx) do
			if key[1] == '-' then
				key = key[2]
				for _, j in ipairs (tab_modname_func) do
					if (j[1] == "") or (j[1] == key) then
						j[2](key)
						break
					end	
				end	
			else
				local mod_abbrev, key, tab_main, tab_lang = func (key[2])
				p.add_msg_error_item (mod_abbrev, key, tab_main, tab_lang)
			end
		end
	else
		for _, grp in ipairs(idx) do
			for _, key in ipairs(idx[grp]) do
				if key[1] == '-' then
					key = key[2]
					for _, j in ipairs (tab_modname_func) do
						if (j[1] == "") or (j[1] == key) then
							j[2](grp, key)
							break
						end	
					end	
				else
					local mod_abbrev, key, tab_main, tab_lang = func (grp, key[2])
					p.add_msg_error_item_grp (mod_abbrev, grp, key, tab_main, tab_lang)
				end	
			end
		end
	end
	table.insert (p.tab_items, '</table>')
	perhaps_mod_used ()
	return table.concat (p.tab_items, '')
end --show_msgs_errors

return p