Module:Category tree/topic cat
Jump to navigation
Jump to search
Documentation for this module may be created at Module:Category tree/topic cat/documentation
local export = {}
local label_data = require("Module:category tree/topic cat/data")
local rsplit = mw.text.split
-- Category object
local Category = {}
Category.__index = Category
function Category.new_main(frame)
local self = setmetatable({}, Category)
local params = {
[1] = {},
[2] = {required = true},
["sc"] = {},
}
args = require("Module:parameters").process(frame:getParent().args, params, nil, "category tree/topic cat", "new_main")
self._info = {code = args[1], label = args[2]}
self:initCommon()
if not self._data then
return nil
end
return self
end
function Category.new(info)
for key, val in pairs(info) do
if not (key == "code" or key == "label") then
error("The parameter “" .. key .. "” was not recognized.")
end
end
local self = setmetatable({}, Category)
self._info = info
if not self._info.label then
error("No label was specified.")
end
self:initCommon()
if not self._data then
error("The label “" .. self._info.label .. "” does not exist.")
end
return self
end
export.new = Category.new
export.new_main = Category.new_main
function Category:initCommon()
if self._info.code then
if self._info.code == "Rhymes" then
else
-- Convert language code to lowercase if possible
local lowercase_code = mw.ustring.lower(self._info.code)
self._lang = require("Module:languages").getByCode(lowercase_code) or
error("The language code “" .. lowercase_code .. "” is not valid.")
end
end
-- Convert label to lowercase if possible
local lowercase_label = mw.getContentLanguage():lcfirst(self._info.label)
-- Check if the label exists
local labels = label_data["LABELS"]
if labels[lowercase_label] then
self._info.label = lowercase_label
end
self._data = labels[self._info.label]
-- Go through handlers
if not self._data then
for _, handler in ipairs(label_data["HANDLERS"]) do
self._data = handler.handler(self._info.label)
if self._data then
self._data.module = handler.module
break
end
end
end
end
function Category:getInfo()
return self._info
end
function Category:getBreadcrumbName()
local ret
if self._lang or self._info.raw then
ret = self._data.breadcrumb
else
-- FIXME, copied from [[Module:category tree/poscatboiler]]. No support for specific umbrella info yet.
ret = self._data.umbrella and self._data.umbrella.breadcrumb
end
if not ret then
ret = self._info.label
end
if type(ret) == "string" or type(ret) == "number" then
ret = {name = ret}
end
local name = self:substitute_template_specs(ret.name)
local nocap = ret.nocap
return name, nocap
end
function Category:getDataModule()
return self._data.module
end
function Category:canBeEmpty()
if self._lang then
return false
else
return true
end
end
function Category:isHidden()
return false
end
function Category:getCategoryName()
if self._lang then
return self._lang:getCode() .. ":" .. mw.getContentLanguage():ucfirst(self._info.label)
else
return mw.getContentLanguage():ucfirst(self._info.label)
end
end
local function replace_special_descriptions(desc)
-- TODO: Should probably find a better way to do this
local descriptionFormats = {
["default"] = "{{{langname}}} terms related to {{{label_lc}}}.",
["default with capital"] = "{{{langname}}} terms related to {{{label_uc}}}.",
["default with the"] = "{{{langname}}} terms related to the {{{label_uc}}}.",
["default with the lower"] = "{{{langname}}} terms related to the {{{label_lc}}}.",
["default with topic"] = "{{{langname}}} terms related to {{{label_lc}}} topics.",
["default with topic capital"] = "{{{langname}}} terms related to {{{label_uc}}} topics.",
["default-set"] = "{{{langname}}} terms for various {{{label_lc}}}.",
["default-set capital"] = "{{{langname}}} terms for various {{{label_uc}}}.",
}
if descriptionFormats[desc] then
return descriptionFormats[desc]
end
if desc then
local stripped_desc = desc
local no_singularize, wikify
while true do
local new_stripped_desc = stripped_desc:match("^(.+) no singularize$")
if new_stripped_desc then
stripped_desc = new_stripped_desc
no_singularize = true
else
new_stripped_desc = stripped_desc:match("^(.+) wikify$")
if new_stripped_desc then
stripped_desc = new_stripped_desc
wikify = true
else
break
end
end
end
if descriptionFormats[stripped_desc] then
local repl_suf = "%1"
if wikify then
repl_suf = repl_suf .. "_wiki"
end
if no_singularize then
repl_suf = repl_suf .. "_no_sing"
end
return descriptionFormats[stripped_desc]:gsub("({{{label_[ul]c)}}}", repl_suf .."}}}")
end
end
return desc
end
function Category:substitute_template_specs(desc)
if not desc then
return desc
end
if type(desc) == "number" then
desc = tostring(desc)
end
-- FIXME, when does this occur? It doesn't occur in the corresponding place in [[Module:category tree/poscatboiler]].
if type(desc) ~= "string" then
return desc
end
desc = desc:gsub("{{PAGENAME}}", mw.title.getCurrentTitle().text)
if self._lang then
desc = desc:gsub("{{{langname}}}", self._lang:getCanonicalName())
desc = desc:gsub("{{{langcode}}}", self._lang:getCode())
desc = desc:gsub("{{{langcat}}}", self._lang:getCategoryName())
desc = desc:gsub("{{{langlink}}}", self._lang:makeCategoryLink())
end
local function handle_label(label_sub, uclc, no_singularize, wikify)
local label = self._info.label
if uclc == "uc" then
label = mw.getContentLanguage():ucfirst(label)
elseif uclc == "lc" then
label = mw.getContentLanguage():lcfirst(label)
end
local function term_exists(term)
local title = mw.title.new(term)
return title and title.exists
end
local singular_label
if not no_singularize then
singular_label = require("Module:string utilities").singularize(label)
end
local function gsub_desc(to)
return (desc:gsub(label_sub, require("Module:pattern utilities").replacement_escape(to)))
end
if wikify then
if singular_label then
return gsub_desc("[[Wikipedia:" .. singular_label .. "|" .. label .. "]]")
else
return gsub_desc("[[Wikipedia:" .. label .. "|" .. label .. "]]")
end
end
-- First try to singularize the label as a whole, unless 'no singularize' was given. If the result exists,
-- return it.
if singular_label and term_exists(singular_label) then
return gsub_desc("[[Wiktionary:" .. singular_label .. "|" .. label .. "]]")
elseif term_exists(label) then
-- Then check if the original label as a whole exists, and return if so.
return gsub_desc("[[Wiktionary: " .. label .. "|" .. label .. "]]")
else
-- Otherwise, if the label is multiword, split into words and try the link each one, singularizing the last
-- one unless 'no singularize' was given.
local split_label
if label:find(" ") then
if not no_singularize then
split_label = rsplit(label, " ")
for i, word in ipairs(split_label) do
if i == #split_label then
local singular_word = require("Module:string utilities").singularize(word)
if term_exists(singular_word) then
split_label[i] = "[[Wiktionary:" .. singular_word .. "|" .. word .. "]]"
else
split_label = nil
break
end
else
if term_exists(word) then
split_label[i] = "[[Wiktionary:" .. word .. "|" .. word .. "]]"
else
split_label = nil
break
end
end
end
if split_label then
split_label = table.concat(split_label, " ")
end
end
-- If we weren't able to link individual words with the last word singularized, link all words as-is.
if not split_label then
split_label = rsplit(label, " ")
for i, word in ipairs(split_label) do
if term_exists(word) then
split_label[i] = "[[Wiktionary:" .. word .. "|" .. word .. "]]"
else
split_label = nil
break
end
end
if split_label then
split_label = table.concat(split_label, " ")
end
end
end
if split_label then
return gsub_desc(split_label)
else
return gsub_desc(label)
end
end
end
for _, spec in ipairs {
{"{{{label_uc}}}", "uc"},
{"{{{label_uc_no_sing}}}", "uc", "no singularize"},
{"{{{label_lc}}}", "lc"},
{"{{{label_lc_no_sing}}}", "lc", "no singularize"},
{"{{{label_uc_wiki}}}", "uc", false, "wikify"},
{"{{{label_uc_wiki_no_sing}}}", "uc", "no singularize", "wikify"},
{"{{{label_lc_wiki}}}", "lc", false, "wikify"},
{"{{{label_lc_wiki_no_sing}}}", "lc", "no singularize", "wikify"},
} do
local label_sub, uclc, no_singularize, wikify = unpack(spec)
if desc:find(label_sub) then
desc = handle_label(label_sub, uclc, no_singularize, wikify)
end
end
if desc:find("{") then
desc = mw.getCurrentFrame():preprocess(desc)
end
return desc
end
function Category:substitute_template_specs_in_args(args)
if not args then
return args
end
local pinfo = {}
for k, v in pairs(args) do
k = self:substitute_template_specs(k)
v = self:substitute_template_specs(v)
pinfo[k] = v
end
return pinfo
end
function Category:getDisplay(isChild)
if self._data["display"] then
if self._info.code then
return self._info.code .. ":" .. self._data["display"]:gsub("^%l", string.upper)
else
return self._data["display"]
end
end
return nil
end
function Category:getDisplay2(isChild)
if self._data["display"] then
return self._data["display"]:gsub("^%l", string.upper)
end
return nil
end
function Category:getSort(isChild)
return self._data["sort"]
end
function Category:getDescription(isChild)
-- Allows different text in the list of a category's children
local isChild = isChild == "child"
if self._lang then
local desc = self._data["description"]
desc = replace_special_descriptions(desc)
if desc then
if not isChild and self._data.additional then
desc = desc .. "\n\n" .. self._data.additional
end
return self:substitute_template_specs(desc)
end
else
if not self._lang and ( self._info.label == "all topics" or self._info.label == "all sets" ) then
return "This category applies to content and not to meta material about the Wiki."
end
local eninfo = mw.clone(self._info)
eninfo.code = "en"
local en = Category.new(eninfo)
local desc = self._data["umbrella_description"] or self._data["description"]
desc = replace_special_descriptions(desc)
if desc then
desc = desc:gsub("^{{{langname}}} ", "")
desc = desc:gsub("{{{langcode}}}:", "")
desc = desc:gsub("^{{{langcode}}} ", "")
desc = desc:gsub("^{{{langcat}}} ", "")
desc = desc:gsub("%.$", "")
desc = self:substitute_template_specs(desc)
else
desc = self._info.label
end
local display = self._data["display"] or mw.getContentLanguage():ucfirst(self._info.label)
return
"This category concerns the topic: " .. desc .. ".\n\n" ..
"It contains no dictionary entries, only other categories. The subcategories are of two sorts:\n\n" ..
"* Subcategories named like “aa:" .. display .. "” (with a prefixed language code) are categories of terms in specific languages. " ..
"* Subcategories of this one named without the prefixed language code are further categories just like this one, but devoted to finer topics."
end
end
function Category:getParents()
local parents = self._data["parents"]
if not self._lang and ( self._info.label == "all topics" or self._info.label == "all sets" ) then
return {{ name = "Category:Fundamental", sort = self._info.label:gsub("all ", "") }}
end
if not parents or #parents == 0 then
return nil
end
local ret = {}
local is_set = false
if self._info.label == "all sets" then
is_set = true
end
for key, parent in ipairs(parents) do
parent = mw.clone(parent)
if type(parent) ~= "table" then
parent = {name = parent}
end
if not parent.sort then
parent.sort = self._info.label
end
if self._lang then
parent.sort = self:substitute_template_specs(parent.sort)
elseif parent.sort:find("{{{langname}}}") or parent.sort:find("{{{langcat}}}") or
parent.template == "langcatboiler" or parent.module then
return nil
end
if not self._lang then
parent.sort = " " .. parent.sort
end
if parent.name and parent.name:find("^Category:") then
if self._lang then
parent.name = self:substitute_template_specs(parent.name)
elseif parent.name:find("{{{langname}}}") or parent.name:find("{{{langcat}}}") or
parent.template == "langcatboiler" or parent.module then
return nil
end
else
if parent.name == "list of sets" then
is_set = true
end
local pinfo = mw.clone(self._info)
pinfo.label = parent.name
if parent.template then
parent.name = require("Module:category tree/" .. parent.template).new(pinfo)
elseif parent.module then
-- A reference to a category using another category tree module.
if not parent.args then
error("Missing .args in parent table with module=\"" .. parent.module .. "\" for '" ..
self._info.label .. "' topic entry in module '" .. (self._data.module or "unknown") .. "'")
end
parent.name = require("Module:category tree/" .. parent.module).new(self:substitute_template_specs_in_args(parent.args))
else
parent.name = Category.new(pinfo)
end
end
table.insert(ret, parent)
end
if not is_set and self._info.label ~= "list of topics" and self._info.label ~= "list of sets" then
local pinfo = mw.clone(self._info)
pinfo.label = "list of topics"
table.insert(ret, {name = Category.new(pinfo), sort = (not self._lang and " " or "") .. self._info.label})
end
return ret
end
function Category:getChildren()
return nil
end
function Category:getUmbrella()
if not self._lang then
return nil
end
local uinfo = mw.clone(self._info)
uinfo.code = nil
return Category.new(uinfo)
end
function Category:getTOCTemplateName()
local lang = self._lang
local code = lang and lang:getCode() or "en"
return "Template:" .. code .. "-categoryTOC"
end
return export