Mô đun:Lang/documentor tool

require('strict')
local p = {}

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

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

local function private_tags (frame)
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,
-- return .. table.concat (private_t) .. '\n|}' ..
--error (mw.dumpObject (private_t))
end

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

}}

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

local function lang_xx_settings(frame)
local page = mw.title.makeTitle('Template', frame.args['template'] or frame.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('{{%sinvoke:%s[Ll]ang%s|[^|]+|[^}]+}}') or content:match('{{%sinvoke:%s[Ll]ang/sandbox%s|[^|]+|[^}]+}}') then -- if this template uses Module:Lang
params = content:match('{{%sinvoke:%s[Ll]ang%s|[^|]+(|[^}]+)}}') or content:match('{{%sinvoke:%s[Ll]ang/sandbox%s|[^|]+(|[^}]+)}}') -- 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, }) -- add inter-row markup and close the wikitable and done
else
return -- does not use Module:Lang so abandon quietly
end
end

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

}}

Reads the content of the template to determine if this template uses Module:Lang.
Returns the index of the substring ' parser functions.

local function uses_module(frame)
local page = mw.title.makeTitle('Template', frame.args['template'] or frame.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

return content:find('{{%sinvoke:[Ll]ang%s|') or -- return index or empty string
end

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

- Tables:
-- language_categories
-- error_messages
-- strings

- Functions:
-- make_error(message, layout, parent_category, nocat)
-- get_language_link(language_name, language_code)
-- get_see_also_section(page_title, language_name, language_code)
-- get_hidden_category_template(frame)
-- get_top_section(frame)
-- get_bottom_section(frame, language_name, see_also_section, parent_category)

local language_categories = {
["LANGUAGES_SOURCES"] = "Articles with %s-language sources (%s)",
["LANGUAGES_COLLECTIVE_SOURCES"] = "Articles with %s-collective sources (%s)",
["CS1"] = "CS1 %s-language sources (%s)",
["LANGUAGE_TEXT"] = "Articles containing %s-language text",
-- old version ["LANGUAGES_COLLECTIVE_TEXT"] = "Articles with text from the %s collective",
["LANGUAGES_COLLECTIVE_TEXT"] = "Articles with text in %s",
["ENGLISH"] = "Articles containing explicitly cited %s-language text",
}

local error_assistance = " Please see Template talk:Lang for assistance."

local error_messages = {
["ASSISTANCE"] = "Please see Template talk:Lang for assistance.",
["INCORRECT_CATEGORY_TITLE"] = ":%s is not the category being populated by the template. The correct category is located at: :%s.",
["NO_CATEGORY_TITLE_FOUND"] = "No language category found for %s." .. error_assistance,
["NOT_VALID_CATEGORY_FORMAT"] = "%s is not a a valid category title." .. error_assistance,
["NOT_VALID_LANGUAGE_CODE"] = "%s is not a valid ISO 639 or IETF language name." .. error_assistance,
}

local strings = {
["ERROR_CATEGORY"] = "",
["ERROR_SPAN"] = 'Error: %s ',
["PURGE_DIV"] = '%s',
["SEE_ALSO"] = "\nSee also",
["SEE_ALSO_ITEM"] = " :%s",
}

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

Create an error message.
Does not place page in error category if args.nocat is used.
Does not categorize in parent cateogory if used in category namespace (usually for /testcases).

local function make_error(message, layout, parent_category, nocat)
table.insert(layout, string.format(strings["ERROR_SPAN"], message))

if not nocat then
table.insert(layout, strings["ERROR_CATEGORY"])
end

if mw.title.getCurrentTitle().nsText == "Category" then
table.insert(layout, parent_category)
end

return table.concat(layout)
end

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

Generates a language link for the correct style.

Collective languages use the name_from_tag value,
while other languages use a display name of "x-language".

local function get_language_link(language_name, language_code)
local lang_module = require('Module:Lang')
-- Is a language collective?
if language_name:find('languages') then
return lang_module.name_from_tag({language_code, link = "yes"})
else
return lang_module.name_from_tag({language_code, link = "yes", label = lang_module.name_from_tag({language_code}) .. "-language"})
end
end

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

Returns the primary language for sub-langage variants.

local function get_primary_language_code(language_code)
-- If no hyphen exists, return nil (for cases like "el")
if not language_code:find("-") then
return nil
end

-- Match everything before the first hyphen, but only if a hyphen exists
if language_code:match("^-") then
return nil -- If the code starts with a hyphen, return nil
end

-- Look for the first part before any hyphen
local primary_code = language_code:match("^[^-]+")
return primary_code
end

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

Generates a consistent style See also section for
and .

If is converted, it should also use it.

local function get_see_also_section(page_title, language_name, language_code)
local see_also_section = {}

for _, category_name in pairs(language_categories) do
local category = mw.title.new(string.format(category_name, language_name, language_code), 14)
if category and page_title ~= category.text and category.exists then
table.insert(see_also_section, string.format(strings["SEE_ALSO_ITEM"], category.prefixedText))
end
end

table.sort(see_also_section)
table.insert(see_also_section, 1, strings["SEE_ALSO"])

if table.getn(see_also_section) == 1 then
return ""
else
return table.concat(see_also_section, "\n")
end
end

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

Generates the Template:Hidden category template.

This function is separate from the get_top_section() function
as this should be used in both error categories and valid categories.

local function get_hidden_category_template(frame)
return frame:expandTemplate{title = 'Hidden category'}
end

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

Generates a consistent top maintenance template section which consists of:
-- Template:Possibly empty category
-- Template:Purge

local function get_top_section(frame)
local top_section = {}
if mw.site.stats.pagesInCategory(mw.title.getCurrentTitle().text, "all") == 0 then
table.insert(top_section, frame:expandTemplate{title = 'Possibly empty category'})
else
table.insert(top_section, frame:expandTemplate{title = 'Possibly empty category', args = {hidden=true}})
end

local purge_module = require('Module:Purge')
table.insert(top_section, string.format(strings["PURGE_DIV"], purge_module._main({"Purge page cache"})))

return table.concat(top_section, "\n\n")
end

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

Generates a consistent non-text section which consists of:
-- Template:Automatic category TOC
-- A see also section
--
-- Categorization in parent category

local function get_bottom_section(frame, language_name, see_also_section, parent_category, parent_language_category)
local bottom_section = {}
table.insert(bottom_section, frame:expandTemplate{title = 'Automatic category TOC'})
table.insert(bottom_section, see_also_section)

if mw.title.getCurrentTitle().nsText == "Category" then
table.insert(bottom_section, frame:preprocess{text = ""})
if parent_language_category then
table.insert(bottom_section, "" .. parent_language_category .. "")
end
table.insert(bottom_section, parent_category)
end

return table.concat(bottom_section, "\n\n\n")
end

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

This function implements .

local non_english_language_text_strings = {
["LINE1"] = "This category contains articles with %s%s text. The primary purpose of these categories is to facilitate manual or automated checking of text in other languages.",
["LINE2"] = "This category should only be added with the %s family of templates, never explicitly.",
["LINE3"] = 'For example %s, which wraps the text with %s. Also available is %s which displays as %s.',
["LINE3_SYNTAXHIGHLIGHT"] = "",
["IN_SCRIPT"] = " (in %s)",
["EXAMPLE_DEFAULT_TEXT"] = "text in %s language here",
["PARENT_CATEGORY"] = "",
["TEMPLATE"] = "Lang",
["TEMPLATE2"] = "Langx",
}

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

-- Naming style: Articles with text from the Berber languages collective
local page_title_modified = page.text
local split_title = "([^,]+)%%s([^,])"
local part1 = ""
local part2 = ""

if page_title_modified:find('Articles with text in') then
-- Naming style: Category:Articles with text from Afro-Asiatic languages (as currently implemented in Module:lang)
part1, part2 = language_categories["LANGUAGES_COLLECTIVE_TEXT"]:match(split_title)
elseif page_title_modified:find('explicitly cited') then
part1, part2 = language_categories["ENGLISH"]:match(split_title)
else
-- Naming style: Category:Articles containing French-language text
part1, part2 = language_categories["LANGUAGE_TEXT"]:match(split_title)
end

page_title_modified = page_title_modified:gsub(part1, "")
page_title_modified = page_title_modified:gsub(part2, "")
local language_name = page_title_modified

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

if language_name == page.text then
-- Error: Category title format not supported.
return make_error(string.format(error_messages["NOT_VALID_CATEGORY_FORMAT"], page.text), layout, parent_category, args.nocat)
end

local lang_module = require('Module:Lang')
local language_code = lang_module._tag_from_name({language_name})

if language_code:find('[Ee]rror') then
-- Error: Language code not found in database.
return make_error(string.format(error_messages["NOT_VALID_LANGUAGE_CODE"], language_name), layout, parent_category, args.nocat)
end

local correct_language_category_title = lang_module._category_from_tag({language_code})
if correct_language_category_title:find('[Ee]rror') 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 current_category_title ~= correct_language_category_title then
-- Error: The current title used is not in the supported format. TODO: can this still be reached?
return make_error(
string.format(error_messages["INCORRECT_CATEGORY_TITLE"], current_category_title, non_english_language_text_strings["TEMPLATE"], correct_language_category_title),
layout, parent_category, args.nocat)
end

table.insert(layout, get_top_section(frame))

local script_text = ""
if args.script then
script_text = string.format(non_english_language_text_strings["IN_SCRIPT"], args.script)
end

local language_link = get_language_link(language_name, language_code)
table.insert(layout, string.format(non_english_language_text_strings["LINE1"], language_link, script_text))

local lang_template = frame:expandTemplate{title = 'Tl', args = {non_english_language_text_strings["TEMPLATE"]}}
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 = frame: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 = frame:expandTemplate{title = "Tlx", args = {non_english_language_text_strings["TEMPLATE2"], language_code, example_text}}
local langx_template_code = frame: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 = frame: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(frame, language_name, see_also_section, non_english_language_text_strings["PARENT_CATEGORY"], parent_language_category)
return table.concat(layout, "\n\n") .. bottom
end

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

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(frame)
local page = mw.title.getCurrentTitle()
local args = require('Module:Arguments').getArgs(frame)
-- 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(frame))
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"], frame:expandTemplate{title = 'Tlx', args = {non_english_language_sources_strings["TEMPLATE"], language_code}}, language_link)

table.insert(layout, get_top_section(frame))
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(frame, language_name, see_also_section, parent_category, parent_language_category)
return table.concat(layout, "\n\n") .. bottom
end

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

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 %s:. 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 %s:.",

local function non_english_language_cs1_sources_category(frame)
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(frame))
local see_also_section = ""
local parameter_doc = frame:expandTemplate{title = 'para', args = {"language", language_code}}
table.insert(layout, get_top_section(frame))
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(frame, language_name, see_also_section, non_english_language_cs1_text_strings["PARENT_CATEGORY"])
return table.concat(layout, "\n\n") .. bottom
end

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

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

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:
{"", ""}

- "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

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

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:
{"", ""}

- "Expected" column value is the .
- "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

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

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:
{"", ""}

- "Expected" column value is the .
- "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

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

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:
{""}

- "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

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

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:
{"", ""}

- "Expected" column value is the .
- "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

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

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 (frame)
if nil frame.args[1] or frame.args[1] then -- if empty, ...
return -- ... return nothing
end

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

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

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,
}