Module:category tree
- පහත දැක්වෙන උපදෙස්, Module:category tree/documentation හි පිහිටා ඇත. Module:category tree/documentation]]. [සංස්කරණය]
- ප්රයෝජනවත් සබැඳි: උප පිටු ලැයිස්තුව • සබැඳි • transclusions • testcases • sandbox (වෙනස)
This module is used for generating category boilerplate templates. It is not meant to be used directly. Rather, each template will have its own submodule, which handles the specifics of that template.
This documentation only covers the generics of the category tree system. If you are looking for documentation on a specific template, or on how to add or modify category information, see the documentation of that template.
Parameters
[සංස්කරණය]The category tree module is invoked as:
{{#invoke:category tree|show|template=name of the template|...other parameters...}}
Every template that uses this module should have a submodule of this module with the name given in the |template=
parameter. At present, only three such templates exist:
{{poscatboiler}}
uses Module:category tree/poscatboiler{{topic cat}}
uses Module:category tree/topic cat{{ws topic cat}}
uses Module:category tree/ws topic cat
The submodule should export a function named new
which takes a single parameter: a table named info
that contains the various parameters that were passed to the template initially. This function should return a new Category
object representing those parameters, or nil
if the combination of parameters was not valid (i.e. no such category exists).
General workings
[සංස්කරණය]The module is based on the principle of two main kinds of category:
Basic categories belong to a specific language (or similar) and are the "regular" categories. Examples are: Category:English nouns, Category:French templates, Category:nl:Linguistics, Category:English terms derived from Japanese, Category:Latin script characters.
Umbrella categories do not have a code, but contain all basic categories of their label, one for each code. These are the "by language" type categories. Examples are: Category:Nouns by language, Category:Templates by language, Category:Linguistics, Category:Terms derived from Japanese, Category:Characters by script.
Some templates also distinguish a third type of category, the fundamental category. This category is used as the parent category for umbrella categories.
Category objects
[සංස්කරණය]Category objects are returned by each submodule's new
function. They represent a single category in the tree. A category object has a variety of methods which may be called on it to ask for information about the category.
getBreadcrumbName
[සංස්කරණය]getBreadcrumbName()
Returns the name that is used for the category in the "breadcrumbs" at the top of the category page.
getDataModule
[සංස්කරණය]getDataModule()
Returns the name of the module which contains the data for this category. This is used to create an "edit" link on the category, which allows users to find and edit the information more easily.
canBeEmpty
[සංස්කරණය]canBeEmpty()
Returns true
either if the category contains pages but might be empty or if the category only contains categories, otherwise returns false
.
getCategoryName
[සංස්කරණය]getCategoryName()
Returns the name of the category that this category object represents.
getDescription
[සංස්කරණය]getDescription()
Returns the description text that is shown at the top of the category page. If the category has no description, this returns nil
.
getParents
[සංස්කරණය]getParents()
Returns a table of the parent categories of this category. Each element in the table is a table itself, with two elements:
.name
- One of two possibilities: An category object representing the parent category, or a string that directly specifies the name of the parent category.
.sort
- The sorting key that should be used when categorizing the current category in the parent.
If the category has no parents, this returns nil
.
If there are two or more parent categories, the first will be used to generate the breadcrumbs that are displayed at the top of the category page. For example, Category:English language is in numerous categories (All languages, West Germanic languages, Latin script languages, Braille script languages, and so on), but the first category, All languages, is the one displayed in the breadcrumbs: සැකිල්ල:serif.
getChildren
[සංස්කරණය]getChildren()
Returns a table of the child categories of this category. Each element in the table is a category object representing the child category. If the category has no children, this returns nil
.
getUmbrella
[සංස්කරණය]getUmbrella()
Returns a category object for the current category's corresponding umbrella category. If the current category is already an umbrella category, this returns nil
. It also returns nil
if the category has no umbrella category.
getAppendix
[සංස්කරණය]getAppendix()
Returns an appendix link (such as Appendix:French verbs) if the page exists, else returns nil
.
getTOCTemplateName
[සංස්කරණය]getTOCTemplateName()
Returns the title for a template used as table of contents with the namespace prefix.
The table of contents is designed for categories with terms in a certain language, The TOC template should be a small, one-line template; a larger template will be used if available with the suffix /full. For example, if getTOCTemplateName() returns Template:en-categoryTOC, {{en-categoryTOC}}
will be used as the small table of contents template (for categories with more than 200 but less than 2500 entries) and {{en-categoryTOC/full}}
as the full table of contents template (for categories with more than 2500 entries).
If the function returns nil
, no such template will be used.
local m_str_utils = require("Module:string utilities")
local m_utilities = require("Module:utilities")
local concat = table.concat
local insert = table.insert
local sort = table.sort
local split = m_str_utils.split
local uupper = m_str_utils.upper
local inFundamental = mw.loadData("Module:category tree/data")
local function show_error(text)
return require("Module:message box").maintenance(
"red",
"[[File:Ambox warning pn.svg|50px]]",
"This category is not defined in Wiktionary's category tree.",
text
)
end
-- Show the text that goes at the very top right of the page.
local function show_topright(current)
return (current.getTopright and current:getTopright() or "")
end
local function link_box(content)
return "<div class=\"noprint plainlinks\" style=\"float: right; clear: both; margin: 0 0 .5em 1em; background: var(--wikt-palette-paleblue, #f9f9f9); border: 1px var(--border-color-base, #aaaaaa) solid; margin-top: -1px; padding: 5px; font-weight: bold;\">"
.. content .. "</div>"
end
local function show_editlink(current)
return link_box(
"[" .. tostring(mw.uri.fullUrl(current:getDataModule(), "action=edit"))
.. " ප්රවර්ග දත්ත සංස්කරණය]")
end
function show_related_changes()
local title = mw.title.getCurrentTitle().fullText
return link_box(
"["
.. tostring(mw.uri.fullUrl("Special:RecentChangesLinked", {
target = title,
showlinkedto = 0,
}))
.. ' <span title="Recent edits and other changes to pages in ' .. title .. '">නව වෙනස්වීම්</span>]')
end
local function show_pagelist(current)
local namespace = "namespace="
local info = current:getInfo()
local lang_code = info.code
if info.label == "citations" or info.label == "citations of undefined terms" then
namespace = namespace .. "Citations"
elseif lang_code then
local lang = require("Module:languages").getByCode(lang_code, true, nil, nil, true)
if lang then
-- Proto-Norse (gmq-pro) is the probably language with a code ending in -pro
-- that's intended to have mostly non-reconstructed entries.
if (lang_code:find("%-pro$") and lang_code ~= "gmq-pro") or lang:hasType("reconstructed") then
namespace = namespace .. "Reconstruction"
elseif lang:hasType("appendix-constructed") then
namespace = namespace .. "Appendix"
end
end
elseif info.label:match("templates") then
namespace = namespace .. "Template"
elseif info.label:match("modules") then
namespace = namespace .. "Module"
elseif info.label:match("^Wiktionary") or info.label:match("^Pages") then
namespace = ""
end
local recent = mw.getCurrentFrame():callParserFunction{
name = "#tag",
args = {
"DynamicPageList",
"category=" .. mw.title.getCurrentTitle().text .. "\n" ..
namespace .. "\n" ..
"count=10\n" ..
"mode=ordered\n" ..
"ordermethod=categoryadd\n" ..
"order=descending"
}
}
local oldest = mw.getCurrentFrame():callParserFunction{
name = "#tag",
args = {
"DynamicPageList",
"category=" .. mw.title.getCurrentTitle().text .. "\n" ..
namespace .. "\n" ..
"count=10\n" ..
"mode=ordered\n" ..
"ordermethod=lastedit\n" ..
"order=ascending"
}
}
return [=[
{| id="newest-and-oldest-pages" class="wikitable mw-collapsible mw-collapsed" style="float: right; clear: both; margin: 0 0 .5em 1em;"
! නවතම සහ පැරණිතම පිටු
|-
| id="recent-additions" style="font-size:0.9em;" | '''Newest pages ordered by last [[mw:Manual:Categorylinks table#cl_timestamp|category link update]]:'''
]=] .. recent .. [=[
|-
| id="oldest-pages" style="font-size:0.9em;" | '''Oldest pages ordered by last edit:'''
]=] .. oldest .. [=[
|}]=]
end
-- Show navigational "breadcrumbs" at the top of the page.
local function show_breadcrumbs(current)
local steps = {}
-- Start at the current label and move our way up the "chain" from child to parent, until we can't go further.
while current do
local category = nil
local display_name = nil
local nocap = nil
if type(current) == "string" then
category = current
display_name = current:gsub("^ප්රවර්ගය:", "")
else
if not current.getCategoryName then
error("Internal error: Bad format in breadcrumb chain structure, probably a misformatted value for `parents`: " ..
mw.dumpObject(current))
end
category = "ප්රවර්ගය:" .. current:getCategoryName()
display_name, nocap = current:getBreadcrumbName()
end
if not nocap then
display_name = mw.getContentLanguage():ucfirst(display_name)
end
insert(steps, 1, "[[:" .. category .. "|" .. display_name .. "]]")
-- Move up the "chain" by one level.
if type(current) == "string" then
current = nil
else
current = current:getParents()
end
if current then
current = current[1].name
elseif inFundamental[category] then
current = "ප්රවර්ගය:මූලධර්ම"
end
end
local templateStyles = require("Module:TemplateStyles")("Module:category tree/styles.css")
local ol = mw.html.create("ol")
for i, step in ipairs(steps) do
local li = mw.html.create("li")
if i ~= 1 then
local span = mw.html.create("span")
:attr("aria-hidden", "true")
:addClass("ts-categoryBreadcrumbs-separator")
:wikitext(" » ")
li:node(span)
end
li:wikitext(step)
ol:node(li)
end
local div = mw.html.create("div")
:attr("role", "navigation")
:attr("aria-label", "Breadcrumb")
:addClass("ts-categoryBreadcrumbs")
:node(ol)
return templateStyles .. tostring(div)
end
-- Show a short description text for the category.
local function show_description(current)
return (current:getDescription() or "")
end
local function show_appendix(current)
local appendix
if current.getAppendix then
appendix = current:getAppendix()
end
if appendix then
return "For more information, see [[" .. appendix .. "]]."
else
return nil
end
end
-- Show a list of child categories.
local function show_children(current)
local children = current:getChildren()
if not children then
return nil
end
sort(children, function(first, second) return uupper(first.sort) < uupper(second.sort) end)
local children_list = {}
for _, child in ipairs(children) do
local child_pagetitle
if type(child.name) == "string" then
child_pagetitle = child.name
else
child_pagetitle = "ප්රවර්ගය:" .. child.name:getCategoryName()
end
local child_page = mw.title.new(child_pagetitle)
if child_page.exists then
local child_description =
child.description or
type(child.name) == "string" and child.name:gsub("^ප්රවර්ගය:", "") .. "." or
child.name:getDescription("child")
insert(children_list, "* [[:" .. child_pagetitle .. "]]: " .. child_description)
end
end
return concat(children_list, "\n")
end
-- Show a table of contents with links to each letter in the language's script.
local function show_TOC(current)
local titleText = mw.title.getCurrentTitle().text
local inCategoryPages = mw.site.stats.pagesInCategory(titleText, "pages")
local inCategorySubcats = mw.site.stats.pagesInCategory(titleText, "subcats")
local TOC_type
-- Compute type of table of contents required.
if inCategoryPages > 2500 or inCategorySubcats > 2500 then
TOC_type = "full"
elseif inCategoryPages > 200 or inCategorySubcats > 200 then
TOC_type = "normal"
else
-- No (usual) need for a TOC if all pages or subcategories can fit on one page;
-- but allow this to be overridden by a custom TOC handler.
TOC_type = "none"
end
if current.getTOC then
local TOC_text = current:getTOC(TOC_type)
if TOC_text ~= true then
return TOC_text
end
end
if TOC_type ~= "none" then
local templatename = current:getTOCTemplateName()
local TOC_template
if TOC_type == "full" then
-- This category is very large, see if there is a "full" version of the TOC.
local TOC_template_full = mw.title.new(templatename .. "/full")
if TOC_template_full.exists then
TOC_template = TOC_template_full
end
end
if not TOC_template then
local TOC_template_normal = mw.title.new(templatename)
if TOC_template_normal.exists then
TOC_template = TOC_template_normal
end
end
if TOC_template then
return mw.getCurrentFrame():expandTemplate{title = TOC_template.text, args = {}}
end
end
return nil
end
-- Show the "catfix" that adds language attributes and script classes to the page.
local function show_catfix(current)
local lang, sc
if current.getCatfixInfo then
lang, sc = current:getCatfixInfo()
elseif not (current._info and current._info.no_catfix) then
-- FIXME: This is hacky and should be removed.
lang = current._lang
sc = current._info and require("Module:scripts").getByCode(current._info.sc, true, nil, true) or nil
end
if lang then
return m_utilities.catfix(lang, sc)
else
return nil
end
end
-- Show the parent categories that the current category should be placed in.
local function show_categories(current, categories)
local parents = current:getParents()
if not parents then
return
end
for _, parent in ipairs(parents) do
local sortkey = type(parent.sort) == "table" and parent.sort:makeSortKey() or parent.sort
if type(parent.name) == "string" then
insert(categories, "[[" .. parent.name .. "|" .. sortkey .. "]]")
else
insert(categories, "[[ප්රවර්ගය:" .. parent.name:getCategoryName() .. "|" .. sortkey .. "]]")
end
end
-- Also put the category in its corresponding "umbrella" or "by language" category.
local umbrella = current:getUmbrella()
if umbrella then
-- FIXME: use a language-neutral sorting function like the Unicode Collation Algorithm.
local sortkey = current._lang and current._lang:getCanonicalName() or current:getCategoryName()
sortkey = require("Module:languages").getByCode("en", true, nil, nil, true):makeSortKey(sortkey)
if type(umbrella) == "string" then
insert(categories, "[[" .. umbrella .. "|" .. sortkey .. "]]")
else
insert(categories, "[[ප්රවර්ගය:" .. umbrella:getCategoryName() .. "|" .. sortkey .. "]]")
end
end
end
local function generate_output(current)
local functions = {
"getBreadcrumbName",
"getDataModule",
"canBeEmpty",
"getDescription",
"getParents",
"getChildren",
"getUmbrella",
"getAppendix",
"getTOCTemplateName",
}
if current then
for _, functionName in pairs(functions) do
if type(current[functionName]) ~= "function" then
require("Module:debug").track{ "category tree/missing function", "category tree/missing function/" .. functionName }
end
end
end
local boxes = {}
local display = {}
local categories = {}
-- Check if the category is empty
local isEmpty = mw.site.stats.pagesInCategory(mw.title.getCurrentTitle().text, "all") == 0
-- Are the parameters valid?
if not current then
-- WARNING: The following name is hardcoded and checked for in [[Module:auto cat]]. If you change it, you
-- also need to change that module.
insert(categories, "[[ප්රවර්ගය:Categories that are not defined in the category tree]]")
insert(categories, isEmpty and "[[ප්රවර්ගය:හිස් ප්රවර්ග]]" or nil)
insert(display, show_error(
"Double-check the category name for typos. <br>" ..
"[[Special:Search/Category: " .. mw.title.getCurrentTitle().text:gsub("^.+:", ""):gsub(" ", "~2 ") .. '~2|Search existing categories]] to check if this category should be created under a different name (for example, "Fruits" instead of "Fruit"). <br>' ..
"To add a new category to Wiktionary's category tree, please consult " .. mw.getCurrentFrame():expandTemplate{title = "section link", args = {
"Help:Category#How_to_create_a_category",
}} .. "."))
-- Exit here, as all code beyond here relies on current not being nil
return concat(categories, "") .. concat(display, "\n\n")
end
-- Does the category have the correct name?
local currentName = current:getCategoryName()
local correctName = mw.title.getCurrentTitle().text == currentName
if not correctName then
insert(categories, "[[ප්රවර්ගය:Categories with incorrect name]]")
insert(display, show_error(
"Based on the data in the category tree, this category should be called '''[[:ප්රවර්ගය:" .. currentName .. "]]'''."))
end
-- Add cleanup category for empty categories.
local canBeEmpty = current:canBeEmpty()
if canBeEmpty and correctName then
insert(categories, " __EXPECTUNUSEDCATEGORY__")
elseif isEmpty then
insert(categories, "[[ප්රවර්ගය:හිස් ප්රවර්ග]]")
end
if current:isHidden() then
insert(categories, "__HIDDENCAT__")
end
-- Put all the float-right stuff into a <div> that does not clear, so that float-left stuff like the breadcrumbs and
-- description can go opposite the float-right stuff without vertical space.
insert(boxes, "<div style=\"float: right;\">")
insert(boxes, show_topright(current))
insert(boxes, show_editlink(current))
insert(boxes, show_related_changes())
insert(boxes, show_pagelist(current))
insert(boxes, "</div>")
-- Generate the displayed information
insert(display, show_breadcrumbs(current))
insert(display, show_description(current))
insert(display, show_appendix(current))
insert(display, show_children(current))
insert(display, show_TOC(current))
insert(display, show_catfix(current))
insert(display, '<br class="clear-both-in-vector-2022-only">')
show_categories(current, categories)
return concat(boxes, "\n") .. "\n" .. concat(display, "\n\n") .. concat(categories, "")
end
local export = {}
function export.split_lang_label(titleObject)
local getByCanonicalName = require("Module:languages").getByCanonicalName
local text = titleObject.text
-- Progressively remove a word from the potential canonical name until it
-- matches an actual canonical name.
local words = split(text, " ", true)
for i = #words - 1, 1, -1 do
local lang = getByCanonicalName(concat(words, " ", 1, i))
if lang then
return lang, concat(words, " ", i + 1)
end
end
return nil, text
end
-- The main entry point from [[Module:auto cat]].
-- TODO: merge [[Module:auto cat]] into this module.
function export.main(submodule, info)
submodule = require("Module:category tree/" .. submodule)
return generate_output(submodule.main(info))
end
-- TODO: new test entrypoint.
return export