# Module:Lang/documentor tool

require('strict')  
local p = {}  

-- -------------------------< P R I V A T E _ T A G S >------------------------------------------------------  

Reads the override{} table in Module:Lang/data and renders a wiki table of private tags and their associated languages  

local function private_tags  
local override_t = mw.loadData ('Module:Lang/data').override -- get the override table  

local private_t = {}  

for tag, lang in pairs (override_t) do  
if tag:find ('%-x%-') then  
table.insert (private_t, table.concat ({'\n|-\n|', lang, '||', tag}))  
end  
end  
table.sort (private_t)  

table.insert (private_t, 1, '{|')  
table.insert (private_t, 2, '\n|+ Supported private-use IETF language tags')  
table.insert (private_t, 3, '\n! Language !! Private-use tag')  

return table.concat (private_t) .. '\n|}'  
-- return '<pre>' .. table.concat (private_t) .. '\n|}' .. '</pre>'  
--error (mw.dumpObject (private_t))  
end  

-- -------------------------< L A N G - X X _ S E T T I N G S >----------------------------------------------  

Reads the content of the template and extracts the parameters from for display on the template's  
documentation page.  

local function lang_xx_settings  
local page = mw.title.makeTitle('Template', .args['template'] or .args[1]) -- get a page object for this page in 'Template:' namespace  
if not page then  
return '' -- TODO: error message?  
end  

local content = page:getContent -- get unparsed content  
if not page then  
return '' -- TODO: error message?  
end  

local out = {}  

local params  
local style  

if content:match(*) or content:match(*) then -- if this template uses Module:Lang  
params = content:match(*) or content:match(*) -- extract the #invoke:'s parameters  
if not params then  
return '' -- there should be at least one or the template/module won't work TODO: error message?  
end  
table.insert(out, '{|\n|+settings') -- start a wikitable  
for k, v in params:gmatch('%s*|%s*([^%s=]+)%s*=%s*([^%s|]+)') do -- get the parameter names (k) and values (v)  
if 'label' == k then -- special case for labels because spaces and pipes  
v = params:match('label%s*=%s*(%[%^%+%]%])') or params:match('label%s*=%s*([^|\n]+)') or 'missing label'  
end  
table.insert(out, table.concat({k, '\n|', v})) -- make rudimentary wikitable entries  
end  

style = content:match('lang_xx_([^|]+)')  
if not style or ('italic' ~= mw.text.trim (style) and 'inherit' ~= mw.text.trim (style)) then  
return '<span>Error: template #invoke calls unknown function</span>'  
end  
return table.concat({table.concat(out,'\n|-\n! scope="row" | '), '\n|-\n||style: ', style, '\n|-\n|}'}) -- add inter-row markup and close the wikitable and done  
else  
return '' -- does not use Module:Lang so abandon quietly  
end  
end  

