Модул:category tree

Документација модула[прикажи] [уреди] [историја] [освежи]

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.

Consider using poscatboiler handlers instead.
If you are considering creating a new category tree submodule, you may find it easier to instead add a poscatboiler handler. Generally, much less code is required, and there is no need to modify Module:auto cat to recognize the categories in question. See Module:category tree/poscatboiler/data/documentation for more info. For examples, see e.g. the handlers in Module:category tree/poscatboiler/data/terms by etymology that handle categories of the form LANG terms coined by COINER and LANG terms with POS1 and POS2 etymologies.


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. (For example, {{poscatboiler}} uses Module:category tree/poscatboiler.) This 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).

Most templates accept and pass this common set of parameters. The parameters passed to the module by a template are defined by that template individually, so not every template will necessarily use all of these. {{family cat}} for example only passes the |code= parameter to the module.

The code that specifies what 'owns' the category's contents. This is usually a language code such as en, but it can also be a script code like Latn or the code of a language family, depending on how the specific template treats it.
A name for the thing that is being categorised. The submodule determines how the label is interpreted, so it depends on the template being used. Many templates use it to look up data in a table, while others may interpret it as a language code of some kind.
The script code of the items to be categorised. This is usually empty, but many categories such as those used by Mandarin Chinese can split into subcategories based on script.

General workings[уреди]

The module is based on the principle of two main kinds of category:

Basic categories are those for which the |code= parameter is not empty. These therefore 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[уреди]

This documentation is out of date.
The documentation on this page or section no longer reflects its current state, and some information may be missing or incorrect. Please help by editing the documentation page, and adding information about undocumented features, while removing information that is no longer applicable.

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.



Returns the name that is used for the category in the "breadcrumbs" at the top of the category page.



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.



Returns true either if the category contains pages but might be empty or if the category only contains categories, otherwise returns false.



Returns the name of the category that this category object represents.



Returns the description text that is shown at the top of the category page. If the category has no description, this returns nil.



Returns a table of the parent categories of this category. Each element in the table is a table itself, with two elements:

One of two possibilities: An category object representing the parent category, or a string that directly specifies the name of the parent category.
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: Fundamental » All languages » English language.



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.



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.



Returns an appendix link (such as Appendix:French verbs) if the page exists, else returns nil.



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 export = {}

local m_utilities = require("Модул:utilities")
local inFundamental = mw.loadData("Модул:category tree/data")

local show_error, check_name, link_box, show_catfix, show_categories, show_intro, show_editlink, show_pagelist,
	show_display, show_breadcrumbs, show_description, show_appendix, show_children, show_TOC

