Open main menu

This module implements {{langcatboiler}}. The documentation here describes how the module works, and how to add, modify or remove information from the category tree. For information on how to use the template itself, see its documentation.

This module does not use labels. Effectively, there is only one label, whose data is hard-coded into the module.


local export = {}

local function makeCategoryLink(object)
	return "[[:Category:" .. object:getCategoryName() .. "|" .. object:getCanonicalName() .. "]]"
end

local function linkbox(lang, setwiki, setwikt, setsister, entryname)
	local wiktionarylinks = "''None.''"
	
	local canonicalName = lang:getCanonicalName()
	local wikimediaLanguages = lang:getWikimediaLanguages()
	local categoryName = lang:getCategoryName()
	local wikipediaArticle = setwiki or lang:getWikipediaArticle()
	
	if setwikt then
		require "Module:debug".track "langcatboiler/setwikt"
		if setwikt == "-" then
			require "Module:debug".track "langcatboiler/setwikt/hyphen"
		end
	end
	
	if setwikt ~= "-" and wikimediaLanguages and wikimediaLanguages[1] then
		wiktionarylinks = {}
		
		for _, wikimedialang in ipairs(wikimediaLanguages) do
			table.insert(wiktionarylinks,
				(wikimedialang:getCanonicalName() ~= canonicalName and "(''" .. wikimedialang:getCanonicalName() .. "'') " or "") ..
				"'''[[:" .. wikimedialang:getCode() .. ":|" .. wikimedialang:getCode() .. ".wiktionary.org]]'''")
		end
		
		wiktionarylinks = table.concat(wiktionarylinks, "<br/>")
	end
	
	local plural = wikimediaLanguages[2] and "s" or ""
	
	return table.concat{
[=[<div style="clear: right; border: solid #aaa 1px; margin: 1 1 1 1; background: #f9f9f9; width: 270px; padding: 5px; margin: 5px; text-align: left; float: right">
<div style="text-align: center; margin-bottom: 10px; margin-top: 5px">''']=], categoryName, [=['''</div>

{| style="font-size: 90%; background: #f9f9f9;"
|-
| style="vertical-align: middle; height: 35px; width: 35px;" | [[File:Wiktionary-logo-v2.svg|35px|none|Wiktionary]]
|| '']=], categoryName, [=[ edition]=], plural, [=[ of Wiktionary''
|-
| colspan="2" style="padding-left: 10px; border-bottom: 1px solid lightgray;" | ]=], wiktionarylinks, [=[

|-
| style="vertical-align: middle; height: 35px" | [[File:Wikipedia-logo.png|35px|none|Wikipedia]]
|| ''Wikipedia article about ]=], categoryName, [=[''
|-
| colspan="2" style="padding-left: 10px; border-bottom: 1px solid lightgray;" | ]=], (setwiki == "-" and "''None.''" or "'''[[w:" .. wikipediaArticle .. "|" .. wikipediaArticle .. "]]'''"), [=[

|-
| style="vertical-align: middle; height: 35px" | [[File:Wikimedia-logo.svg|35px|none|Wikimedia Commons]]
|| ''Links related to ]=], categoryName, [=[ in sister projects at Wikimedia Commons''
|-
| colspan="2" style="padding-left: 10px; border-bottom: 1px solid lightgray;" | ]=], (setsister == "-" and "''None.''" or "'''[[commons:Category:" .. (setsister or categoryName) .. "|" .. (setsister or categoryName) .. "]]'''"), [=[

|-
| style="vertical-align: middle; height: 35px" | [[File:Crystal kfind.png|35px|none|Considerations]]
|| ]=], categoryName, [=[ considerations
|-
| colspan="2" style="padding-left: 10px; border-bottom: 1px solid lightgray;" | '''[[Wiktionary:About ]=], canonicalName, [=[]]'''
|-
| style="vertical-align: middle; height: 35px" | [[File:Incomplete list.svg|35px|none|Index]]
|| ]=], categoryName, [=[ index
|-
| colspan="2" style="padding-left: 10px; border-bottom: 1px solid lightgray;" | '''[[Index:]=], canonicalName, [=[]]'''
|-
| style="vertical-align: middle; height: 35px" | [[File:Open book nae 02.svg|35px|none|Entry]]
|| ]=], categoryName, [=[ entry
|-
| colspan="2" style="padding-left: 10px;" | ''']=], require("Module:links").full_link({lang = require("Module:languages").getByCode("en"), term = entryname or canonicalName}), [=['''
|}
</div>]=]
}
end

