<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://linguifex.com/w/index.php?action=history&amp;feed=atom&amp;title=Module%3Acategory_tree%2Ftopic</id>
	<title>Module:category tree/topic - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://linguifex.com/w/index.php?action=history&amp;feed=atom&amp;title=Module%3Acategory_tree%2Ftopic"/>
	<link rel="alternate" type="text/html" href="https://linguifex.com/w/index.php?title=Module:category_tree/topic&amp;action=history"/>
	<updated>2026-04-22T06:44:04Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.43.6</generator>
	<entry>
		<id>https://linguifex.com/w/index.php?title=Module:category_tree/topic&amp;diff=494858&amp;oldid=prev</id>
		<title>Sware: 1 revision imported</title>
		<link rel="alternate" type="text/html" href="https://linguifex.com/w/index.php?title=Module:category_tree/topic&amp;diff=494858&amp;oldid=prev"/>
		<updated>2026-04-21T11:22:47Z</updated>

		<summary type="html">&lt;p&gt;1 revision imported&lt;/p&gt;
&lt;table style=&quot;background-color: #fff; color: #202122;&quot; data-mw=&quot;interface&quot;&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;en&quot;&gt;
				&lt;td colspan=&quot;1&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Older revision&lt;/td&gt;
				&lt;td colspan=&quot;1&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Revision as of 11:22, 21 April 2026&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-notice&quot; lang=&quot;en&quot;&gt;&lt;div class=&quot;mw-diff-empty&quot;&gt;(No difference)&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;</summary>
		<author><name>Sware</name></author>
	</entry>
	<entry>
		<id>https://linguifex.com/w/index.php?title=Module:category_tree/topic&amp;diff=494857&amp;oldid=prev</id>
		<title>wikt&gt;WingerBot: use breadcrumb_and_first_sort_key instead of breadcrumb_and_first_sort_base in category tree modules as the sort base is language-specific which normally doesn&#039;t apply here (manually assisted)</title>
		<link rel="alternate" type="text/html" href="https://linguifex.com/w/index.php?title=Module:category_tree/topic&amp;diff=494857&amp;oldid=prev"/>
		<updated>2026-02-15T23:56:12Z</updated>

		<summary type="html">&lt;p&gt;use breadcrumb_and_first_sort_key instead of breadcrumb_and_first_sort_base in category tree modules as the sort base is language-specific which normally doesn&amp;#039;t apply here (manually assisted)&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;local raw_handlers = {}&lt;br /&gt;
