Module:topic list
- The following documentation is located at Module:topic list/documentation. [edit]
- Useful links: subpage list • links • transclusions • testcases • sandbox
Exported functions
export.show
function export.show(frame)
{{list:continents/sw}}
) is {{#invoke:topic list|show|sw
|hypernym=[[bara|mabara]]
|Afrika<t:Africa>
|Antaktika~Antaktiki<t:Antarctica>
|Asia<t:Asia>
|Ulaya,Uropa<t:Europe>
|Amerika ya Kaskazini<t:North America>
|Amerika ya Kusini<t:South America>
|Australia<t:Australia>
}}
The syntax of the params is largely the same as for {{col}}
, but the following additional params supported:
|cat=
: Comma-separated list of categories to add the page to. The categories should specify bare topic categories without the preceding language code, e.g.Islamic calendar months
for a categoryLANGCODE:Islamic calendar months
. If you need to specify a different type of category, prefix the full category name withCategory:
. There must not be any spaces after the comma delimiter for it to be recognized as such. By default, the category comes from the template name, minus the initiallist:
and anything starting with a slash, and with the first letter capitalized; but only if the top-level language-agnostic category exists. Hence a template like{{list:prefectures of Japan/ja}}
will automatically categorize into Category:ja:Prefectures of Japan (and likewise{{list:prefectures of Japan/sq}}
will still categorize into Category:sq:Prefectures of Japan whether or not it exists, because the top-level Category:Prefectures of Japan exists), but a template like{{list:human anatomy direction adjectives/en}}
will not categorize anywhere by default because there is no top-level Category:Human anatomy direction adjectives. Use|cat=-
to disable categorization. Note that no categorization takes place if|nocat=1
is specified in the invocation of the list template or if the page is the same as any of the pages linked to by the language-specific hypernym(s) given in|hypernym=
.|hypernym=
: The language-specific plural form of the hypernym category (e.g. in the above example, the word for "continents" in Swahili). Generally this should be linked to the corresponding singular, if they are different. If specified, it is displayed after the English hypernym, following a colon. This parameter can be omitted to not display a hypernym, and you can also specify multiple comma-separated or tilde-separated hypernyms with inline modifiers attached to each one (essentially, following the format of a given item in{{col}}
).|enhypernym=
: The English-language hypernym, e.g.continents
in the above example. If omitted, this is derived from the template name by chopping off the initiallist:
and anything starting with a slash. This will be hyperlinked to the first category that the page categorizes into, if such a category exists.|appendix=
: One or more comma-separated appendices to display after the hypernym, in parens. The appendix should be a full pagename including the namespaceAppendix:
, or a two-part link giving a pagename and display form. If a link is not specified, the display form will beappendix
.|pagename=
: Override the pagename, which should normally be a template of the formTemplate:list:list name/langcode
orTemplate:list:list name/langcode/variant
. The list name and language code are parsed out of the pagename and used as the default title and language, and various other defaults are set based on the list name.
export.parse_directions_and_draw_compass
function export.parse_directions_and_draw_compass(args, lang, sc, notr)
This function lacks documentation. Please add a description of its usages, inputs and outputs, or its difference from similar functions, or make it local to remove it from the function list.
export.compass_plain
function export.compass_plain(frame)
This function lacks documentation. Please add a description of its usages, inputs and outputs, or its difference from similar functions, or make it local to remove it from the function list.
export.compass
function export.compass(frame)
This function lacks documentation. Please add a description of its usages, inputs and outputs, or its difference from similar functions, or make it local to remove it from the function list.
local export = {}
local columns_module = "Module:columns"
local languages_module = "Module:languages"
local parameters_module = "Module:parameters"
local parameter_utilities_module = "Module:parameter utilities"
local parse_interface_module = "Module:parse interface"
local string_utilities_module = "Module:string utilities"
local utilities_module = "Module:utilities"
local m_str_utils = require(string_utilities_module)
local is_callable = require("Module:fun").is_callable
local rfind = m_str_utils.find
local ucfirst = m_str_utils.ucfirst
local unpack = unpack or table.unpack -- Lua 5.2 compatibility
local html = mw.html.create
local force_cat = false -- for testing
local function letter_like_category(data)
return ("Category:%s letters"):format(data.lang:getCanonicalName())
end
local function page_exists(page)
local title = mw.title.new(page)
return title and title.exists
end
local function letter_like_appendix(data)
local appendices = {}
local alphabet_appendix = ("Appendix:%s alphabet"):format(data.lang:getCanonicalName())
if page_exists(alphabet_appendix) then
table.insert(appendices, ("[[%s|alphabet appendix]]"):format(alphabet_appendix))
end
local script_name = data.list_name:match("^(.* script) .*$")
if not script_name then
error(("Internal error: Can't pull out script name from list name '%s'"):format(data.list_name))
end
local script_appendix = "Appendix:" .. script_name
if page_exists(script_appendix) then
table.insert(appendices, ("[[%s|script appendix]]"):format(script_appendix))
end
if appendices[1] then
return table.concat(appendices, ",")
else
return nil
end
end
local letter_like_properties = {
horiz = "comma",
sort = false,
cat = letter_like_category,
appendix = letter_like_appendix,
notr = true,
allow_space_delim = true, -- allow space as a delimiter
tilde_delim = "~", -- use a tilde without spaces around it
}
local function letter_name_category(data)
local script_name = data.list_name:match("^(.*) script .*$")
if not script_name then
error(("Internal error: Can't pull out script name from list name '%s'"):format(data.list_name))
end
return ("%s letter names"):format(script_name)
end
local function calendar_month_adjectives_category(data)
local calendar = data.list_name:match("^(.*) calendar month adjectives$")
if not calendar then
error(("Internal error: Can't pull out calendar type from list name '%s'"):format(data.list_name))
end
return calendar .. " calendar months"
end
local topic_list_properties = {
{".* calendar months", {sort = false}},
{".* calendar month adjectives", {sort = false, cat = calendar_month_adjectives_category}},
{"compass point adjectives", {cat = "Compass points"}},
{"compass point adverbs", {cat = "Compass points"}},
{".* script letters", letter_like_properties},
{".* script vowels", letter_like_properties},
{".* script consonants", letter_like_properties},
{".* script diacritics", letter_like_properties},
{".* script digraphs", letter_like_properties},
-- FIXME: We may need to be smarter, and use a regular columnar display in some cases (e.g. when translit is
-- present)
{".* script letter names", {horiz = "bullet", sort = false, cat = letter_name_category}},
-- Geographic lists should not categorize. {{place}} does that, and may do it differently.
{"administrative .* of .*", {nocat = true}},
{"autonomous .* of .*", {nocat = true}},
{"cantons of .*", {nocat = true}},
{"capital cities of .*", {nocat = true}},
{"continents", {nocat = true}},
{"counties of .*", {nocat = true}},
{"countries in .*", {appendix = "Appendix:Countries of the world", nocat = true}},
{"districts of .*", {nocat = true}},
{"divisions of .*", {nocat = true}},
{"federal .* of .*", {nocat = true}},
{"governorates of .*", {nocat = true}},
{"macroregions of .*", {nocat = true}},
{"prefectures of .*", {nocat = true}},
{"provinces.* of .*", {nocat = true}},
{"regions of .*", {nocat = true}},
{"states.* of .*", {nocat = true}},
{"territories of .*", {nocat = true}},
{"union territories of .*", {nocat = true}},
{"vilayets of .*", {nocat = true}},
{"books of the.* Testament", {sort = false, cat = "Books of the Bible"}},
-- FIXME: Use the following instead of the previous when we create 'Books of the Old Testament' and
-- 'Books of the New Testament'
--{"books of the Old Testament", {sort = false}},
--{"books of the .*New Testament", {sort = false, cat = "Books of the New Testament"}},
{"canids", {horiz = "bullet"}}, -- only 5 items on most lists
{"days of the week", {horiz = "bullet", sort = false, appendix = "Appendix:Days of the week"}},
{"dentistry location adjectives", {cat = "Dentistry"}},
-- FIXME: Delete the following once we rename these categories to 'electromagnetic spectrum'
{"electromagnetic radiation", {sort = false, cat = "Electromagnetic spectrum"}}, -- FIXME, add category
{"electromagnetic spectrum", {sort = false}},
-- FIXME: Delete the following once we rename these categories to 'terms for fingers'
{"fingers", {sort = false, cat = "Terms for fingers"}},
{"terms for fingers", {sort = false, cat = "Terms for fingers"}},
{"fundamental interactions", {cat = "Physics"}},
{"geological time units", {sort = false, cat = "Units of time,Geology"}},
{"human anatomy location adjectives", {cat = "Medicine"}},
{"Islamic prophets", {sort = false}},
{"leptons", {sort = false, horiz = "bullet"}},
{"antileptons", {sort = false, horiz = "bullet", cat = "Leptons"}},
{"oceans", {horiz = "bullet"}},
{"planets of the Solar System", {horiz = "bullet", sort = false}},
{"quarks", {sort = false, horiz = "bullet"}},
{"squarks", {sort = false, horiz = "bullet", cat = "Quarks"}},
{"antiquarks", {sort = false, horiz = "bullet", cat = "Quarks"}},
{"religions", {cat = "Religion"}}, -- FIXME, add and use category 'Religions'
{"religious adherents", {cat = "Religion"}},
{"religious texts", {cat = "Religion"}}, -- FIXME, add and use category 'Religious texts'
{"seasons", {horiz = "bullet", sort = false}},
{"sexual orientation adjectives", {cat = "Sexual orientations"}},
{"taxonomic ranks", {sort = false}},
{"times of day", {sort = false}},
{"units of time", {sort = false}},
}
local function parse_topic_list_user_args(raw_user_args)
local user_params = {
nocat = {type = "boolean"},
sortbase = {},
}
return require(parameters_module).process(raw_user_args, user_params)
end
local function add_topic_list_params(params)
params["cat"] = {}
params["nocat"] = {type = "boolean"}
params["enhypernym"] = {}
params["hypernym"] = {}
params["appendix"] = {}
params["pagename"] = {} -- for testing of topic list
end
local function set_default_arguments(args, default_props)
if default_props then
for k, v in pairs(default_props) do
if args[k] == nil then
args[k] = v
end
end
end
end
local function make_horiz_edit_button(topic_list_template)
return tostring(html("small"):node(html("sup")
:wikitext(" [[[Special:EditPage/" .. topic_list_template .. "|edit]]]")
))
end
local function analyze_template_name(topic_list_template)
-- Analyze template name for list name language and variant. The variant comes after the language code and a
-- slash and may or may not be present. Cf. [[Template:list:days of the week/cim/Luserna]] (for the Luserna dialect
-- of Cimbrian) and [[Template:list:days of the week/cim/13]] (for the Tredici Comuni dialect of Cimbrian).
--
-- Also compute default properties based on template name.
local list_name_plus_lang = topic_list_template:gsub("^Template:", ""):gsub("^list:", "")
local list_name, langcode_and_variant = list_name_plus_lang:match("^(.-)/(.*)$")
local lang, variant
if langcode_and_variant then
local langcode
langcode, variant = langcode_and_variant:match("^(.-)/(.*)$")
langcode = langcode or langcode_and_variant
lang = require(languages_module).getByCode(langcode, nil, "allow etym")
if not lang then
error(("Unrecognized language code '%s' in topic list template name [[%s]]"):format(
langcode, topic_list_template))
end
else
error(("Can't parse language code out of topic list template name [[%s]]; it should be of the form " ..
"'Template:list:LISTNAME/LANGCODE' or 'Template:list:LISTNAME/LANGCODE/VARIETY'"):format(
topic_list_template))
end
local default_props
for _, pattern_and_props in ipairs(topic_list_properties) do
local pattern, props = unpack(pattern_and_props)
if rfind(list_name, "^" .. pattern .. "$") then
default_props = props
break
end
end
if default_props then
-- Safest to make a copy of the default props in case we modify it, as we do when one of the values is a
-- function, in case we are called twice in the same Scribunto invocation (not common but possible I think).
local default_props_copy = {}
for k, v in pairs(default_props) do
if is_callable(v) then
default_props_copy[k] = v {
topic_list_template = topic_list_template,
list_name = list_name,
lang = lang,
variant = variant,
}
else
default_props_copy[k] = v
end
end
default_props = default_props_copy
end
return list_name, lang, variant, default_props
end
-- FIXME: This needs to be implemented properly in [[Module:links]].
local function get_left_side_link(link)
local left, right = link:match("^%[%[([^%[%]|]+)|([^%[%]|]+)%]%]$")
if left then
return left
end
local single_part = link:match("^%[%[([^%[%]|]+)%]%]$")
if single_part then
return single_part
end
if not link:match("%[%[") then
return link
end
return nil
end
local term_param_mods = require(parameter_utilities_module).construct_param_mods {
{group = "link"}, -- sc has separate_no_index = true; that's the only one
-- It makes no sense to have overall l=, ll=, q= or qq= params for columnar display.
{group = {"ref", "l", "q"}, require_index = true},
}
local function parse_term_with_delimiters(data)
local term, paramname, lang, sc, pagename = data.term, data.paramname, data.lang, data.sc, data.pagename
local function generate_obj(term, parse_err)
local actual_term, termlang = require(parse_interface_module).parse_term_with_lang {
term = term,
parse_err = parse_err,
paramname = paramname,
}
return {
term = actual_term ~= "" and actual_term or nil,
lang = termlang or lang,
}
end
local items = require(parse_interface_module).parse_inline_modifiers(term, {
paramname = paramname,
param_mods = term_param_mods,
generate_obj = generate_obj,
splitchar = "[,~]",
preserve_splitchar = true,
outer_container = {},
})
local item_is_page = false
for i, item in ipairs(items.terms) do
if i == 1 then
item.separator = nil
elseif item.delimiter == "~" then
item.separator = data.tilde_delim or " ~ "
else
item.separator = data.comma_delim or ", "
end
-- Do this afterwards rather than in generate_obj() or use of <sc:...> will trigger an error
-- because the modifier is already set.
if not item.sc and sc then
item.sc = sc
end
if data.notr then
item.tr = "-"
end
if item.term and pagename then
local left_side = get_left_side_link(item.term)
if left_side and left_side == pagename then
item_is_page = true
end
end
end
return items, item_is_page
end
local function get_title_and_formatted_cats(args, lang, sc, topic_list_data)
local title = args.title
local formatted_cats
local cats
local default_title = topic_list_data.list_name
local user_args = topic_list_data.user_args
local nocat = args.nocat or user_args.nocat
local fulllangcode = lang:getFullCode()
if nocat then
-- don't generate categories for the title
elseif not args.cat then
local default_cat = ucfirst(default_title)
local cat_title = mw.title.new(("Category:%s"):format(default_cat))
if cat_title and cat_title.exists then
cats = {fulllangcode .. ":" .. default_cat}
end
elseif args.cat ~= "-" then
cats = require(parse_interface_module).split_on_comma(args.cat)
for i, cat in ipairs(cats) do
if cat:find("^Category:") then
cats[i] = cat:gsub("^Category:", "")
else
cats[i] = fulllangcode .. ":" .. cats[i]
end
end
end
local hypernym_is_page
if not title then
local titleparts = {}
local function ins(txt)
table.insert(titleparts, txt)
end
local enhypernym
if args.enhypernym then
enhypernym = args.enhypernym:gsub("%+", m_str_utils.replacement_escape(default_title))
else
enhypernym = default_title
if topic_list_data.variant then
enhypernym = ("%s (%s)"):format(enhypernym, topic_list_data.variant)
end
end
if cats and not enhypernym:find("%[%[") then
ins(("[[:Category:%s|%s]]"):format(cats[1], enhypernym))
else
ins(enhypernym)
end
if args.hypernym then
local lang_hypernyms
local pagename = args.pagename or mw.loadData("Module:headword/data").pagename
lang_hypernyms, hypernym_is_page = parse_term_with_delimiters {
term = args.hypernym,
paramname = "hypernym",
lang = lang,
sc = sc,
pagename = pagename,
-- Don't set notr= here; we still want to transliterate the hypernym if given
}
local formatted_hypernyms = require(columns_module).format_item(lang_hypernyms, {lang = lang}, "bold")
ins(": " .. formatted_hypernyms)
end
if args.appendix then
local appendices = require(parse_interface_module).split_on_comma(args.appendix)
for i, appendix in ipairs(appendices) do
if not appendix:find("%[%[") then
appendices[i] = ("[[%s|appendix]]"):format(appendix)
end
end
ins((" (<small>%s</small>)"):format(table.concat(appendices, ", ")))
end
title = table.concat(titleparts)
end
if not args.horiz and not topic_list_data.suppress_edit_button then
-- append edit button to title
local edit_link = html("span")
:addClass("list-switcher-edit")
:wikitext("[[Special:Edit/" .. topic_list_data.topic_list_template .. "|edit]]")
title = title .. tostring(edit_link)
end
if cats and not nocat and not hypernym_is_page then
formatted_cats = require(utilities_module).format_categories(cats, lang, nil, user_args.sortbase, force_cat)
end
return title, formatted_cats
end
--[==[
This implements topic lists. A given topic list template must directly invoke this function rather than
going through a wrapping template. A sample template implementation (e.g. for {{tl|list:continents/sw}}) is
{
{{#invoke:topic list|show|sw
|hypernym=[[bara|mabara]]
|Afrika<t:Africa>
|Antaktika~Antaktiki<t:Antarctica>
|Asia<t:Asia>
|Ulaya,Uropa<t:Europe>
|Amerika ya Kaskazini<t:North America>
|Amerika ya Kusini<t:South America>
|Australia<t:Australia>
}}
}
The syntax of the params is largely the same as for {{tl|col}}, but the following additional params supported:
* {{para|cat}}: Comma-separated list of categories to add the page to. The categories should specify bare topic
categories without the preceding language code, e.g. `Islamic calendar months` for a category
`LANGCODE:Islamic calendar months`. If you need to specify a different type of category, prefix the full category name
with `Category:`. There must not be any spaces after the comma delimiter for it to be recognized as such. By default,
the category comes from the template name, minus the initial `list:` and anything starting with a slash, and with the
first letter capitalized; but only if the top-level language-agnostic category exists. Hence a template like
{{tl|list:prefectures of Japan/ja}} will automatically categorize into [[:Category:ja:Prefectures of Japan]] (and
likewise {{tl|list:prefectures of Japan/sq}} will still categorize into [[:Category:sq:Prefectures of Japan]] whether
or not it exists, because the top-level [[:Category:Prefectures of Japan]] exists), but a template like
{{tl|list:human anatomy direction adjectives/en}} will not categorize anywhere by default because there is no
top-level [[:Category:Human anatomy direction adjectives]]. Use {{para|cat|-}} to disable categorization. Note that no
categorization takes place if {{para|nocat|1}} is specified in the invocation of the list template or if the page is
the same as any of the pages linked to by the language-specific hypernym(s) given in {{para|hypernym}}.
* {{para|hypernym}}: The language-specific plural form of the hypernym category (e.g. in the above example, the word for
"continents" in Swahili). Generally this should be linked to the corresponding singular, if they are different. If
specified, it is displayed after the English hypernym, following a colon. This parameter can be omitted to not display
a hypernym, and you can also specify multiple comma-separated or tilde-separated hypernyms with inline modifiers
attached to each one (essentially, following the format of a given item in {{tl|col}}).
* {{para|enhypernym}}: The English-language hypernym, e.g. `continents` in the above example. If omitted, this is
derived from the template name by chopping off the initial `list:` and anything starting with a slash. This will be
hyperlinked to the first category that the page categorizes into, if such a category exists.
* {{para|appendix}}: One or more comma-separated appendices to display after the hypernym, in parens. The appendix
should be a full pagename including the namespace `Appendix:`, or a two-part link giving a pagename and display form.
If a link is not specified, the display form will be `appendix`.
* {{para|pagename}}: Override the pagename, which should normally be a template of the form
`Template:list:<var>list name</var>/<var>langcode</var>` or
`Template:list:<var>list name</var>/<var>langcode</var>/<var>variant</var>`. The list name and language code are
parsed out of the pagename and used as the default title and language, and various other defaults are set based on the
list name.
]==]
function export.show(frame)
local raw_item_args = frame.args
local frame_parent = frame:getParent()
local raw_user_args = frame_parent.args
local topic_list_template = raw_item_args.pagename or frame_parent:getTitle()
local user_args = parse_topic_list_user_args(raw_user_args)
local list_name, lang, variant, default_props = analyze_template_name(topic_list_template)
return require(columns_module).handle_display_from_or_topic_list(
{minrows = 2, sort = true, collapse = true, lang = lang}, raw_item_args, {
topic_list_template = topic_list_template,
list_name = list_name,
variant = variant,
set_default_arguments = function(args)
set_default_arguments(args, default_props)
end,
get_title_and_formatted_cats = get_title_and_formatted_cats,
add_topic_list_params = add_topic_list_params,
make_horiz_edit_button = make_horiz_edit_button,
user_args = user_args,
}
)
end
function export.parse_directions_and_draw_compass(args, lang, sc, notr)
local m_columns = require(columns_module)
local function parse_and_format_direction(paramname)
if not args[paramname] then
return ""
end
local terms = parse_term_with_delimiters {
term = args[paramname],
paramname = paramname,
lang = lang,
sc = sc,
comma_delim = "<br />",
notr = notr,
}
return m_columns.format_item(terms, {lang = lang})
end
local parts = {}
local function ins(txt)
table.insert(parts, txt)
end
ins('{| frame=void border=0 rules=none style="background:var(--wikt-palette-paleblue, #f9f9f9);"')
ins('\n| style="text-align: left" | ')
ins(parse_and_format_direction("nw"))
ins('\n| style="text-align: center" | ')
ins(parse_and_format_direction("n"))
ins('\n| style="text-align: right" | ')
ins(parse_and_format_direction("ne"))
ins("\n|-")
ins('\n| style="text-align: left" | ')
ins(parse_and_format_direction("w"))
ins("\n| [[File:Compass rose simple plain.svg|upright=0.31]] ")
ins('\n| style="text-align: right" | ')
ins(parse_and_format_direction("e"))
ins("\n|-")
ins('\n| style="text-align: left" | ')
ins(parse_and_format_direction("sw"))
ins('\n| style="text-align: center" | ')
ins(parse_and_format_direction("s"))
ins('\n| style="text-align: right" | ')
ins(parse_and_format_direction("se"))
ins("\n|}")
return table.concat(parts)
end
function export.compass_plain(frame)
local parent_args = frame:getParent().args
local params = {
[1] = {type = "language"},
["sc"] = {type = "script"},
["notr"] = boolean,
["n"] = {},
["s"] = {},
["e"] = {},
["w"] = {},
["ne"] = {},
["nw"] = {},
["se"] = {},
["sw"] = {},
}
local args = require(parameters_module).process(parent_args, params)
return export.parse_directions_and_draw_compass(args, args[1], args.sc, args.notr)
end
function export.compass(frame)
local raw_item_args = frame.args
local frame_parent = frame:getParent()
local raw_user_args = frame_parent.args
local topic_list_template = raw_item_args.pagename or frame_parent:getTitle()
local user_args = parse_topic_list_user_args(raw_user_args)
local list_name, lang, variant, default_props = analyze_template_name(topic_list_template)
local params = {
["lang"] = {type = "language"},
["sc"] = {type = "script"},
["notr"] = boolean,
["title"] = {},
["n"] = {},
["s"] = {},
["e"] = {},
["w"] = {},
["ne"] = {},
["nw"] = {},
["se"] = {},
["sw"] = {},
}
add_topic_list_params(params)
local args = require(parameters_module).process(raw_item_args, params)
set_default_arguments(args, default_props)
local title, formatted_cats = get_title_and_formatted_cats(args, args.lang or lang, sc, {
topic_list_template = topic_list_template,
list_name = list_name,
variant = variant,
user_args = user_args,
suppress_edit_button = true,
})
local m_columns = require(columns_module)
local parts = {}
local function ins(txt)
table.insert(parts, txt)
end
ins(m_columns.construct_old_style_header(title, "horiz"))
ins(make_horiz_edit_button(topic_list_template))
ins("\n")
ins(export.parse_directions_and_draw_compass(args, args.lang or lang, sc, args.notr))
return table.concat(parts) .. (formatted_cats or "")
end
return export