-- Should perhaps use wiki syntax.
local function infobox(lang)
	local ret = {}
	
	table.insert(ret, "<table class=\"wikitable language-category-info\">\n")
	table.insert(ret, "<tr>\n<th colspan=\"2\" class=\"plainlinks\">[//en.wiktionary.org/w/index.php?title=Module:" .. require("Module:languages").getDataModuleName(lang:getCode()) .. "&action=edit Edit language data]</th>\n</tr>\n")
	table.insert(ret, "<tr>\n<th>Canonical name</th><td>" .. lang:getCanonicalName() .. "</td>\n</tr>\n")
	
	if lang:getOtherNames() then
		local names = {}
		
		for _, name in ipairs(lang:getOtherNames()) do
			table.insert(names, "<li>" .. name .. "</li>")
		end
		
		if #names > 0 then
			table.insert(ret, "<tr>\n<th>Other names</th><td><ul>" .. table.concat(names, "\n") .. "</ul></td>\n</tr>\n")
		end
	end
	
	table.insert(ret, "<tr>\n<th>[[Wiktionary:Languages|Language code]]</th><td><code>" .. lang:getCode() .. "</code></td>\n</tr>\n")
	table.insert(ret, "<tr>\n<th>[[Wiktionary:Families|Language family]]</th>\n")
	
	local fam = lang:getFamily()
	local famCode = fam and fam:getCode()
	
	if not fam then
		table.insert(ret, "<td>unclassified</td>")
	elseif famCode == "qfa-iso" then
		table.insert(ret, "<td>[[:Category:Language isolates|language isolate]]</td>")
	elseif famCode == "qfa-mix" then
		table.insert(ret, "<td>[[:Category:Mixed languages|mixed language]]</td>")
	elseif famCode == "sgn" then
		table.insert(ret, "<td>[[:Category:Sign languages|sign language]]</td>")
	elseif famCode == "crp" then
		table.insert(ret, "<td>[[:Category:Creole or pidgin languages|creole or pidgin]]</td>")
	elseif famCode == "art" then
		table.insert(ret, "<td>[[:Category:Constructed languages|constructed language]]</td>")
	else
		table.insert(ret, "<td>" .. makeCategoryLink(fam) .. "</td>")
	end
	
	table.insert(ret, "\n</tr>\n<tr>\n<th>Ancestors</th>\n")
	
	local ancestors, ancestorChain = lang:getAncestors(), lang:getAncestorChain()
	if ancestors[2] then
		local ancestorList = {}
		
		for i, anc in ipairs(ancestors) do
			ancestorList[i] = "<li>" .. makeCategoryLink(anc) .. "</li>"
		end
		
		table.insert(ret, "<td><ul>\n" .. table.concat(ancestorList, "\n") .. "</ul></td>\n")
	elseif ancestorChain[1] then
		table.insert(ret, "<td><ul>\n")
		
		local chain = {}
		
		for i, anc in ipairs(ancestorChain) do
			chain[i] = "<li>" .. makeCategoryLink(anc) .. "</li>"
		end
		
		table.insert(ret, table.concat(chain, "\n<ul>\n"))
		
		for _, _ in ipairs(chain) do
			table.insert(ret, "</ul>")
		end
		
		table.insert(ret, "</td>\n")
	else
		table.insert(ret, "<td>unknown</td>\n")
	end
	
	table.insert(ret, "</tr>\n")
	
	local scripts = lang:getScripts()
	
	if scripts[1] then
		local script_text = {}
		
		for _, sc in ipairs(scripts) do
			local text = {}
			local code = sc:getCode()
			if code ~= "Hira" then
				table.insert(text, "<li>" .. makeCategoryLink(sc))
			end
			
			if code == "Jpan" then
				local m_scripts = require("Module:scripts")
				local Hani = m_scripts.getByCode("Hani")
				local Hira = m_scripts.getByCode("Hira")
				local Kana = m_scripts.getByCode("Kana")
				table.insert(text, "<ul>")
				table.insert(text, "<li>" .. makeCategoryLink(Hani) .. "</li>")
				table.insert(text, "<li>" .. makeCategoryLink(Hira) .. "</li>")
				table.insert(text, "<li>" .. makeCategoryLink(Kana) .. "</li>")
				table.insert(text, "</ul>")
			elseif code == "Kore" then
				local m_scripts = require("Module:scripts")
				local Hang = m_scripts.getByCode("Hang")
				local Hani = m_scripts.getByCode("Hani")
				table.insert(text, "<ul>")
				table.insert(text, "<li>" .. makeCategoryLink(Hang) .. "</li>")
				table.insert(text, "<li>" .. makeCategoryLink(Hani) .. "</li>")
				table.insert(text, "</ul>")
			end
			
			table.insert(text, "</li>")
			
			table.insert(script_text, table.concat(text, "\n"))
		end
		
		table.insert(ret, "<tr>\n<th>[[Wiktionary:Scripts|Scripts]]</th>\n<td><ul>\n" .. table.concat(script_text, "\n") .. "</ul></td>\n</tr>\n")
	else
		table.insert(ret, "<tr>\n<th>[[Wiktionary:Scripts|Scripts]]</th>\n<td>not specified</td>\n</tr>\n")
	end
	
	if lang._rawData.translit_module then
		local translit_module = lang._rawData.translit_module
		local translit_module_info = {}
		table.insert(translit_module_info,
			("[[Module:%s]]"):format(translit_module))
		
		if translit_module == "translit-redirect" then
			local data = mw.loadData("Module:translit-redirect/data")[lang:getCode()]
			if data then
				table.insert(translit_module_info, ":")
				local redirects_to = {}
				local m_scripts = require "Module:scripts"
				for script, data in require "Module:table".sortedPairs(data) do
					-- Skip boolean fields like "debug_mode" and "noError".
					if type(data) == "table" then
						table.insert(redirects_to,
							("\n* <code>%s</code>: %s"):format(
								script,
								data.module and ("[[Module:%s]]"):format(data.module)
									or "(none)"))
					end
				end
				table.insert(translit_module_info, table.concat(redirects_to))
			end
		end
		
		table.insert(ret, [=[
<tr>
<th>[[Wiktionary:Transliteration and romanization|Transliteration<br>module]]</th>
<td>]=] .. table.concat(translit_module_info) .. [=[</td>
</tr>
]=])
end
	local wikidataItem = lang:getWikidataItem()
	if lang:getWikidataItem() and mw.wikibase then
		local URL = mw.wikibase.getEntityUrl(wikidataItem)
		local link
		if URL then
			link = '[' .. URL .. ' ' .. wikidataItem .. ']'
		else
			link = '<span class="error">Invalid Wikidata item: <code>' .. wikidataItem .. '</code></span>'
		end
		table.insert(ret, "<tr><th>Wikidata</th><td>" .. link .. "</td></tr>")
	end
	
	table.insert(ret, "</table>")
	
	return table.concat(ret)