local raw_categories = {}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[=[&lt;br /&gt;
This module implements the topic category subsystem. It is currently implemented with a single raw handler that&lt;br /&gt;
handlers both language-specific and umbrella topic categories, and a corresponding handler for thesaurus categories.&lt;br /&gt;
The topmost topic category [[:Category:All topics]] is special and potentially could be handled as a separate raw&lt;br /&gt;
category, but currently it&amp;#039;s handled as part of the raw topic handler. The topmost thesaurus category&lt;br /&gt;
[[:Category:Thesaurus]] is in fact handled as a raw category.&lt;br /&gt;
]=]&lt;br /&gt;
&lt;br /&gt;
local functions_module = &amp;quot;Module:fun&amp;quot;&lt;br /&gt;
local labels_utilities_module = &amp;quot;Module:labels/utilities&amp;quot;&lt;br /&gt;
local languages_module = &amp;quot;Module:languages&amp;quot;&lt;br /&gt;
local string_pattern_escape_module = &amp;quot;Module:string/patternEscape&amp;quot;&lt;br /&gt;
local string_replacement_escape_module = &amp;quot;Module:string/replacementEscape&amp;quot;&lt;br /&gt;
local string_utilities_module = &amp;quot;Module:string utilities&amp;quot;&lt;br /&gt;
local table_module = &amp;quot;Module:table&amp;quot;&lt;br /&gt;
&lt;br /&gt;
local topic_data_module = &amp;quot;Module:category tree/topic/data&amp;quot;&lt;br /&gt;
local topic_utilities_module = &amp;quot;Module:category tree/topic/utilities&amp;quot;&lt;br /&gt;
local thesaurus_data_module = &amp;quot;Module:category tree/topic/thesaurus data&amp;quot;&lt;br /&gt;
&lt;br /&gt;
local concat = table.concat&lt;br /&gt;
local insert = table.insert&lt;br /&gt;
local dump = mw.dumpObject&lt;br /&gt;
local is_callable = require(functions_module).is_callable&lt;br /&gt;
local pattern_escape = require(string_pattern_escape_module)&lt;br /&gt;
local replacement_escape = require(string_replacement_escape_module)&lt;br /&gt;
local split = require(string_utilities_module).split&lt;br /&gt;
&lt;br /&gt;
local type_data = {&lt;br /&gt;
	[&amp;quot;related-to&amp;quot;] = {&lt;br /&gt;
		desc = &amp;quot;terms related to&amp;quot;,&lt;br /&gt;
		additional = &amp;quot;&amp;#039;&amp;#039;&amp;#039;NOTE&amp;#039;&amp;#039;&amp;#039;: This is a \&amp;quot;related-to\&amp;quot; category. It should contain terms directly related to &amp;quot; ..&lt;br /&gt;
		&amp;quot;{{{topic}}}. Please do not include terms that merely have a tangential connection to {{{topic}}}. &amp;quot; ..&lt;br /&gt;
		&amp;quot;Be aware that terms for types or instances of this topic often go in a separate category.&amp;quot;,&lt;br /&gt;
	},&lt;br /&gt;
	set = {&lt;br /&gt;
		desc = &amp;quot;terms for types or instances of&amp;quot;,&lt;br /&gt;
		additional = &amp;quot;&amp;#039;&amp;#039;&amp;#039;NOTE&amp;#039;&amp;#039;&amp;#039;: This is a set category. It should contain terms for {{{topic}}}, not merely &amp;quot; ..&lt;br /&gt;
		&amp;quot;terms related to {{{topic}}}. It may contain more general terms (e.g. types of {{{topic}}}) or more &amp;quot; ..&lt;br /&gt;
		&amp;quot;specific terms (e.g. names of specific {{{topic}}}), although there may be related categories &amp;quot;..&lt;br /&gt;
		&amp;quot;specifically for these types of terms.&amp;quot;,&lt;br /&gt;
	},&lt;br /&gt;
	name = {&lt;br /&gt;
		desc = &amp;quot;names of specific&amp;quot;,&lt;br /&gt;
		additional = &amp;quot;&amp;#039;&amp;#039;&amp;#039;NOTE&amp;#039;&amp;#039;&amp;#039;: This is a name category. It should contain names of specific {{{topic}}}, not &amp;quot; ..&lt;br /&gt;
		&amp;quot;merely terms related to {{{topic}}}, and should also not contain general terms for types of {{{topic}}}.&amp;quot;,&lt;br /&gt;
	},&lt;br /&gt;
	type = {&lt;br /&gt;
		desc = &amp;quot;terms for types of&amp;quot;,&lt;br /&gt;
		additional = &amp;quot;&amp;#039;&amp;#039;&amp;#039;NOTE&amp;#039;&amp;#039;&amp;#039;: This is a type category. It should contain terms for types of {{{topic}}}, not &amp;quot; ..&lt;br /&gt;
		&amp;quot;merely terms related to {{{topic}}}, and should also not contain names of specific {{{topic}}}.&amp;quot;,&lt;br /&gt;
	},&lt;br /&gt;
	grouping = {&lt;br /&gt;
		desc = &amp;quot;categories concerning more specific variants of&amp;quot;,&lt;br /&gt;
		additional = &amp;quot;&amp;#039;&amp;#039;&amp;#039;NOTE&amp;#039;&amp;#039;&amp;#039;: This is a grouping category. It should not directly contain any terms, but &amp;quot; ..&lt;br /&gt;
		&amp;quot;only subcategories. If there are any terms directly in this category, please move them to a subcategory.&amp;quot;,&lt;br /&gt;
	},&lt;br /&gt;
	toplevel = {&lt;br /&gt;
		desc = &amp;quot;UNUSED&amp;quot;, -- all categories of this type hardcode their description&lt;br /&gt;
		additional = &amp;quot;&amp;#039;&amp;#039;&amp;#039;NOTE&amp;#039;&amp;#039;&amp;#039;: This is a top-level list category. It should not directly contain any terms, but &amp;quot; ..&lt;br /&gt;
		&amp;quot;only a {{{topic}}}.&amp;quot;,&lt;br /&gt;
	},&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function invalid_type(types)&lt;br /&gt;
	local valid_types = {}&lt;br /&gt;
	for typ, _ in pairs(type_data) do&lt;br /&gt;
		insert(valid_types, (&amp;quot;&amp;#039;%s&amp;#039;&amp;quot;):format(typ))&lt;br /&gt;
	end&lt;br /&gt;
	error((&amp;quot;Invalid type &amp;#039;%s&amp;#039;, should be one or more of %s, comma-separated&amp;quot;)&lt;br /&gt;
		:format(types, mw.text.listToText(valid_types)))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function split_types(types)&lt;br /&gt;
	types = types or &amp;quot;related-to&amp;quot;&lt;br /&gt;
	local splitvals = split(types, &amp;quot;%s*,%s*&amp;quot;)&lt;br /&gt;
	for i, typ in ipairs(splitvals) do&lt;br /&gt;
		-- FIXME: Temporary&lt;br /&gt;
		if typ == &amp;quot;topic&amp;quot; then&lt;br /&gt;
			typ = &amp;quot;related-to&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
		if not type_data[typ] then&lt;br /&gt;
			invalid_type(types)&lt;br /&gt;
		end&lt;br /&gt;
		splitvals[i] = typ&lt;br /&gt;
	end&lt;br /&gt;
	return splitvals&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function gsub_escaping_replacement(str, from, to)&lt;br /&gt;
	return (str:gsub(pattern_escape(from), replacement_escape(to)))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function ucfirst(txt)&lt;br /&gt;
	local italics, raw_txt = txt:match(&amp;quot;^(&amp;#039;*)(.-)$&amp;quot;)&lt;br /&gt;
	return italics .. mw.getContentLanguage():ucfirst(raw_txt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function lcfirst(txt)&lt;br /&gt;
	local italics, raw_txt = txt:match(&amp;quot;^(&amp;#039;*)(.-)$&amp;quot;)&lt;br /&gt;
	return italics .. mw.getContentLanguage():lcfirst(raw_txt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function convert_spec_to_string(data, desc)&lt;br /&gt;
	if not desc then&lt;br /&gt;
		return desc&lt;br /&gt;
	end&lt;br /&gt;
	local desc_type = type(desc)&lt;br /&gt;
	if desc_type == &amp;quot;string&amp;quot; then&lt;br /&gt;
		return desc&lt;br /&gt;
	elseif desc_type == &amp;quot;number&amp;quot; then&lt;br /&gt;
		return tostring(desc)&lt;br /&gt;
	elseif not is_callable(desc) then&lt;br /&gt;
		error(&amp;quot;Internal error: `desc` must be a string, number, function, callable table or nil; received a &amp;quot; ..&lt;br /&gt;
			desc_type)&lt;br /&gt;
	end&lt;br /&gt;
	desc = desc {&lt;br /&gt;
		lang = data.lang,&lt;br /&gt;
		sc = data.sc,&lt;br /&gt;
		label = data.label,&lt;br /&gt;
		category = data.category,&lt;br /&gt;
		topic_data = data.topdata,&lt;br /&gt;
	}&lt;br /&gt;
	if not desc then&lt;br /&gt;
		return desc&lt;br /&gt;
	end&lt;br /&gt;
	desc_type = type(desc)&lt;br /&gt;
	if desc_type == &amp;quot;string&amp;quot; then&lt;br /&gt;
		return desc&lt;br /&gt;
	end&lt;br /&gt;
	error(&amp;quot;Internal error: the value returned by `desc` must be a string or nil; received a &amp;quot; .. desc_type)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function get_and_cache(data, obj, key)&lt;br /&gt;
	local val = convert_spec_to_string(data, obj[key])&lt;br /&gt;
	obj[key] = val&lt;br /&gt;
	return val&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function process_default(desc)&lt;br /&gt;
	local stripped_desc = desc&lt;br /&gt;
	local no_singularize, wikify, add_the&lt;br /&gt;
	while true do&lt;br /&gt;
		local new_stripped_desc = stripped_desc:match(&amp;quot;^(.+) no singularize$&amp;quot;)&lt;br /&gt;
		if new_stripped_desc then&lt;br /&gt;
			no_singularize = true&lt;br /&gt;
		end&lt;br /&gt;
		if not new_stripped_desc then&lt;br /&gt;
			new_stripped_desc = stripped_desc:match(&amp;quot;^(.+) wikify$&amp;quot;)&lt;br /&gt;
			if new_stripped_desc then&lt;br /&gt;
				wikify = true&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		if not new_stripped_desc then&lt;br /&gt;
			new_stripped_desc = stripped_desc:match(&amp;quot;^(.+) with the$&amp;quot;)&lt;br /&gt;
			if new_stripped_desc then&lt;br /&gt;
				add_the = true&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		if new_stripped_desc then&lt;br /&gt;
			stripped_desc = new_stripped_desc&lt;br /&gt;
		else&lt;br /&gt;
			break&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	if stripped_desc == &amp;quot;default&amp;quot; then&lt;br /&gt;
		return true, no_singularize, wikify, add_the&lt;br /&gt;
	else&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function format_desc(data, desc)&lt;br /&gt;
	local desc_parts = {}&lt;br /&gt;
	local types = split_types(data.topdata.type)&lt;br /&gt;
	for _, typ in ipairs(types) do&lt;br /&gt;
		insert(desc_parts, type_data[typ].desc .. &amp;quot; &amp;quot; .. desc)&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;quot;{{{langname}}} &amp;quot; .. require(table_module).serialCommaJoin(desc_parts) .. &amp;quot;.&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local substitute_template_specs&lt;br /&gt;
&lt;br /&gt;
local function format_displaytitle(data, include_lang_prefix, upcase)&lt;br /&gt;
	local topdata, lang, label = data.topdata, data.lang, data.label&lt;br /&gt;
	local displaytitle = substitute_template_specs(data, topdata.displaytitle)&lt;br /&gt;
	if not displaytitle then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
	if upcase then&lt;br /&gt;
		displaytitle = ucfirst(displaytitle)&lt;br /&gt;
	end&lt;br /&gt;
	if include_lang_prefix and lang then&lt;br /&gt;
		displaytitle = (&amp;quot;%s:%s&amp;quot;):format(lang:getCode(), displaytitle)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return displaytitle&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function get_breadcrumb(data)&lt;br /&gt;
	local topdata, lang, label = data.topdata, data.lang, data.label&lt;br /&gt;
	local ret&lt;br /&gt;
&lt;br /&gt;
	if not lang and topdata.umbrella then&lt;br /&gt;
		ret = topdata.umbrella.breadcrumb or topdata.umbrella.breadcrumb_and_first_sort_key&lt;br /&gt;
	end&lt;br /&gt;
	if not ret then&lt;br /&gt;
		ret = topdata.breadcrumb or topdata.breadcrumb_and_first_sort_key or&lt;br /&gt;
			format_displaytitle(data, false, &amp;quot;upcase&amp;quot;) or label&lt;br /&gt;
	end&lt;br /&gt;
	if type(ret) == &amp;quot;string&amp;quot; or type(ret) == &amp;quot;number&amp;quot; then&lt;br /&gt;
		ret = {name = ret}&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local name = substitute_template_specs(data, ret.name)&lt;br /&gt;
	local nocap = ret.nocap&lt;br /&gt;
&lt;br /&gt;
	return {name = name, nocap = nocap}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function make_category_name(lang, label)&lt;br /&gt;
	if lang then&lt;br /&gt;
		return lang:getCode() .. &amp;quot;:&amp;quot; .. ucfirst(label)&lt;br /&gt;
	else&lt;br /&gt;
		return ucfirst(label)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function replace_special_descriptions(data, desc)&lt;br /&gt;
	if not desc then&lt;br /&gt;
		return desc&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if desc:find(&amp;quot;^=&amp;quot;) then&lt;br /&gt;
		desc = desc:gsub(&amp;quot;^=&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
		return format_desc(data, desc)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local is_default, no_singularize, wikify, add_the = process_default(desc)&lt;br /&gt;
	if is_default then&lt;br /&gt;
		local linked_label = require(topic_utilities_module).link_label(data.label, no_singularize, wikify)&lt;br /&gt;
		if add_the then&lt;br /&gt;
			linked_label = &amp;quot;the &amp;quot; .. linked_label&lt;br /&gt;
		end&lt;br /&gt;
		return format_desc(data, linked_label)&lt;br /&gt;
	else&lt;br /&gt;
		return desc&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function get_displaytitle_or_label(data)&lt;br /&gt;
	return format_displaytitle(data, false) or data.label&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function process_default_add_the(data, topic)&lt;br /&gt;
	local is_default, _, _, add_the = process_default(topic)&lt;br /&gt;
	if is_default then&lt;br /&gt;
		topic = get_displaytitle_or_label(data)&lt;br /&gt;
		if add_the then&lt;br /&gt;
			topic = &amp;quot;the &amp;quot; .. topic&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return topic, is_default&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
substitute_template_specs = function(data, desc)&lt;br /&gt;
	desc = convert_spec_to_string(data, desc)&lt;br /&gt;
	if not desc then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local topdata, lang, label = data.topdata, data.lang, data.label&lt;br /&gt;
	if desc:find(&amp;quot;{{{umbrella_msg}}}&amp;quot;) then&lt;br /&gt;
		local catname = ucfirst(label)&lt;br /&gt;
		desc = gsub_escaping_replacement(desc, &amp;quot;{{{umbrella_msg}}}&amp;quot;,&lt;br /&gt;
			&amp;quot;This category contains no dictionary entries, only other categories. The subcategories are of two &amp;quot; ..&lt;br /&gt;
			&amp;quot;sorts:\n\n* Subcategories named like \&amp;quot;{{{thespref}}}aa:&amp;quot; .. catname ..&lt;br /&gt;
			&amp;quot;\&amp;quot; (with a prefixed language code) are categories of terms in specific languages. &amp;quot; ..&lt;br /&gt;
			&amp;quot;You may be interested especially in [[:Category:{{{thespref}}}en:&amp;quot; .. catname .. &amp;quot;]], for English terms.\n&amp;quot; ..&lt;br /&gt;
			&amp;quot;* Subcategories of this one named without the prefixed language code are further categories just like &amp;quot; ..&lt;br /&gt;
			&amp;quot;this one, but devoted to finer topics.&amp;quot;&lt;br /&gt;
		)&lt;br /&gt;
	end&lt;br /&gt;
	if desc:find(&amp;quot;{{{topic}}}&amp;quot;) then&lt;br /&gt;
		-- Compute the value for {{{topic}}}. If the user specified `topic`, use it. (If we&amp;#039;re an umbrella category,&lt;br /&gt;
		-- allow a separate value for `umbrella.topic`, falling back to `topic`.) Otherwise, see if the description&lt;br /&gt;
		-- was specified as &amp;#039;default&amp;#039; or a variant; if so, parse it to determine whether to add &amp;quot;the&amp;quot; to the label.&lt;br /&gt;
		-- Otherwise, just use the label directly.&lt;br /&gt;
		local topic = not lang and topdata.umbrella and topdata.umbrella.topic or topdata.topic&lt;br /&gt;
		if topic then&lt;br /&gt;
			topic = process_default_add_the(data, topic)&lt;br /&gt;
		else&lt;br /&gt;
			local desc&lt;br /&gt;
			if not lang then&lt;br /&gt;
				desc = topdata.umbrella and get_and_cache(data, topdata.umbrella, &amp;quot;description&amp;quot;) or&lt;br /&gt;
					get_and_cache(data, topdata, &amp;quot;umbrella_description&amp;quot;)&lt;br /&gt;
			end&lt;br /&gt;
			desc = desc or get_and_cache(data, topdata, &amp;quot;description&amp;quot;)&lt;br /&gt;
			local defaulted_desc, is_default = process_default_add_the(data, desc)&lt;br /&gt;
			if is_default then&lt;br /&gt;
				topic = defaulted_desc&lt;br /&gt;
			else&lt;br /&gt;
				topic = get_displaytitle_or_label(data)&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		desc = gsub_escaping_replacement(desc, &amp;quot;{{{topic}}}&amp;quot;, topic)&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	desc = desc:gsub(&amp;quot;{{{thespref}}}&amp;quot;, data.thesaurus_data and &amp;quot;Thesaurus:&amp;quot; or &amp;quot;&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
	return desc&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function process_box(data, def_topright_parts, val, pattern)&lt;br /&gt;
	if not val then&lt;br /&gt;
		return&lt;br /&gt;
	end&lt;br /&gt;
	local defval = ucfirst(data.label)&lt;br /&gt;
	if type(val) ~= &amp;quot;table&amp;quot; then&lt;br /&gt;
		val = {val}&lt;br /&gt;
	end&lt;br /&gt;
	for _, v in ipairs(val) do&lt;br /&gt;
		if v == true then&lt;br /&gt;
			insert(def_topright_parts, pattern:format(defval))&lt;br /&gt;
		else&lt;br /&gt;
			insert(def_topright_parts, pattern:format(v))&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function get_topright(data)&lt;br /&gt;
	local topdata, lang = data.topdata, data.lang&lt;br /&gt;
	local def_topright_parts = {}&lt;br /&gt;
	process_box(data, def_topright_parts, topdata.wp, &amp;quot;{{wp|%s}}&amp;quot;)&lt;br /&gt;
	process_box(data, def_topright_parts, topdata.wpcat, &amp;quot;{{wp|cat=%s}}&amp;quot;)&lt;br /&gt;
	process_box(data, def_topright_parts, topdata.commonscat, &amp;quot;{{commonscat|%s}}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
	local def_topright&lt;br /&gt;
	if #def_topright_parts &amp;gt; 0 then&lt;br /&gt;
		def_topright = concat(def_topright_parts, &amp;quot;\n&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if lang then&lt;br /&gt;
		return substitute_template_specs(data, topdata.topright or def_topright)&lt;br /&gt;
	else&lt;br /&gt;
		return topdata.umbrella and substitute_template_specs(data, topdata.umbrella.topright) or&lt;br /&gt;
			substitute_template_specs(data, def_topright)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function remove_lang_params(desc)&lt;br /&gt;
	desc = desc:gsub(&amp;quot;^{{{langname}}} &amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
	desc = desc:gsub(&amp;quot;{{{langcode}}}:&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
	desc = desc:gsub(&amp;quot;^{{{langcode}}} &amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
	desc = desc:gsub(&amp;quot;^{{{langcat}}} &amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
	return desc&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function get_additional_msg(data)&lt;br /&gt;
	local types = split_types(data.topdata.type)&lt;br /&gt;
	if #types &amp;gt; 1 then&lt;br /&gt;
		local parts = {&amp;quot;&amp;#039;&amp;#039;&amp;#039;NOTE&amp;#039;&amp;#039;&amp;#039;: This is a mixed category. It may contain terms of any of the following category types:&amp;quot;}&lt;br /&gt;
		for i, typ in ipairs(types) do&lt;br /&gt;
			insert(parts, (&amp;quot;* %s {{{topic}}}%s&amp;quot;):format(type_data[typ].desc, i == #types and &amp;quot;.&amp;quot; or &amp;quot;;&amp;quot;))&lt;br /&gt;
		end&lt;br /&gt;
		insert(parts, &amp;quot;&amp;#039;&amp;#039;&amp;#039;WARNING&amp;#039;&amp;#039;&amp;#039;: Such categories are strongly dispreferred and should be split into separate per-type categories.&amp;quot;)&lt;br /&gt;
		return concat(parts, &amp;quot;\n&amp;quot;)&lt;br /&gt;
	elseif label == &amp;quot;all topics&amp;quot; then&lt;br /&gt;
		return &amp;quot;&amp;#039;&amp;#039;&amp;#039;NOTE&amp;#039;&amp;#039;&amp;#039;: This is the topmost topic category for {{{langname}}}. It should not directly contain &amp;quot; ..&lt;br /&gt;
		&amp;quot;any terms, but only lists of topic categories organized by type.&amp;quot;&lt;br /&gt;
	else&lt;br /&gt;
		return type_data[types[1]].additional&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function get_labels_categorizing(data)&lt;br /&gt;
	local m_labels_utilities = require(labels_utilities_module)&lt;br /&gt;
	return m_labels_utilities.format_labels_categorizing(&lt;br /&gt;
		m_labels_utilities.find_labels_for_category(data.label, &amp;quot;topic&amp;quot;, data.lang), nil, data.lang)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Return the description along with the text following and preceding the description. The description and additional&lt;br /&gt;
-- (i.e. following) text are returned in the form of closures so the work of calculating the text (which can be&lt;br /&gt;
-- expensive, especially in the case of the additional text, where get_labels_categorizing() scans the entire set of&lt;br /&gt;
-- labels for any that categorize into this category) is not done when not needed, e.g. in higher levels of the&lt;br /&gt;
-- breadcrumb chain, where only the breadcrumb and parents (in fact, really just the first parent) are actually needed.&lt;br /&gt;
local function get_description_additional_preceding(data)&lt;br /&gt;
	local topdata, lang, label = data.topdata, data.lang, data.label&lt;br /&gt;
	local desc, additional, preceding&lt;br /&gt;
&lt;br /&gt;
	-- This is kind of hacky, but it works for now.&lt;br /&gt;
	local function postprocess_thesaurus(txt)&lt;br /&gt;
		if not txt then&lt;br /&gt;
			return nil&lt;br /&gt;
		end&lt;br /&gt;
		if not data.thesaurus_data then&lt;br /&gt;
			return txt&lt;br /&gt;
		end&lt;br /&gt;
		txt = txt:gsub(&amp;quot; terms([ .,])&amp;quot;, &amp;quot; thesaurus entries%1&amp;quot;)&lt;br /&gt;
		return txt&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if lang then&lt;br /&gt;
		desc = function()&lt;br /&gt;
			return postprocess_thesaurus(substitute_template_specs(data,&lt;br /&gt;
				replace_special_descriptions(data, get_and_cache(data, topdata, &amp;quot;description&amp;quot;))))&lt;br /&gt;
		end&lt;br /&gt;
		preceding = topdata.preceding&lt;br /&gt;
		additional = function()&lt;br /&gt;
			local additional_parts = {}&lt;br /&gt;
			if topdata.additional then&lt;br /&gt;
				insert(additional_parts, topdata.additional)&lt;br /&gt;
			end&lt;br /&gt;
			if not data.thesaurus_data then&lt;br /&gt;
				insert(additional_parts, get_additional_msg(data))&lt;br /&gt;
				local labels_msg = get_labels_categorizing(data)&lt;br /&gt;
				if labels_msg then&lt;br /&gt;
					insert(additional_parts, labels_msg)&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
			return postprocess_thesaurus(substitute_template_specs(data, concat(additional_parts, &amp;quot;\n\n&amp;quot;)))&lt;br /&gt;
		end&lt;br /&gt;
	else&lt;br /&gt;
		if label == &amp;quot;all topics&amp;quot; then&lt;br /&gt;
			desc = &amp;quot;This is the topmost topic category for all languages.&amp;quot;&lt;br /&gt;
			additional = &amp;quot;It contains no dictionary entries, only other categories. The subcategories are of two &amp;quot; ..&lt;br /&gt;
				&amp;quot;sorts:\n\n&amp;quot; ..&lt;br /&gt;
				&amp;quot;* Subcategories listed at the beginning, without a prefixed language code, are grouping &amp;quot; ..&lt;br /&gt;
				&amp;quot;categories similar to this category, but are devoted to general subject areas. Under them are &amp;quot; ..&lt;br /&gt;
				&amp;quot;finer-grained subject areas.\n&amp;quot; ..&lt;br /&gt;
				&amp;quot;* Subcategories named like \&amp;quot;aa:All topics\&amp;quot; (with a prefixed language code) are top-level &amp;quot; ..&lt;br /&gt;
				&amp;quot;categories like this one, but for specific languages. You may be interested especially in &amp;quot; ..&lt;br /&gt;
				&amp;quot;[[:Category:en:All topics]], for English terms.\n&amp;quot; ..&lt;br /&gt;
				&amp;quot;Note that categories under this tree categorize terms semantically rather than grammatically. &amp;quot; ..&lt;br /&gt;
				&amp;quot;Grammatical categories (such as all French verbs, or all English irregular plural forms) &amp;quot; ..&lt;br /&gt;
				&amp;quot;have a different naming structure, with the language name spelled out, such as &amp;quot; ..&lt;br /&gt;
				&amp;quot;[[:Category:French verbs]] or [[:Category:English irregular plurals]].&amp;quot;&lt;br /&gt;
			return desc, additional&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		-- Assume that if the description field contains a function, the function will return non-nil, so we don&amp;#039;t&lt;br /&gt;
		-- have to call the function at this point (in case it is heavyweight).&lt;br /&gt;
		local has_umbrella_desc = topdata.umbrella and topdata.umbrella.description or topdata.umbrella_description&lt;br /&gt;
&lt;br /&gt;
		desc = function()&lt;br /&gt;
			local desc = topdata.umbrella and get_and_cache(data, topdata.umbrella, &amp;quot;description&amp;quot;) or&lt;br /&gt;
				get_and_cache(data, topdata, &amp;quot;umbrella_description&amp;quot;)&lt;br /&gt;
			if not desc then&lt;br /&gt;
				 desc = get_and_cache(data, topdata, &amp;quot;description&amp;quot;)&lt;br /&gt;
				 if desc then&lt;br /&gt;
					desc = replace_special_descriptions(data, desc)&lt;br /&gt;
					desc = remove_lang_params(desc)&lt;br /&gt;
					desc = desc:gsub(&amp;quot;%.$&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
					desc = &amp;quot;This category concerns the topic: &amp;quot; .. desc .. &amp;quot;.&amp;quot;&lt;br /&gt;
				 end&lt;br /&gt;
			end&lt;br /&gt;
			if not desc then&lt;br /&gt;
				desc = &amp;quot;Categories concerning &amp;quot; .. label .. &amp;quot; in various specific languages.&amp;quot;&lt;br /&gt;
			end&lt;br /&gt;
			return postprocess_thesaurus(substitute_template_specs(data, desc))&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		preceding = topdata.umbrella and topdata.umbrella.preceding or not has_umbrella_desc and topdata.preceding&lt;br /&gt;
		if preceding then&lt;br /&gt;
			preceding = remove_lang_params(preceding)&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		additional = function()&lt;br /&gt;
			local additional_parts = {}&lt;br /&gt;
			local topdata_additional = topdata.umbrella and topdata.umbrella.additional or&lt;br /&gt;
				not has_umbrella_desc and topdata.additional&lt;br /&gt;
			if topdata_additional then&lt;br /&gt;
				insert(additional_parts, remove_lang_params(topdata_additional))&lt;br /&gt;
			end&lt;br /&gt;
			insert(additional_parts, &amp;quot;{{{umbrella_msg}}}&amp;quot;)&lt;br /&gt;
			if not data.thesaurus_data then&lt;br /&gt;
				insert(additional_parts, get_additional_msg(data))&lt;br /&gt;
				local labels_msg = get_labels_categorizing(data)&lt;br /&gt;
				if labels_msg then&lt;br /&gt;
					insert(additional_parts, labels_msg)&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
			return postprocess_thesaurus(substitute_template_specs(data, concat(additional_parts, &amp;quot;\n\n&amp;quot;)))&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	preceding = substitute_template_specs(data, preceding)&lt;br /&gt;
	return desc, additional, preceding&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function normalize_sort_key(data, sort, parent_index)&lt;br /&gt;
	local topdata, lang, label = data.topdata, data.lang, data.label&lt;br /&gt;
	sort = sort or parent_index == 1 and (not lang and topdata.umbrella and&lt;br /&gt;
		topdata.umbrella.breadcrumb_and_first_sort_key or topdata.breadcrumb_and_first_sort_key) or nil&lt;br /&gt;
	if not sort then&lt;br /&gt;
		-- When defaulting sort key to label, strip &amp;#039;The &amp;#039; (e.g. in &amp;#039;The Matrix&amp;#039;, &amp;#039;The Hunger Games&amp;#039;)&lt;br /&gt;
		-- and &amp;#039;A &amp;#039; (e.g. in &amp;#039;A Song of Ice and Fire&amp;#039;, &amp;#039;A Christmas Carol&amp;#039;) from label.&lt;br /&gt;
		local stripped_sort = label:match(&amp;quot;^[Tt]he (.*)$&amp;quot;)&lt;br /&gt;
		if stripped_sort then&lt;br /&gt;
			sort = stripped_sort&lt;br /&gt;
		end&lt;br /&gt;
		if not stripped_sort then&lt;br /&gt;
			stripped_sort = label:match(&amp;quot;^[Aa] (.*)$&amp;quot;)&lt;br /&gt;
			if stripped_sort then&lt;br /&gt;
				sort = stripped_sort&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		if not stripped_sort then&lt;br /&gt;
			sort = label&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	sort = substitute_template_specs(data, sort)&lt;br /&gt;
&lt;br /&gt;
	if not lang then&lt;br /&gt;
		sort = &amp;quot; &amp;quot; .. sort&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return sort&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function get_topic_parents(data)&lt;br /&gt;
	local topdata, lang, label = data.topdata, data.lang, data.label&lt;br /&gt;
	local parents = topdata.parents&lt;br /&gt;
&lt;br /&gt;
	if not lang and label == &amp;quot;all topics&amp;quot; then&lt;br /&gt;
		return {{ name = &amp;quot;Category:Fundamental&amp;quot;, sort = &amp;quot;topics&amp;quot; }}&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if not parents or #parents == 0 then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local ret = {}&lt;br /&gt;
&lt;br /&gt;
	for i, parent in ipairs(parents) do&lt;br /&gt;
		parent = mw.clone(parent)&lt;br /&gt;
&lt;br /&gt;
		if type(parent) ~= &amp;quot;table&amp;quot; then&lt;br /&gt;
			parent = {name = parent}&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		parent.sort = normalize_sort_key(data, parent.sort, i)&lt;br /&gt;
&lt;br /&gt;
		if type(parent.name) ~= &amp;quot;string&amp;quot; then&lt;br /&gt;
			error((&amp;quot;Internal error: parent.name is not a string: parent = %s&amp;quot;):format(dump(parent)))&lt;br /&gt;
		end&lt;br /&gt;
		if parent.name:find(&amp;quot;^Category:&amp;quot;) or parent.nontopic then&lt;br /&gt;
			-- leave as-is&lt;br /&gt;
			parent.nontopic = nil&lt;br /&gt;
		else&lt;br /&gt;
			parent.name = make_category_name(lang, parent.name)&lt;br /&gt;
		end&lt;br /&gt;
		parent.name = substitute_template_specs(data, parent.name)&lt;br /&gt;
		&lt;br /&gt;
		insert(ret, parent)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local function make_list_of_type_parent(typ)&lt;br /&gt;
		return {&lt;br /&gt;
			name = make_category_name(lang, (&amp;quot;list of %s categories&amp;quot;):format(typ)),&lt;br /&gt;
			sort = (not lang and &amp;quot; &amp;quot; or &amp;quot;&amp;quot;) .. label,&lt;br /&gt;
		}&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if topdata.type ~= &amp;quot;toplevel&amp;quot; then&lt;br /&gt;
		local types = split_types(topdata.type)&lt;br /&gt;
		for _, typ in ipairs(types) do&lt;br /&gt;
			insert(ret, make_list_of_type_parent(typ))&lt;br /&gt;
		end&lt;br /&gt;
		if #types &amp;gt; 1 then&lt;br /&gt;
			insert(ret, make_list_of_type_parent(&amp;quot;mixed&amp;quot;))&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Add umbrella category.&lt;br /&gt;
	if lang then&lt;br /&gt;
		insert(ret, {&lt;br /&gt;
			name = make_category_name(nil, label),&lt;br /&gt;
			sort = lang:getCanonicalName(),&lt;br /&gt;
		})&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return ret&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function get_thesaurus_parents(data)&lt;br /&gt;
	local topdata, lang, label = data.topdata, data.lang, data.label&lt;br /&gt;
	local parent_substitutions = data.thesaurus_data.parent_substitutions&lt;br /&gt;
	local parents = topdata.parents&lt;br /&gt;
&lt;br /&gt;
	if not parents or #parents == 0 then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local ret = {}&lt;br /&gt;
&lt;br /&gt;
	for i, parent in ipairs(parents) do&lt;br /&gt;
		-- Process parent categories as follows:&lt;br /&gt;
		-- 1. skip non-topic cats and meta-categories that start with &amp;quot;List of&amp;quot;&lt;br /&gt;
		-- 2. map &amp;quot;en:All topics&amp;quot; to &amp;quot;English thesaurus entries&amp;quot; (and same for other languages), but map &amp;quot;All topics&amp;quot; itself to the root &amp;quot;Thesaurus&amp;quot; category&lt;br /&gt;
		-- 3. check if this parent is to be substituted, if so, substitute it&lt;br /&gt;
		-- 4. prepend &amp;quot;Thesaurus:&amp;quot; to all other category names&lt;br /&gt;
		parent = mw.clone(parent)&lt;br /&gt;
&lt;br /&gt;
		if type(parent) ~= &amp;quot;table&amp;quot; then&lt;br /&gt;
			parent = {name = parent}&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		parent.sort = normalize_sort_key(data, parent.sort, i)&lt;br /&gt;
&lt;br /&gt;
		if type(parent.name) ~= &amp;quot;string&amp;quot; then&lt;br /&gt;
			error((&amp;quot;Internal error: parent.name is not a string: parent = %s&amp;quot;):format(dump(parent)))&lt;br /&gt;
		end&lt;br /&gt;
		if parent.name:find(&amp;quot;^Category:&amp;quot;) or parent.nontopic then&lt;br /&gt;
			-- skip&lt;br /&gt;
		elseif parent.name == &amp;quot;all topics&amp;quot; or parent_substitutions[parent.name] == &amp;quot;all topics&amp;quot; then&lt;br /&gt;
			if not lang then&lt;br /&gt;
				insert(ret, {&lt;br /&gt;
					name = &amp;quot;Thesaurus&amp;quot;,&lt;br /&gt;
					sort = label,&lt;br /&gt;
				})&lt;br /&gt;
			else&lt;br /&gt;
				insert(ret, {&lt;br /&gt;
					name = &amp;quot;thesaurus entries&amp;quot;,&lt;br /&gt;
					sort = parent.sort,&lt;br /&gt;
					lang = lang:getCode(),&lt;br /&gt;
					is_label = true,&lt;br /&gt;
				})&lt;br /&gt;
			end&lt;br /&gt;
		else&lt;br /&gt;
			parent.name = &amp;quot;Thesaurus:&amp;quot; .. make_category_name(lang, parent_substitutions[parent.name] or parent.name)&lt;br /&gt;
			parent.name = substitute_template_specs(data, parent.name)&lt;br /&gt;
			insert(ret, parent)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Add the non-thesaurus version of this category as a parent, unless it is a thesaurus-only category.&lt;br /&gt;
	if not topdata.thesaurusonly then&lt;br /&gt;
		insert(ret, { name = make_category_name(lang, label), sort = &amp;quot; &amp;quot; })&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Add umbrella category.&lt;br /&gt;
	if lang then&lt;br /&gt;
		insert(ret, {&lt;br /&gt;
			name = &amp;quot;Thesaurus:&amp;quot; .. make_category_name(nil, label),&lt;br /&gt;
			sort = lang:getCanonicalName(),&lt;br /&gt;
		})&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return ret&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function generate_spec(category, lang, upcase_label, thesaurus_data)&lt;br /&gt;
	local label_data = require(topic_data_module)&lt;br /&gt;
	local label&lt;br /&gt;
&lt;br /&gt;
	-- Convert label to lowercase if possible&lt;br /&gt;
	local lowercase_label = mw.getContentLanguage():lcfirst(upcase_label)&lt;br /&gt;
	local uppercase_label = mw.getContentLanguage():ucfirst(upcase_label)&lt;br /&gt;
	-- Make sure that the label is either uppercase or caseless.&lt;br /&gt;
	-- FIXME: Will this run into weirdness if this code is used on the Turkish Wiktionary?&lt;br /&gt;
	if lowercase_label ~= uppercase_label and upcase_label ~= uppercase_label then&lt;br /&gt;
		error((&amp;quot;Topic category label &amp;#039;%s&amp;#039; needs to be uppercase in the category name&amp;quot;):format(&lt;br /&gt;
			upcase_label))&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Check if the label exists&lt;br /&gt;
	local labels = label_data[&amp;quot;LABELS&amp;quot;]&lt;br /&gt;
&lt;br /&gt;
	if labels[lowercase_label] then&lt;br /&gt;
		label = lowercase_label&lt;br /&gt;
	else&lt;br /&gt;
		label = upcase_label&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local topdata = labels[label]&lt;br /&gt;
&lt;br /&gt;
	-- Go through handlers&lt;br /&gt;
	if not topdata then&lt;br /&gt;
		for _, handler in ipairs(label_data[&amp;quot;HANDLERS&amp;quot;]) do&lt;br /&gt;
			topdata = handler.handler(label)&lt;br /&gt;
			if topdata then&lt;br /&gt;
				topdata.module = handler.module&lt;br /&gt;
				break&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if not topdata then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local data = {&lt;br /&gt;
		category = category,&lt;br /&gt;
		lang = lang,&lt;br /&gt;
		label = label,&lt;br /&gt;
		topdata = topdata,&lt;br /&gt;
		thesaurus_data = thesaurus_data,&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	local description, additional, preceding = get_description_additional_preceding(data)&lt;br /&gt;
	local parents&lt;br /&gt;
	if thesaurus_data then&lt;br /&gt;
		parents = get_thesaurus_parents(data)&lt;br /&gt;
	else&lt;br /&gt;
		parents = get_topic_parents(data)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return {&lt;br /&gt;
		lang = lang and lang:getCode() or nil,&lt;br /&gt;
		description = description,&lt;br /&gt;
		additional = additional,&lt;br /&gt;
		preceding = preceding,&lt;br /&gt;
		parents = parents,&lt;br /&gt;
		breadcrumb = get_breadcrumb(data),&lt;br /&gt;
		displaytitle = format_displaytitle(data, &amp;quot;include lang prefix&amp;quot;, &amp;quot;upcase&amp;quot;),&lt;br /&gt;
		topright = get_topright(data),&lt;br /&gt;
		module = topdata.module,&lt;br /&gt;
		can_be_empty = not lang,&lt;br /&gt;
		hidden = false,&lt;br /&gt;
	}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Handler for `Thesaurus:...` categories.&lt;br /&gt;
table.insert(raw_handlers, function(data)&lt;br /&gt;
	local code, upcase_label = data.category:match(&amp;quot;^Thesaurus:(%l[%a-]*%a):(.+)$&amp;quot;)&lt;br /&gt;
	local lang&lt;br /&gt;
	if code then&lt;br /&gt;
		lang = require(languages_module).getByCode(code)&lt;br /&gt;
		if not lang then&lt;br /&gt;
			mw.log((&amp;quot;Category &amp;#039;%s&amp;#039; looks like a language-specific thesaurus category but unable to match language prefix&amp;quot;):&lt;br /&gt;
				format(data.category))&lt;br /&gt;
			return nil&lt;br /&gt;
		end&lt;br /&gt;
	else&lt;br /&gt;
		upcase_label = data.category:match(&amp;quot;^Thesaurus:(.+)$&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if upcase_label then&lt;br /&gt;
		local thesaurus_data = require(thesaurus_data_module)&lt;br /&gt;
		-- substituted category names are not allowed&lt;br /&gt;
		if thesaurus_data.parent_substitutions[lcfirst(upcase_label)] then&lt;br /&gt;
			error((&amp;quot;Category is not allowed as a Thesaurus category: %s (see the list of parent substitutions at &amp;quot; ..&lt;br /&gt;
				&amp;quot;[[Module:category tree/topic/thesaurus]])&amp;quot;):format(data.category))&lt;br /&gt;
		end&lt;br /&gt;
		return generate_spec(data.category, lang, upcase_label, thesaurus_data)&lt;br /&gt;
	end&lt;br /&gt;
end)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Handler for regular topic categories.&lt;br /&gt;
table.insert(raw_handlers, function(data)&lt;br /&gt;
	local code, upcase_label = data.category:match(&amp;quot;^(%l[%a-]*%a):(.+)$&amp;quot;)&lt;br /&gt;
	local lang&lt;br /&gt;
	if code then&lt;br /&gt;
		lang = require(languages_module).getByCode(code)&lt;br /&gt;
		if not lang then&lt;br /&gt;
			mw.log((&amp;quot;Category &amp;#039;%s&amp;#039; looks like a language-specific topic category but unable to match language prefix&amp;quot;):&lt;br /&gt;
				format(data.category))&lt;br /&gt;
			return nil&lt;br /&gt;
		end&lt;br /&gt;
	else&lt;br /&gt;
		upcase_label = data.category&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return generate_spec(data.category, lang, upcase_label)&lt;br /&gt;
end)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-----------------------------------------------------------------------------&lt;br /&gt;
--                                                                         --&lt;br /&gt;
--                              RAW CATEGORIES                             --&lt;br /&gt;
--                                                                         --&lt;br /&gt;
-----------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
raw_categories[&amp;quot;Thesaurus&amp;quot;] = {&lt;br /&gt;
	description = &amp;quot;Category for entries of the Wiktionary thesaurus, located in a separate namespace.&amp;quot;,&lt;br /&gt;
	additional = [=[&lt;br /&gt;
There are &amp;#039;&amp;#039;&amp;#039;three ways to browse&amp;#039;&amp;#039;&amp;#039; the thesaurus:&lt;br /&gt;
* Look under &amp;#039;&amp;#039;&amp;#039;[[:Category:Thesaurus entries by language]]&amp;#039;&amp;#039;&amp;#039; to get started.&lt;br /&gt;
* Use the search box below.&lt;br /&gt;
* Browse the thesaurus by topic using the links under &amp;quot;Subcategories&amp;quot; below.&lt;br /&gt;
&lt;br /&gt;
The main project page is [[Wiktionary:Thesaurus]].&lt;br /&gt;
&lt;br /&gt;
{{ws header|&amp;lt;nowiki/&amp;gt;|link=}}]=],&lt;br /&gt;
	parents = {&lt;br /&gt;
		&amp;quot;Category:Fundamental&amp;quot;,&lt;br /&gt;
		&amp;quot;Category:Wiktionary projects&amp;quot;,&lt;br /&gt;
	},&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
return {RAW_CATEGORIES = raw_categories, RAW_HANDLERS = raw_handlers}&lt;/div&gt;</summary>
		<author><name>wikt&gt;WingerBot</name></author>
	</entry>
</feed>