--[[ -------------------------- < U S E S _ M O D U L E > --------------------------  

Reads the content of the template to determine if this template uses Module:Lang.  
Returns the index of the substring '  
table.insert(layout, string.format(non_english_language_text_strings["LINE2"], lang_template))  

local language_code_link = lang_module._name_from_tag({language_code, link="yes", label=language_code})  
local example_default_text = string.format(non_english_language_text_strings["EXAMPLE_DEFAULT_TEXT"], language_name)  
local example_text = args.example or example_default_text  
local lang_template_example = :expandTemplate{title = "Tlx", args = {non_english_language_text_strings["TEMPLATE"], language_code_link, example_text}}  
local langx_args = {non_english_language_text_strings["TEMPLATE2"], language_code_link, example_text}  
local langx_template_example = :expandTemplate{title = "Tlx", args = {non_english_language_text_strings["TEMPLATE2"], language_code, example_text}}  
local langx_template_code = :expandTemplate{title = non_english_language_text_strings["TEMPLATE2"], args = {language_code, example_text}}  

-- Strip private use subtag from code tag because this is what Module:Lang does.  
local language_code_without_private_use = language_code:gsub("%-x%-.*", "")  

-- Wrap in syntaxhighlight.  
local syntaxhighlight = :extensionTag(  
"syntaxhighlight",  
string.format(non_english_language_text_strings["LINE3_SYNTAXHIGHLIGHT"], language_code_without_private_use),  
{lang = "html", inline = true}  

table.insert(layout, string.format(non_english_language_text_strings["LINE3"], lang_template_example, syntaxhighlight, langx_template_example, langx_template_code))  
local see_also_section = get_see_also_section(page.text, language_name, language_code)  

local parent_language_code = get_primary_language_code(language_code)  
local parent_language_category  
if parent_language_code then  
parent_language_category = lang_module._category_from_tag({parent_language_code})  
end  

local bottom = get_bottom_section(, language_name, see_also_section, non_english_language_text_strings["PARENT_CATEGORY"], parent_language_category)  
return table.concat(layout, "\n\n") .. bottom  
end  

-- -------------------------- < N O N _ E N G L I S H _ L A N G U A G E _ S O U R C E S _ C A T E G O R Y > --------------------------  

This function implements .  

local non_english_language_sources_strings = {  
["LINE1"] = "This is a tracking category for articles that use %s to identify %s sources.",  
["PARENT_CATEGORY"] = "",  
["TEMPLATE"] = "In lang",  
}  

local function non_english_language_sources_category  
local page = mw.title.getCurrentTitle  
local args = require('Module:Arguments').getArgs  
-- args.test is used for /testcases  
if args.test then  
page = mw.title.new(args.test)  
end  

local page_title = page.text  
local language_code = page_title:match('%(([%a%-]+)%)')  
local language_name = require('Module:Lang')._name_from_tag({language_code})  

local layout = {}  
table.insert(layout, get_hidden_category_template)  
local parent_category = non_english_language_sources_strings["PARENT_CATEGORY"]  

local in_lang_module = require('Module:In lang')  
local correct_language_category_title = in_lang_module._in_lang({language_code, ["list-cats"]="yes"})  
if correct_language_category_title == "" then  
-- Error: No category title found for language code.  
return make_error(string.format(error_messages["NO_CATEGORY_TITLE_FOUND"], language_code), layout, parent_category, args.nocat)  
end  

local current_category_title = page.prefixedText  
if correct_language_category_title ~= current_category_title then  
-- Error: The current title used is not in the supported format.  
return make_error(  
string.format(error_messages["INCORRECT_CATEGORY_TITLE"], current_category_title, non_english_language_sources_strings["TEMPLATE"], correct_language_category_title),  
layout, parent_category, args.nocat)  
end  

local language_link = get_language_link(language_name, language_code)  
local text = string.format(non_english_language_sources_strings["LINE1"], :expandTemplate{title = 'Tlx', args = {non_english_language_sources_strings["TEMPLATE"], language_code}}, language_link)  

table.insert(layout, get_top_section)  
table.insert(layout, text)  
local see_also_section = get_see_also_section(page_title, language_name, language_code)  

local parent_language_code = get_primary_language_code(language_code)  
local parent_language_category  
if parent_language_code then  
parent_language_category = in_lang_module._in_lang({parent_language_code, ["list-cats"]="yes"})  
end  

local bottom = get_bottom_section(, language_name, see_also_section, parent_category, parent_language_category)  
return table.concat(layout, "\n\n") .. bottom  
end  

-- -------------------------- < N O N _ E N G L I S H _ L A N G U A G E _ C S 1 _ S O U R C E S _ C A T E G O R Y > --------------------------  

This function implements .  

local non_english_language_cs1_text_strings = {  
["LINE1"] = "This is a tracking category for CS1 citations that use the parameter %s to identify a source in %s. Pages in this category should only be added by CS1 templates and Module:Citation/CS1.",  
["PARENT_CATEGORY"] = "", -- #TODO change to "Articles with non-english CS1 language sources" or "CS1 non-English language sources"  
}  

--"This is a tracking category for CS1 citations that use the parameter %s to hold a citation title that uses %s characters and contains the language prefix <code>%s:</code>. Pages in this category should only be added by CS1 templates and Module:Citation/CS1.",  
--"",  

-- "This is a tracking category for CS1 citations that use the parameter %s. Pages in this category should only be added by CS1 templates and Module:Citation/CS1.",  
-- "to identify a source in %s.",  
-- "to hold a citation title that uses %s characters and contains the language prefix <code>%s:</code>.",  

local function non_english_language_cs1_sources_category  
local page_title_object = mw.title.getCurrentTitle  
local page_title = page_title_object.text  
local language_code = page_title:match('%(([%a%-]+)%)')  
local language_name = require('Module:Lang')._name_from_tag({language_code})  
local layout = {}  
table.insert(layout, get_hidden_category_template)  
local see_also_section = ""  
local parameter_doc = :expandTemplate{title = 'para', args = {"language", language_code}}  
table.insert(layout, get_top_section)  
table.insert(layout, string.format(non_english_language_cs1_text_strings["LINE1"], parameter_doc, language_name, language_name))  
local see_also_section = get_see_also_section(page_title, language_name, language_code)  
local bottom = get_bottom_section(, language_name, see_also_section, non_english_language_cs1_text_strings["PARENT_CATEGORY"])  
return table.concat(layout, "\n\n") .. bottom  
end  

-- -------------------------- < T E S T _ C A S E S _ S H A R E D _ C O D E > --------------------------  

local function compare_by_keys2(a, b) -- local function used by table.sort  
return a[2] < b[2] -- ascending sort by code  
end  

local function compare_by_keys(a, b) -- local function used by table.sort  
return a[1] < b[1] -- ascending sort by code  
end  

-- Used by testcases_iso_code_to_name  
local function get_language_code_table_from_code(args)  
local entry = {}  
--if args.override_table[args.language_code] then  
-- table.insert(entry, args.override_table[args.language_code][1]) -- :gsub(' %b$', '') fails here  
--else  
table.insert(entry, args.language_code)  
-- end  
return entry  
end  

-- Used by testcases_name_from_tag  
local function get_language_code_and_name_table_from_code(args)  
local entry = {}  
if args.override_table and args.override_table[args.language_code] then  
table.insert(entry, args.language_code)  
local language_code, _ = args.override_table[args.language_code][1]:gsub(' %b$', '')  
table.insert(entry, language_code)  
else  
table.insert(entry, args.language_code)  
table.insert(entry, args.language_table[args.language_code])  
end  
return entry  
end  

-- Used by testcases_category_from_tag  
local function get_language_code_and_category_table_from_code(args)  
local entry = {}  
table.insert(entry, args.language_code)  
table.insert(entry, args.test_function({args.language_code}))  
return entry  
end  

-- Used by testcases_iso_name_to_code and testcases_tag_from_name  
local function get_language_name_and_code_table_from_code(args)  
local entry = {}  
if args.override_table[args.language_code] then  
table.insert(entry, args.override_table[args.language_code][1]) -- only the first name when there are multiples  
table.insert(entry, args.language_code)  
else  
table.insert(entry, args.language_names[1]) -- only the first name when there are multiples  
table.insert(entry, args.language_code)  
end  
return entry  
end  

local function get_table(table_function, language_table, length, range, iso_number, test_function)  
local table_of_language_name_and_code_tables = {}  

local override_table_name = "override"  
if iso_number then  
override_table_name = "override_" .. iso_number  
end  
local override_table = require("Module:ISO 639 name/ISO_639_override/sandbox")[override_table_name]  

-- For most ISO 639s.  
if range then  
for language_code, language_names in pairs(language_table) do  
if language_code:find(range) then  
table.insert(table_of_language_name_and_code_tables, table_function({  
override_table = override_table,  
language_code = language_code,  
language_names = language_names,  
test_function = test_function,  
language_table = language_table  
}))  
end  
end  
-- For ISO 639-1.  
elseif length then  
for language_code, language_names in pairs(language_table) do  
if language_code:len == 2 then  
table.insert(table_of_language_name_and_code_tables, table_function({  
override_table = override_table,  
language_code = language_code,  
language_names = language_names,  
test_function = test_function,  
language_table = language_table  
}))  
end  
end  
-- For general /testcases.  
else  
for language_code, language_names in pairs(language_table) do  
table.insert(table_of_language_name_and_code_tables, table_function({  
override_table = override_table,  
language_code = language_code,  
language_names = language_names,  
test_function = test_function,  
language_table = language_table  
}))  
end  
end  
return table_of_language_name_and_code_tables  
end  

local function get_undabbed_table(language_list, length, range)  
local undabbed_language_table = {} -- for this test, ISO 639-3 language name disambiguators must be removed; un-dabbed names go here  
for language_code, language_names in pairs(language_list) do  
-- For most ISO 639s.  
if range then  
if language_code:find(range) then  
undabbed_language_table[language_code] = language_names[1]:gsub(' %b$', '') -- undab and save only the first name; ignore all other names assigned to a code  
end  
-- For ISO 639-1.  
elseif length then  
if language_code:len == 2 then  
undabbed_language_table[language_code] = language_names[1]:gsub(' %b$', '') -- undab and save only the first name; ignore all other names assigned to a code  
end  
-- For general /testcases.  
else  
undabbed_language_table[language_code] = language_names[1]:gsub(' %b$', '')  
end  
end  
return undabbed_language_table  
end  

-- -------------------------- < T E S T C A S E S _ C A T E G O R Y _ F R O M _ T A G > --------------------------  

Entry point for the various category_from_tag testcases.  

Build a table of test patterns where each entry in the table is a table with two members:  
{"<language_code>", "<category name according to Module:Lang>"}  

- "Expected" column value is the category name according to Module:Lang.  
- "Actual" column value is the result of .  

TODO: Currently not working.  

local function testcases_category_from_tag(self, args)  
local cat_from_tag_function = require('Module:Lang')._category_from_tag  
local language_tables = get_table(get_language_code_and_category_table_from_code, args.language_list, args.length, args.range, args.iso_number, cat_from_tag_function)  
table.sort(language_tables, compare_by_keys)  

self:preprocess_equals_preprocess_many(  
*, *, '',  
language_tables,  
{nowiki=false}  
end  

-- -------------------------- < T E S T C A S E S _ N A M E _ F R O M _ T A G > --------------------------  

Entry point for the various name_from_tag testcases.  

Build a table of test patterns where each entry in the table is a table with two members:  
{"<language_code>", "<language_name>"}  

- "Expected" column value is the <language_name>.  
- "Actual" column value is the result of sandbox version .  

local function testcases_name_from_tag(self, args)  
local undabbed_language_table = get_undabbed_table(args.language_list, args.length, args.range, nil)  
local language_tables = get_table(get_language_code_and_name_table_from_code, undabbed_language_table, args.length, args.range)  
table.sort(language_tables, compare_by_keys)  

self:preprocess_equals_preprocess_many(  
*, *, '',  
language_tables,  
{nowiki=false}  
end  

-- -------------------------- < T E S T C A S E S _ T A G _ F R O M _ N A M E > --------------------------  

Entry point for the various tag_from_name testcases.  

Build a table of test patterns where each entry in the table is a table with two members:  
{"<language_name>", "<language_code>"}  

- "Expected" column value is the <language_code>.  
- "Actual" column value is the result of sandbox version .  

TODO: Currently not working.  

local function testcases_tag_from_name(self, args)  
local language_tables = get_table(get_language_name_and_code_table_from_code, args.language_list, args.length, args.range, args.iso_number, nil)  
table.sort(language_tables, compare_by_keys2)  

local ordered_table = {}  
table.sort(unordered_table)  
for _, key in ipairs(unordered_table) do  
table.insert(ordered_table, {key, reverse_table[key]})  
end  

self:preprocess_equals_preprocess_many(  
*, *, '',  
language_tables,  
{nowiki=false}  
end  

-- -------------------------- < T E S T C A S E S _ I S O _ C O D E _ T O _ N A M E > --------------------------  

Entry point for the various iso_code_to_name testcases.  

Build a table of test patterns where each entry in the table is a table with one member:  
{"<language_code>"}  

- "Expected" column value is the result of the live version of .  
- "Actual" column value is the result of sandbox version .  

local function testcases_iso_code_to_name(self, args)  
local language_tables = get_table(get_language_code_table_from_code, args.language_list, args.length, args.range, args.iso_number, nil)  
table.sort(language_tables, compare_by_keys)  

self:preprocess_equals_preprocess_many(  
*, *,  
language_tables,  
{nowiki=false}  
end  

-- -------------------------- < T E S T C A S E S _ I S O _ N A M E _ T O _ C O D E > --------------------------  

Entry point for the various iso_name_to_code testcases.  

Build a table of test patterns where each entry in the table is a table with two members:  
{"<language_name>", "<language_code>"}  

- "Expected" column value is the <language_code>.  
- "Actual" column is value the result of .  

local function testcases_iso_name_to_code(self, args)  
local language_tables = get_table(get_language_name_and_code_table_from_code, args.language_list, args.length, args.range, args.iso_number, nil)  
table.sort(language_tables, compare_by_keys2)  
self:preprocess_equals_preprocess_many(  
*, *, '',  
language_tables,  
{nowiki=false}  
end  

----------------------------< S E E _ A L S O >--------------------------------------------------------------  

adds items to the list of items in §See also section of Template:Lang-x/doc; Evaluates single positional parameter  
which is a comma-separated list of items including list markup.  

local function see_also  
if nil == .args[1] or '' == .args[1] then -- if empty, ...  
return -- ... return nothing  
end  

return :preprocess (.args[1]:gsub ('%s*,%s', '\n')) -- preprocess so any templates are rendered before saving and done  
end  

-- -------------------------< E X P O R T E D _ F U N C T I O N S > -----------------------------------------  

return {  
lang_xx_settings = lang_xx_settings,  
uses_module = uses_module,  
see_also = see_also,  
non_english_language_text_category = non_english_language_text_category,  
non_english_language_sources_category = non_english_language_sources_category,  
non_english_language_cs1_sources_category = non_english_language_cs1_sources_category,  
private_tags = private_tags,  

-- Module:Lang testcases  
testcases_category_from_tag = testcases_category_from_tag,  
testcases_name_from_tag = testcases_name_from_tag,  
testcases_tag_from_name = testcases_tag_from_name,  

-- Module:ISO 639 name testcases  
testcases_iso_code_to_name = testcases_iso_code_to_name,  
testcases_iso_name_to_code = testcases_iso_name_to_code,  
}