end

local function NavFrame(content, title)
	return '<div class="NavFrame"><div class="NavHead">'
		.. (title or '{{{title}}}') .. '</div>'
		.. '<div class="NavContent" style="text-align: left;">'
		.. content
		.. '</div></div>'
end

function export.country_categories(frame)
	local categories = {}
	
	for i, country in ipairs(frame:getParent().args) do
		if i > 1 then
			table.insert(categories, "[[Category:Languages of " .. country .. "]]")
		end
	end
	
	if #categories > 0 then
		return table.concat(categories)
	else
		return "[[Category:Languages not sorted into a country category]]"
	end
end


-- Category object

local Category = {}
Category.__index = Category


function Category.new(info)
	for key, val in pairs(info) do
		if not (key == "code" or key == "entryname" or key == "setsister" or key == "setwiki" or key == "setwikt") then
			error("The parameter \"" .. key .. "\" was not recognized.")
		end
	end
	
	local self = setmetatable({}, Category)
	self._info = info
	
	if not self._info.code then
		error("No language code was specified.")
	else
		self._lang = require("Module:languages").getByCode(self._info.code) or error("The language code \"" .. self._info.code .. "\" is not valid.")
	end
	
	return self
end

export.new = Category.new


function Category:getInfo()
	return self._info
end


function Category:getBreadcrumbName()
	return self._lang:getCanonicalName()
end


function Category:getDataModule()
	return "Module:category tree/langcatboiler"
end


function Category:canBeEmpty()
	return true
end


function Category:isHidden()
	return false
end


function Category:getCategoryName()
	return mw.getContentLanguage():ucfirst(self._lang:getCategoryName())
end