-- The main entry point.
-- This is the only function that can be invoked from a template.
function export.show(frame)
	local template = frame.args["template"]
	if not template or template == "" then
		error("The \"template\" parameter was not specified.")
	if mw.title.getCurrentTitle().nsText == "Шаблон" then
		local text = {}
		table.insert(text, "This template should be used on pages in the Категорија: namespace, ")
		table.insert(text, "and automatically generates descriptions and categorization for categories of a recognized type (see below).")
		table.insert(text, " It is implemented by [[Модул:category tree]] and its submodule [[Модул:category tree/")
		table.insert(text, template .. "]].")
		if frame.args["useautocat"] then
			table.insert(text, " It is preferable not to invoke this template directly, but to simply use ")
			table.insert(text, require("Модул:template link").format_link({"auto cat"}))
			table.insert(text, " (with no parameters), which will automatically invoke this template on appropriately-named category pages.")
		return table.concat(text)
	elseif mw.title.getCurrentTitle().nsText ~= "Категорија" then
		error("This template/module can only be used on pages in the Category: namespace.")
	local submodule = require("Модул:category tree/" .. template)
	-- Get all the parameters and the label data
	local current
	if submodule.new_main then
		current = submodule.new_main(frame)
		local info = {}
		for key, val in pairs(frame.args) do
			if val ~= "" and key ~= "useautocat" then
				info[key] = val
		info.template = nil
		current = submodule.new(info, true)
	local functions = {
	if current then
		for i, functionName in pairs(functions) do
			if type(current[functionName]) ~= "function" then
				require("Модул:debug").track{ "category tree/missing function", "category tree/missing function/" .. functionName }

	local boxes = {}
	local display = {}
	local categories = {}

	if template == "topic cat" then
		table.insert(categories, "[[Категорија:topic cat]]")
	-- 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
		table.insert(categories, "[[Категорија:Категорије са погрешном ознаком]]")
		table.insert(categories, isEmpty and "[[Категорија:Празне категорије]]" or nil)
		table.insert(display, show_error(
			"The label given to the " ..
			require("Модул:template link").format_link{template} ..
			" template is not valid. You may have mistyped it, or it simply has not been created yet. " ..
			"To add a new label, please consult the documentation of the template."))
		-- Exit here, as all code beyond here relies on current not being nil
		return table.concat(categories, "") .. table.concat(display, "\n\n")
	-- Does the category have the correct name?
	if mw.title.getCurrentTitle().text ~= current:getCategoryName() then
		table.insert(categories, "[[Категорија:Categories with incorrect name]]")
		table.insert(display, show_error(
			"Based on the parameters given to the " ..
			require("Модул:template link").format_link{template} ..
			" template, this category should be called '''[[:Категорија:" .. current:getCategoryName() .. "]]'''."))
	-- Add cleanup category for empty categories
	local canBeEmpty = current:canBeEmpty()
	if isEmpty and not canBeEmpty then
		table.insert(categories, "[[Категорија:Празне категорије]]")
	if current:isHidden() then
		table.insert(categories, "__HIDDENCAT__")

	if canBeEmpty then
		table.insert(categories, " __EXPECTUNUSEDCATEGORY__")
	table.insert(boxes, show_intro(current))
	table.insert(boxes, show_editlink(current))
	table.insert(boxes, show_related_changes())
	table.insert(boxes, show_pagelist(current))
	-- Generate the displayed information
	table.insert(display, show_display(current))
	table.insert(display, show_breadcrumbs(current))
	table.insert(display, show_description(current))
	table.insert(display, show_appendix(current))
	table.insert(display, show_children(current))
	table.insert(display, show_TOC(current))
	table.insert(display, show_catfix(current))
	show_categories(current, categories)
	return table.concat(boxes, "\n") .. "\n" .. table.concat(display, "\n\n") .. table.concat(categories, "")

function show_error(text)
	return  mw.getCurrentFrame():expandTemplate{title = "maintenance box", args = {
		image = "[[File:Ambox warning pn.svg|50px]]",
		title = "The automatically-generated contents of this category has errors.",
		text = text,

-- Check the name of the current page, and return an error if it's not right.
function check_name(current, template, info)
	local errortext = nil
	local category = nil
	if not current then
		errortext =
			"The label \"" .. (info.label or "") .. "\" given to the " ..
			require("Модул:template link").format_link{template} .. " template is not valid. " ..
			"You may have mistyped it, or it simply has not been created yet. To add a new label, please consult the documentation of the template."
		category = "[[Категорија:Категорије са погрешном ознаком]]"
	if errortext then
		return (category or "") .. show_error(errortext)
		return nil

local function get_catfix_info(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 current._info.sc and require("Модул:scripts").getByCode(current._info.sc) or nil
	return lang, sc

-- Show the "catfix" that adds language attributes and script classes to the page.
function show_catfix(current)
	local lang, sc = get_catfix_info(current)
	if lang then
		return m_utilities.catfix(lang, sc)
		return nil

-- Show the parent categories that the current category should be placed in.
function show_categories(current, categories)
	local parents = current:getParents()
	if not parents then
	local sort_override = nil
	if current.getSort then
		sort_override = current:getSort()
	for _, parent in ipairs(parents) do
		local sort = sort_override or parent.sort

		if type(parent.name) == "string" then
			table.insert(categories, "[[" .. parent.name .. "|" .. sort .. "]]")
			if string.sub(sort, 1, 1) ~= " " and not current._lang then
				sort = " " .. sort
			table.insert(categories, "[[Категорија:" .. parent.name:getCategoryName() .. "|" .. sort .. "]]")
	if current.getTopicParents then
		if current:getTopicParents() then
			for _, topic_parent in ipairs(current:getTopicParents()) do
				table.insert(categories, "[[Категорија:" .. topic_parent .. "]]")
	-- Also put the category in its corresponding "umbrella" or "by language" category.
	local umbrella = current:getUmbrella()
	if umbrella then
		local sort
		if current._lang then
			sort = current._lang:getCanonicalName()
			sort = current:getCategoryName()
		if type(umbrella) == "string" then
			table.insert(categories, "[[" .. umbrella .. "|" .. sort .. "]]")
			table.insert(categories, "[[Категорија:" .. umbrella:getCategoryName() .. "|" .. sort .. "]]")

function link_box(content)
	return "<div class=\"noprint plainlinks\" style=\"float: right; clear: both; margin: 0 0 .5em 1em; background: #f9f9f9; border: 1px #aaaaaa solid; margin-top: -1px; padding: 5px; font-weight: bold;\">"
		.. content .. "</div>"

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="Последњи уноси и друге промене на страни у ' .. title .. '">Задње промене</span>]')

function show_editlink(current)
	return link_box(
		"[" .. tostring(mw.uri.fullUrl(current:getDataModule(), "action=edit"))
		.. " Унос категорије података]")

function show_pagelist(current)
	local namespace = ""
	local info = current:getInfo()
	local lang_code = info.code
	if info.label == "citations" or info.label == "citations of undefined terms" then
		namespace = "Цитати"
	elseif lang_code then
		local lang = require("Модул:languages").getByCode(lang_code)
		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 = "Reconstruction"
			elseif lang:hasType("appendix-constructed") then
				namespace = "Додатак"
	local recent = mw.getCurrentFrame():callParserFunction{
		name = "#tag",
		args = {
			"category=" .. mw.title.getCurrentTitle().text .. "\n" ..
			"namespace=" .. namespace .. "\n" ..
			"count=10\n" ..
			"mode=ordered\n" ..
			"ordermethod=categoryadd\n" ..
	local oldest = mw.getCurrentFrame():callParserFunction{
		name = "#tag",
		args = {
			"category=" .. mw.title.getCurrentTitle().text .. "\n" ..
			"namespace=" .. namespace .. "\n" ..
			"count=10\n" ..
			"mode=ordered\n" ..
			"ordermethod=lastedit\n" ..
	return [=[
{| id="newest-and-oldest-pages" class="wikitable" style="float: right; clear: both; margin: 0 0 .5em 1em;"
! Задња додавања у категорији [[mw:Manual:Categorylinks table#cl_timestamp|category link update]]
| id="recent-additions" style="font-size:0.9em;" | ]=] .. recent .. [=[

! Најстарије стране сложене по задњем уносу
| id="oldest-pages" style="font-size:0.9em;" | ]=] .. oldest .. [=[


function show_display(current)
	if current.getDisplay then
		local display = current:getDisplay()
		if display then
			mw.getCurrentFrame():callParserFunction("DISPLAYTITLE", "Категорија:" .. current:getDisplay())
	return nil

-- Show navigational "breadcrumbs" at the top of the page.
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("^Категорија:", "")
			category = "Категорија:" .. current:getCategoryName()
			display_name, nocap = current:getBreadcrumbName()

		if not nocap then
			display_name = mw.getContentLanguage():ucfirst(display_name)

		if current.getDisplay2 then
			display_name = current:getDisplay2() or display_name
		elseif current.getDisplay then
			display_name = current:getDisplay() or display_name
		table.insert(steps, 1, "[[:" .. category .. "|" .. display_name .. "]]")
		-- Move up the "chain" by one level.
		if type(current) == "string" then
			current = nil
			current = current:getParents()
		if current then
			current = current[1].name
		elseif inFundamental[category] then
			current = "Категорија:Почетак"
	steps = table.concat(steps, " » ")
	return "<small>" .. steps .. "</small>"

-- Show the intro text that goes at the very top of the page.
function show_intro(current)
	return (current.getIntro and current:getIntro() or "")

-- Show a short description text for the category.
function show_description(current)
	return (current:getDescription() or "")

function show_appendix(current)
	local appendix
	if current.getAppendix then
		appendix = current:getAppendix()
	if appendix then
		return "For more information, see [[" .. appendix .. "]]."
		return nil

-- Show a list of child categories.
function show_children(current)
	local children = current:getChildren()
	if not children then
		return nil
	table.sort(children, function(first, second) return mw.ustring.upper(first.sort) < mw.ustring.upper(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
			child_pagetitle = "Категорија:" .. child.name:getCategoryName()
		local child_page = mw.title.new(child_pagetitle)
		local display = child_pagetitle
		if child.name.getDisplay then
			if child.name:getDisplay() then
				display = "Категорија:" .. child.name:getDisplay()
		if child_page.exists then
			local child_description =
				child.description or
				type(child.name) == "string" and child.name:gsub("^Категорија:", "") .. "." or
			table.insert(children_list, "* [[:" .. child_pagetitle .. "|" .. display .. "]]: " .. child_description)
	return table.concat(children_list, "\n")

-- Show a table of contents with links to each letter in the language's script.
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"
		-- 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"

	if current.getTOC then
		local TOC_text = current:getTOC(TOC_type)
		if TOC_text ~= true then
			return TOC_text

	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

		if not TOC_template then
			local TOC_template_normal = mw.title.new(templatename)
			if TOC_template_normal.exists then
				TOC_template = TOC_template_normal

		if TOC_template then
			return mw.getCurrentFrame():expandTemplate{title = TOC_template.text, args = {}}

	return nil

function export.test(frame)
	local template = frame.args[1]
	local submodule = require("Модул:category tree/" .. template)
	if submodule.new_main then
		current = submodule.new_main(frame)
		local info = {}
		for key, val in pairs(frame.args) do
			info[key] = val; if info[key] == "" then info[key] = nil end
		info.template = nil
		current = submodule.new(info, true)

return export