function Category:getDescription()
	if self._lang:getCode() == "und" then
		return
			"This is the main category of the '''" .. self._lang:getCategoryName() .. "''', represented in Wiktionary by the [[Wiktionary:Languages|code]] '''" .. self._lang:getCode() .. "'''. " ..
			"This language contains terms in historical writing, whose meaning has not yet been determined by scholars."
	end
	
	local canonicalName = self._lang:getCanonicalName()
	
	local ret = linkbox(self._lang, self._info.setwiki, self._info.setwikt, self._info.setsister, self._info.entryname)
	
	ret = ret .. "This is the main category of the '''" .. self._lang:getCategoryName() .. "'''.\n\nInformation about " .. canonicalName .. ":\n\n" .. infobox(self._lang)
	
	if self._lang:getType() == "reconstructed" then
		ret = ret .. "\n\n" ..
			canonicalName .. " is a reconstructed language. Its words and roots are not directly attested in any written works, but have been reconstructed through the ''comparative method'', " ..
			"which finds regular similarities between languages that cannot be explained by coincidence or word-borrowing, and extrapolates ancient forms from these similarities.\n\n" ..
			"According to our [[Wiktionary:Criteria for inclusion|criteria for inclusion]], terms in " .. canonicalName ..
			" should '''not''' be present in entries in the main namespace, but may be added to the Reconstruction: namespace."
	elseif self._lang:getType() == "appendix-constructed" then
		ret = ret .. "\n\n" ..
			canonicalName .. " is a constructed language that is only in sporadic use. " ..
			"According to our [[Wiktionary:Criteria for inclusion|criteria for inclusion]], terms in " .. canonicalName ..
			" should '''not''' be present in entries in the main namespace, but may be added to the Appendix: namespace. " ..
			"All terms in this language may be available at [[Appendix:" .. canonicalName .. "]]."
	end
	
	local about = mw.title.new("Wiktionary:About " .. canonicalName)
	
	if about.exists then
		ret = ret .. "\n\n" ..
			"Please see '''[[Wiktionary:About " .. canonicalName .. "]]''' for information and special considerations for creating " .. self._lang:getCategoryName() .. " entries."
	end
	
	local tree_of_descendants = require("Module:family tree").print_children(self._lang:getCode(), {
		protolanguage_under_family = true,
	})
	
	if tree_of_descendants then
		ret = ret .. NavFrame(
			tree_of_descendants,
			"Family tree")
	end
	
	return ret
end


function Category:getParents()
	local canonicalName = self._lang:getCanonicalName()
	
	local ret = {{name = "Category:All languages", sort = canonicalName}}
	
	local fam = self._lang:getFamily()
	local famCode = fam and fam:getCode()
	
	if not fam then
		table.insert(ret, {name = "Category:Unclassified languages", sort = canonicalName})
	elseif famCode == "qfa-iso" then
		table.insert(ret, {name = "Category:Language isolates", sort = canonicalName})
	elseif famCode == "qfa-mix" then
		table.insert(ret, {name = "Category:Mixed languages", sort = canonicalName})
	elseif famCode == "sgn" then
		table.insert(ret, {name = "Category:All sign languages", sort = canonicalName})
	elseif famCode == "crp" then
		table.insert(ret, {name = "Category:Creole or pidgin languages", sort = canonicalName})
		
		for _, anc in ipairs(self._lang:getAncestors()) do
			table.insert(ret, {name = "Category:" .. anc:getCanonicalName() .. "-based creole or pidgin languages", sort = canonicalName})
		end
	elseif famCode == "art" then
		if self._lang:getType() == "appendix-constructed" then
			table.insert(ret, {name = "Category:Appendix-only constructed languages", sort = canonicalName})
		else
			table.insert(ret, {name = "Category:Constructed languages", sort = canonicalName})
		end
		
		for _, anc in ipairs(self._lang:getAncestors()) do
			table.insert(ret, {name = "Category:" .. anc:getCanonicalName() .. "-based constructed languages", sort = canonicalName})
		end
	else
		table.insert(ret, {name = "Category:" .. mw.getContentLanguage():ucfirst(fam:getCategoryName()), sort = canonicalName})
		
		if self._lang:getType() == "reconstructed" then
			table.insert(ret, {name = "Category:Reconstructed languages", sort = (mw.ustring.gsub(canonicalName, "^Proto%-", ""))})
		end
	end
	
	for _, sc in ipairs(self._lang:getScripts()) do
		table.insert(ret, {name = "Category:" .. mw.getContentLanguage():ucfirst(sc:getCategoryName() .. " languages"), sort = canonicalName})
		
		if sc:getCode() == "Jpan" then
			table.insert(ret, {name = "Category:" .. mw.getContentLanguage():ucfirst(require("Module:scripts").getByCode("Hani"):getCategoryName() .. " languages"), sort = canonicalName})
			table.insert(ret, {name = "Category:" .. mw.getContentLanguage():ucfirst(require("Module:scripts").getByCode("Hira"):getCategoryName() .. " languages"), sort = canonicalName})
			table.insert(ret, {name = "Category:" .. mw.getContentLanguage():ucfirst(require("Module:scripts").getByCode("Kana"):getCategoryName() .. " languages"), sort = canonicalName})
		elseif sc:getCode() == "Kore" then
			table.insert(ret, {name = "Category:" .. mw.getContentLanguage():ucfirst(require("Module:scripts").getByCode("Hang"):getCategoryName() .. " languages"), sort = canonicalName})
			table.insert(ret, {name = "Category:" .. mw.getContentLanguage():ucfirst(require("Module:scripts").getByCode("Hani"):getCategoryName() .. " languages"), sort = canonicalName})
		end
	end
	
	if self._lang:hasTranslit() then
		table.insert(ret, {name = "Category:Languages with automatic transliteration", sort = canonicalName})
	end
	
	return ret
end


function Category:getChildren()
	local ret = {}
	
	local m_poscatboiler = require("Module:category tree/poscatboiler")
	
	for _, label in ipairs({"appendices", "entry maintenance", "lemmas", "names", "phrases", "rhymes", "symbols", "templates", "terms by etymology", "terms by usage", "transliterations"}) do
		local child = m_poscatboiler.new({code = self._lang:getCode(), label = label})
		local parents = child:getParents()
		
		if parents then
			-- Find the current category among the child's parents, to find its sort key
			for _, parent in ipairs(parents) do
				if type(parent.name) == "string" and parent.name == "Category:" .. self:getCategoryName() then
					table.insert(ret, {name = child, sort = parent.sort})
					break
				end
			end
		end
	end
	
	local m_derivcatboiler = require("Module:category tree/derived cat")
	local child = m_derivcatboiler.new({code = nil, label = self._lang:getCode()})
	local sortkey = child._info.label
	local parents = child:getParents()
	
	if parents then
		-- Find the current category among the child's parents, to find its sort key
		for _, parent in ipairs(parents) do
			if type(parent.name) == "string" and parent.name == "Category:" .. self:getCategoryName() then
				sortkey = parent.sort
				break
			end
		end
	end
	
	table.insert(ret, {name = child, sort = sortkey})
	
	local m_topic_cat = require("Module:category tree/topic cat")
	local child = m_topic_cat.new({code = self._lang:getCode(), label = "all topics"})
	local sortkey = child._info.label
	local parents = child:getParents()
	
	if parents then
		-- Find the current category among the child's parents, to find its sort key
		for _, parent in ipairs(parents) do
			if type(parent.name) == "string" and parent.name == "Category:" .. self:getCategoryName() then
				sortkey = parent.sort
				break
			end
		end
	end
	
	table.insert(ret, {name = child, sort = sortkey})
	
	-- FIXME: This is hacky, but it works as a stopgap measure.
	-- We should fix this when these categories get their own category tree modules.
	table.insert(ret, {name = {
		_lang = self._lang,
		getCategoryName = function(self) return "Regional " .. self._lang:getCanonicalName() end,
		getDescription = function(self) return self._lang:getCanonicalName() .. " terms used in specific regions or dialects." end,
		}, sort = "regional"})
	
	table.insert(ret, {name = {
		_lang = self._lang,
		getCategoryName = function(self) return "Requests (" .. self._lang:getCanonicalName() .. ")" end,
		getDescription = function(self) return self._lang:getCanonicalName() .. " entries that need attention of experienced editors." end,
		}, sort = "requests"})
	
	table.insert(ret, {name = {
		_lang = self._lang,
		getCategoryName = function(self) return "User " .. self._lang:getCode() end,
		getDescription = function(self) return "Wiktionary users categorized by fluency levels in " .. self._lang:getCanonicalName() .. "." end,
		}, sort = "user"})
	
	return ret
end


function Category:getUmbrella()
	return nil
end


return export