Zelda Wiki

OoT Navi.png
Hey! Listen!

This wiki contains spoilers! Read at your own risk!

READ MORE

Zelda Wiki
Zelda Wiki
10,154
pages
(moving back to category)
m
 
(47 intermediate revisions by 2 users not shown)
Line 1: Line 1:
 
local p = {}
 
local p = {}
local cargo = mw.ext.cargo
+
local h = {}
  +
local utilsCode = require("Module:UtilsCode")
 
  +
local cache = mw.ext.LuaCache
local utilsGame = require("Module:UtilsGame")
 
  +
local utilsText = require('Module:UtilsText')
 
  +
local Franchise = require("Module:Franchise")
  +
local utilsArg = require("Module:UtilsArg")
  +
local utilsCargo = require("Module:UtilsCargo")
  +
local utilsError = require("Module:UtilsError")
  +
local utilsMarkup = require("Module:UtilsMarkup")
  +
local utilsPage = require("Module:UtilsPage")
  +
local utilsString = require('Module:UtilsString')
 
local utilsTable = require('Module:UtilsTable')
 
local utilsTable = require('Module:UtilsTable')
   
  +
local CARGO_TABLE = "Terminologies"
function p._Main (frame)
 
  +
local args = frame.args
 
  +
-- In the past Cargo has been iffy with storage from modules, so the actual Cargo store is still done on the actual template.
return p.Main(args["game"], args["term"], args["link"], args["plural"], args["display"], args["section"])
 
  +
-- We still do the validation + caching layer here, though.
  +
function p.TermStore(frame)
  +
local args, err = utilsArg.parse(frame:getParent().args, p.Templates["Term/Store"])
  +
local errCategories = err and err.categories or {}
  +
local result = args.singularTerm
  +
if args.plural and utilsString.isEmpty(args.pluralTerm) then
  +
table.insert(errCategories, "Pages with Invalid Arguments")
  +
utilsError.warn("<code>plural</code> option specified yet no plural term is defined. Using singular form.")
  +
elseif args.plural then
  +
result = args.pluralTerm
  +
end
  +
h.storeCache(args)
  +
return result .. utilsMarkup.categories(errCategories)
 
end
 
end
   
function p.Main(game, term, link, plural, display, section)
+
function p.Singular(frame)
  +
local args, err = utilsArg.parse(frame:getParent().args, p.Templates.Term)
local returnedValue = ""
 
  +
local printErrorCategories = not utilsPage.inNamespace({"User", "MediaWiki"}) -- MediaWiki namespace is listed here to prevent a weird bug with MediaWiki:Gadget-EditToolbarButtons.js
 
local fetchedTerm = p.fetchRow(game, term)
+
local result = p.printTerm(args, false, printErrorCategories)
if utilsCode.IsEmpty(fetchedTerm) then
+
if err and printErrorCategories then
  +
result = result .. utilsMarkup.categories(err.categories)
 
local displayedTerm = term
 
if not utilsCode.IsEmpty(display) then
 
displayedTerm = display
 
end
 
 
local linkedTerm = term
 
if not utilsCode.IsEmpty(section) then
 
linkedTerm = linkedTerm .. "#" .. section
 
end
 
return "<span class='explain facelift-term-invalid' title='Invalid or missing term'>[[" .. term .. "|" .. displayedTerm .. "]]</span>[[Category:Pages with Invalid or Missing Terms]][[Category:" .. utilsGame.AbbToGame(game) .. " Pages with Invalid or Missing Terms]]"
 
else
 
if link == "link" then
 
local gameSub = utilsGame.AbbToBaseGame(game, true)
 
returnedValue = returnedValue .. "[[" .. term
 
if not utilsCode.IsEmpty(section) then
 
returnedValue = returnedValue .. "#" .. section
 
elseif (gameSub ~= "Unknown" and game ~= "Series") then
 
returnedValue = returnedValue .. "#" .. gameSub
 
end
 
returnedValue = returnedValue .. "|"
 
else
 
returnedValue = returnedValue .. "<span class='term'>"
 
end
 
 
if not utilsCode.IsEmpty(display) then
 
returnedValue = returnedValue .. display
 
elseif plural == "plural" then
 
returnedValue = returnedValue .. fetchedTerm["plural"]
 
else
 
returnedValue = returnedValue .. fetchedTerm["term"]
 
end
 
 
if link == "link" then
 
returnedValue = returnedValue .. "]]"
 
else
 
returnedValue = returnedValue .. "</span>"
 
end
 
 
end
 
end
return returnedValue
+
return result
 
end
 
end
   
  +
function p.Plural(frame)
-- Returns raw term
 
  +
local args, err = utilsArg.parse(frame:getParent().args, p.Templates.Plural)
function p.fetchTerm(game, page)
 
  +
local printErrorCategories = not utilsPage.inNamespace({"User", "MediaWiki"})
return p.fetchRow(game, page)["term"]
 
  +
local result = p.printTerm(args, true, printErrorCategories)
  +
if err and printErrorCategories then
  +
result = result .. utilsMarkup.categories(err.categories)
  +
end
  +
return result
 
end
 
end
   
 
function p._fetchTerm(frame)
 
function p._fetchTerm(frame)
 
local args = frame.args
 
local args = frame.args
  +
args = utilsTable.mapValues(args, utilsString.trim)
return p.fetchTerm(args["game"], args["term"])
 
  +
args = utilsTable.mapValues(args, utilsString.nilIfEmpty)
  +
return p.fetchTerm(args.term, args.game)
 
end
 
end
   
  +
function p.printTerm(args, plural, printErrorCategories)
-- Returns raw plural
 
  +
local term, fetchErrors = p.fetchTerm(args.page, args.game, plural or args.plural)
function p.fetchPlural(game, page)
 
  +
return p.fetchRow(game, page)["plural"]
 
  +
local errorCategories = ""
  +
if printErrorCategories then
  +
local errors = utilsTable.concat(validationErrors or {}, fetchErrors or {})
  +
errorCategories = utilsMarkup.categories(errors)
  +
end
  +
local result = ""
  +
if not term then
  +
local errLink = utilsMarkup.sectionLink(args.page, args.section, args.display)
  +
result = utilsMarkup.inline(errLink, {
  +
class = "facelift-term-invalid",
  +
tooltip = "Invalid or missing term",
  +
})
  +
elseif args.link == "link" then
  +
local gameSub = args.game and Franchise.shortName(args.game)
  +
if not args.section and gameSub and args.game ~= "Series" then
  +
args.section = gameSub
  +
end
  +
result = utilsMarkup.sectionLink(args.page, args.section, args.display or term)
  +
else
  +
result = utilsMarkup.class("term", args.display or term)
  +
end
  +
return result .. errorCategories
 
end
 
end
   
function p._fetchPlural(frame)
+
function p.fetchTerm(page, game, plural)
  +
if not page then
local args = frame.args
 
  +
return nil
return p.fetchPlural(args["game"], args["term"])
 
end
 
 
-- Returns the table row of the specified game for the specified page.
 
-- Returns the entry for "Series" if not found.
 
function p.fetchRow(game, page)
 
local tables = 'Terminologies'
 
local fields = 'games, term, plural'
 
local queryArgs = {
 
where = '_pageName = "' .. page .. '"'
 
}
 
local result = cargo.query( tables, fields, queryArgs )
 
 
--Looks for the game
 
for _, row in pairs(result) do
 
local games = utilsText.split(row.games)
 
if utilsTable.keyOf(games, game) then
 
return row
 
end
 
 
end
 
end
 
 
  +
-- Cargo queries don't allow # and it's impossible to have a page with # anyway because of section anchors.
--Else, looks for "Series"
 
  +
-- Ideally, users should input the name of the page where the term is stored (e.g. Swordsman Newsletter 4 instead of Swordsman Newsletter #4)
for _, row in pairs(result) do
 
  +
page = string.gsub(page, "#", "")
local games = utilsText.split(row.games)
 
  +
if utilsTable.keyOf(games, "Series") then
 
  +
-- Things like {{PAGENAME}} return HTML entities. These have to be removed as the "#" character cannot be used in Cargo queries.
return row
 
  +
page = mw.text.decode(page)
  +
  +
local cacheKey = h.cacheKey(page, game, plural)
  +
local term = cache.get(cacheKey)
  +
if term then
  +
return term
  +
end
  +
  +
local rows = utilsCargo.query("Terminologies=terms, Terminologies__games=termGames", "termGames._value=game, terms.term=term, terms.plural=plural", {
  +
join = "terms._ID=termGames._rowID",
  +
where = utilsCargo.allOf(
  +
{ _pageName = page },
  +
utilsCargo.IN("termGames._value", {"Series", game})
  +
)
  +
})
  +
local termsByGame = utilsTable.keyBy(rows, "game")
  +
local term = termsByGame[game or "Series"] or termsByGame["Series"]
  +
local subtitle = Franchise.shortName(game or "Series")
  +
local errCategories = {}
  +
if mw.title.getCurrentTitle().nsText ~= "User" then
  +
table.insert(errCategories, "Pages with Invalid or Missing Terms")
  +
if subtitle then
  +
table.insert(errCategories, string.format("%s Pages with Invalid or Missing Terms", subtitle))
 
end
 
end
 
end
 
end
  +
if not term then
  +
return nil, errCategories
  +
elseif plural and utilsString.isEmpty(term.plural) then
  +
utilsError.warn(string.format("Term <code>%s</code> has no plural form defined. Using singular form.", term.term))
  +
return term.term, errCategories
  +
elseif plural then
  +
cache.set(cacheKey, term.plural)
  +
return term.plural
  +
else
  +
cache.set(cacheKey, term.term)
  +
return term.term
  +
end
  +
end
  +
  +
function p.fetchSubjects(term, game)
  +
local rows = utilsCargo.query(CARGO_TABLE, "_pageName", {
  +
where = utilsCargo.allOf(
  +
{ term = term },
  +
game and ("games HOLDS '%s'"):format(game)
  +
)
  +
})
  +
return utilsTable.map(rows, "_pageName")
  +
end
  +
  +
function h.cacheKey(page, game, plural)
  +
local key = string.format("%s.%s.%s", plural and "plural" or "term", game or "Series", page)
  +
return key
  +
end
  +
  +
function h.storeCache(args)
  +
local singularTerm = args.singularTerm
  +
local pluralTerm = args.pluralTerm
  +
local games = args.games
  +
local plural = args.plural
  +
local page = mw.title.getCurrentTitle().text
 
 
  +
local cacheTerms = {}
return nil
 
  +
for _, game in ipairs(games or {}) do
  +
if singularTerm then
  +
local key = h.cacheKey(page, game, false)
  +
cacheTerms[key] = singularTerm
  +
end
  +
if pluralTerm then
  +
local key = h.cacheKey(page, game, true)
  +
cacheTerms[key] = pluralTerm
  +
end
  +
end
  +
cache.setMulti(cacheTerms)
 
end
 
end
  +
  +
p.Schemas = {
  +
fetchTerm = {
  +
page = {
  +
type = "string",
  +
required = true,
  +
desc = "The name of a wiki article from which to retrieve a term.",
  +
},
  +
game = {
  +
type = "string",
  +
default = mw.dumpObject("Series"),
  +
desc = "A game code. See [[Data:Franchise]].",
  +
},
  +
plural = {
  +
type = "boolean",
  +
desc = "If true, retrieves the plural form."
  +
},
  +
},
  +
fetchSubjects = {
  +
term = {
  +
type = "string",
  +
required = true,
  +
},
  +
game = {
  +
type = "string"
  +
}
  +
}
  +
}
  +
  +
p.Documentation = {
  +
fetchTerm = {
  +
params = {"page", "game", "plural"},
  +
returns = {
  +
"The term for the given article and game, or nil if none found.",
  +
"An error category if no term was found.",
  +
},
  +
cases = {
  +
outputOnly = true,
  +
{
  +
args = {"Dynalfos", "OoT"},
  +
expect = { "Dinolfos", nil },
  +
},
  +
{
  +
desc = "Defaults to series term.",
  +
args = {"Dinolfos"},
  +
expect = {"Dynalfos"},
  +
},
  +
{
  +
desc = "Defaults to series term when term does not exist for specified game.",
  +
args = {"Dinolfos", "ALttP"},
  +
expect = {"Dynalfos"},
  +
},
  +
{
  +
desc = "Error when page does store any terms (game specified).",
  +
args = {"Flippityfloppito", "SS"},
  +
expect = {nil, {"Pages with Invalid or Missing Terms", "Skyward Sword Pages with Invalid or Missing Terms"}}
  +
},
  +
{
  +
desc = "Error when page does store any terms (no game specified).",
  +
args = {"Flippityfloppityfloo"},
  +
expect = {nil, {"Pages with Invalid or Missing Terms", "The Legend of Zelda Series Pages with Invalid or Missing Terms"}}
  +
},
  +
{
  +
desc = "Plural",
  +
args = {"Bubble", "Series", true},
  +
expect = {"Bubbles", nil},
  +
},
  +
{
  +
desc = "Returns singular when no plural form exists.",
  +
args = {"A Brother's Roast", "BotW", true},
  +
expect = { "A Brother's Roast", {
  +
"Pages with Invalid or Missing Terms",
  +
"Breath of the Wild Pages with Invalid or Missing Terms",
  +
}}
  +
}
  +
},
  +
},
  +
fetchSubjects = {
  +
params = {"term", "game"},
  +
returns = "Returns the names of wiki pages that store the given term. If game is specified, the function will only return pages that store the term for that game.",
  +
cases = {
  +
{
  +
args = {"Wood"},
  +
expect = {"Wood", "Wood (Character)"},
  +
},
  +
{
  +
args = {"Wood", "ST"},
  +
expect = {"Wood (Character)"},
  +
},
  +
{
  +
args = {"Link", "MM"},
  +
expect = {"Link", "Link (Goron)", "Mr. No Fairy"},
  +
},
  +
{
  +
args = {"Fooloo Limpah"},
  +
expect = {},
  +
},
  +
},
  +
}
  +
}
  +
  +
p.Templates = {
  +
Term = {
  +
purpose = "Returns the proper singular form of a term for any given topic in {{TLoZ|Series}}. Terms are stored using [[:Template:Term/Store]].",
  +
format = "inline",
  +
params = {
  +
[1] = {
  +
name = "game",
  +
type = "string",
  +
enum = Franchise.enum({ includeSeries = true }),
  +
desc = "The game from which to fetch the term of the given subject. Defaults to <code>Series</code>.",
  +
trim = true,
  +
nilIfEmpty = true,
  +
},
  +
[2] = {
  +
name = "page",
  +
type = "wiki-page-name",
  +
desc = "The name of the page for said subject.",
  +
trim = true,
  +
nilIfEmpty = true,
  +
},
  +
[3] = {
  +
name = "link",
  +
type = "string",
  +
desc = "Entering anything in this field will output the result as a link. (Enter <code>link</code> for standardization)",
  +
trim = true,
  +
nilIfEmpty = true,
  +
},
  +
[4] = {
  +
name = "plural",
  +
type = "string",
  +
deprecated = true,
  +
desc = "Deprecated.",
  +
trim = true,
  +
nilIfEmpty = true,
  +
},
  +
display = {
  +
type = "string",
  +
desc = "Alternative display text for term.",
  +
trim = true,
  +
nilIfEmpty = true,
  +
},
  +
section = {
  +
type = "string",
  +
desc = "Section of <code>page</code> to link to.",
  +
trim = true,
  +
nilIfEmpty = true,
  +
},
  +
}
  +
},
  +
["Term/Store"] = {
  +
purpose = "Used in article leads to store [[Guidelines:Terminology|terms]], for use by [[Template:Term]] and other modules.",
  +
format = "inline",
  +
paramOrder = {1, 2, 3, 4},
  +
params = {
  +
[1] = {
  +
name = "singularTerm",
  +
type = "string",
  +
required = true,
  +
desc = "The singular form of the term.",
  +
},
  +
[2] = {
  +
name = "pluralTerm",
  +
type = "string",
  +
required = true,
  +
desc = "The plural form of the term. Leave empty for characters or other terms where no plural form applies.",
  +
trim = true,
  +
},
  +
[3] = {
  +
name = "games",
  +
type = "string",
  +
required = true,
  +
enum = Franchise.enum({ includeSeries = true }),
  +
desc = "Comma-separated list of games to which the term applies. For example, <code>OoT, SS, BotW</code>.",
  +
split = true,
  +
trim = true,
  +
nilIfEmpty = true,
  +
},
  +
[4] = {
  +
name = "plural",
  +
type = "string",
  +
desc = "Entering <code>plural</code> here will make the template output the plural term instead of the singular.",
  +
canOmit = true,
  +
},
  +
},
  +
},
  +
}
  +
p.Templates.Plural = p.Templates.Term
   
 
return p
 
return p

Latest revision as of 17:23, 15 November 2020

This is the main module for the following templates: In addition, this module exports the following functions.

fetchTerm

fetchTerm(page, [game], [plural])

Parameters
Returns
  • The term for the given article and game, or nil if none found.
  • An error category if no term was found.
Examples
InputOutputStatus
fetchTerm("Dynalfos", "OoT")
"Dinolfos"
Green check.svg
nil
Green check.svg
Defaults to series term.
fetchTerm("Dinolfos")
"Dynalfos"
Green check.svg
nil
Green check.svg
Defaults to series term when term does not exist for specified game.
fetchTerm("Dinolfos", "ALttP")
"Dynalfos"
Green check.svg
nil
Green check.svg
Error when page does store any terms (game specified).
fetchTerm("Flippityfloppito", "SS")
nil
Green check.svg
{
  "Pages with Invalid or Missing Terms",
  "Skyward Sword Pages with Invalid or Missing Terms",
}
Green check.svg
Error when page does store any terms (no game specified).
fetchTerm("Flippityfloppityfloo")
nil
Green check.svg
{
  "Pages with Invalid or Missing Terms",
  "The Legend of Zelda Series Pages with Invalid or Missing Terms",
}
Green check.svg
Plural
fetchTerm("Bubble", "Series", true)
"Bubbles"
Green check.svg
nil
Green check.svg
Returns singular when no plural form exists.
fetchTerm("A Brother's Roast", "BotW", true)
Expected
"A Brother's Roast"
Actual
""
TFH Red Link desperate.png
Expected
{
  "Pages with Invalid or Missing Terms",
  "Breath of the Wild Pages with Invalid or Missing Terms",
}
Actual
nil
TFH Red Link desperate.png

fetchSubjects

fetchSubjects(term, [game])

Parameters
Returns
  • Returns the names of wiki pages that store the given term. If game is specified, the function will only return pages that store the term for that game.
Examples
InputOutputStatus
fetchSubjects("Wood")
{"Wood", "Wood (Character)"}
Green check.svg
fetchSubjects("Wood", "ST")
{"Wood (Character)"}
Green check.svg
fetchSubjects("Link", "MM")
{"Link", "Link (Goron)", "Mr. No Fairy"}
Green check.svg
fetchSubjects("Fooloo Limpah")
{}
Green check.svg

local p = {}
local h = {}

local cache = mw.ext.LuaCache

local Franchise = require("Module:Franchise")
local utilsArg = require("Module:UtilsArg")
local utilsCargo = require("Module:UtilsCargo")
local utilsError = require("Module:UtilsError")
local utilsMarkup = require("Module:UtilsMarkup")
local utilsPage = require("Module:UtilsPage")
local utilsString = require('Module:UtilsString')
local utilsTable = require('Module:UtilsTable')

local CARGO_TABLE = "Terminologies"

-- In the past Cargo has been iffy with storage from modules, so the actual Cargo store is still done on the actual template.
-- We still do the validation + caching layer here, though.
function p.TermStore(frame)
	local args, err = utilsArg.parse(frame:getParent().args, p.Templates["Term/Store"])
	local errCategories = err and err.categories or {}
	local result = args.singularTerm
	if args.plural and utilsString.isEmpty(args.pluralTerm) then
		table.insert(errCategories, "Pages with Invalid Arguments")
		utilsError.warn("<code>plural</code> option specified yet no plural term is defined. Using singular form.")
	elseif args.plural then
		result = args.pluralTerm
	end
	h.storeCache(args)
	return result .. utilsMarkup.categories(errCategories)
end

function p.Singular(frame)
	local args, err = utilsArg.parse(frame:getParent().args, p.Templates.Term)
	local printErrorCategories = not utilsPage.inNamespace({"User", "MediaWiki"}) -- MediaWiki namespace is listed here to prevent a weird bug with MediaWiki:Gadget-EditToolbarButtons.js
	local result = p.printTerm(args, false, printErrorCategories)
	if err and printErrorCategories then 
		result = result .. utilsMarkup.categories(err.categories)
	end
	return result
end

function p.Plural(frame)
	local args, err = utilsArg.parse(frame:getParent().args, p.Templates.Plural)
	local printErrorCategories = not utilsPage.inNamespace({"User", "MediaWiki"})
	local result = p.printTerm(args, true, printErrorCategories)
	if err and printErrorCategories then
		result = result .. utilsMarkup.categories(err.categories)
	end
	return result
end

function p._fetchTerm(frame)
	local args = frame.args
	args = utilsTable.mapValues(args, utilsString.trim)
	args = utilsTable.mapValues(args, utilsString.nilIfEmpty)
	return p.fetchTerm(args.term, args.game)
end

function p.printTerm(args, plural, printErrorCategories)
	local term, fetchErrors = p.fetchTerm(args.page, args.game, plural or args.plural)
	
	local errorCategories = ""
	if printErrorCategories then
		local errors = utilsTable.concat(validationErrors or {}, fetchErrors or {})
		errorCategories = utilsMarkup.categories(errors)
	end
	local result = ""
	if not term then
		local errLink = utilsMarkup.sectionLink(args.page, args.section, args.display)
		result = utilsMarkup.inline(errLink, {
			class = "facelift-term-invalid",
			tooltip = "Invalid or missing term",
		})
	elseif args.link == "link" then
		local gameSub = args.game and Franchise.shortName(args.game)
		if not args.section and gameSub and args.game ~= "Series" then
			args.section = gameSub
		end
		result = utilsMarkup.sectionLink(args.page, args.section, args.display or term)
	else
		result = utilsMarkup.class("term", args.display or term)
	end
	return result .. errorCategories
end

function p.fetchTerm(page, game, plural)
	if not page then
		return nil
	end
	
	-- Cargo queries don't allow # and it's impossible to have a page with # anyway because of section anchors. 
	 -- Ideally, users should input the name of the page where the term is stored (e.g. Swordsman Newsletter 4 instead of Swordsman Newsletter #4)
	page = string.gsub(page, "#", "")
	
	-- Things like {{PAGENAME}} return HTML entities. These have to be removed as the "#" character cannot be used in Cargo queries.
	page = mw.text.decode(page) 
	
	local cacheKey = h.cacheKey(page, game, plural)
	local term = cache.get(cacheKey)
	if term then
		return term
	end
	
	local rows = utilsCargo.query("Terminologies=terms, Terminologies__games=termGames", "termGames._value=game, terms.term=term, terms.plural=plural", {
		join = "terms._ID=termGames._rowID",
		where = utilsCargo.allOf(
			{ _pageName = page },
			utilsCargo.IN("termGames._value", {"Series", game})
		)
	})
	local termsByGame = utilsTable.keyBy(rows, "game")
	local term = termsByGame[game or "Series"] or termsByGame["Series"]
	local subtitle = Franchise.shortName(game or "Series")
	local errCategories = {}
	if mw.title.getCurrentTitle().nsText ~= "User" then
		table.insert(errCategories, "Pages with Invalid or Missing Terms")
		if subtitle then
			table.insert(errCategories, string.format("%s Pages with Invalid or Missing Terms", subtitle))
		end
	end
	if not term then
		return nil, errCategories
	elseif plural and utilsString.isEmpty(term.plural) then
		utilsError.warn(string.format("Term <code>%s</code> has no plural form defined. Using singular form.", term.term))
		return term.term, errCategories
	elseif plural then
		cache.set(cacheKey, term.plural)
		return term.plural
	else
		cache.set(cacheKey, term.term)
		return term.term
	end
end

function p.fetchSubjects(term, game)
	local rows = utilsCargo.query(CARGO_TABLE, "_pageName", {
		where = utilsCargo.allOf(
			{ term = term },
			game and ("games HOLDS '%s'"):format(game)
		)
	})
	return utilsTable.map(rows, "_pageName")
end

function h.cacheKey(page, game, plural)
	local key = string.format("%s.%s.%s", plural and "plural" or "term", game or "Series", page)
	return key
end

function h.storeCache(args)
	local singularTerm = args.singularTerm
	local pluralTerm = args.pluralTerm
	local games = args.games
	local plural = args.plural
	local page = mw.title.getCurrentTitle().text
	
	local cacheTerms = {}
	for _, game in ipairs(games or {}) do
		if singularTerm then
			local key = h.cacheKey(page, game, false)
			cacheTerms[key] = singularTerm
		end
		if pluralTerm then
			local key = h.cacheKey(page, game, true)
			cacheTerms[key] = pluralTerm
		end
	end
	cache.setMulti(cacheTerms)
end

p.Schemas = {
	fetchTerm = {
		page = {
			type = "string",
			required = true,
			desc = "The name of a wiki article from which to retrieve a term.",
		},
		game = {
			type = "string",
			default = mw.dumpObject("Series"),
			desc = "A game code. See [[Data:Franchise]].",
		},
		plural = {
			type = "boolean",
			desc = "If true, retrieves the plural form."
		},
	},
	fetchSubjects = {
		term = {
			type = "string",
			required = true,
		},
		game = {
			type = "string"
		}
	}
}

p.Documentation = {
	fetchTerm = {
		params = {"page", "game", "plural"},
		returns = {
			"The term for the given article and game, or nil if none found.",
			"An error category if no term was found.",
		},
		cases = {
			outputOnly = true,
			{
				args = {"Dynalfos", "OoT"},
				expect = { "Dinolfos", nil },
			},
			{
				desc = "Defaults to series term.",
				args = {"Dinolfos"},
				expect = {"Dynalfos"},
			},
			{
				desc = "Defaults to series term when term does not exist for specified game.",
				args = {"Dinolfos", "ALttP"},
				expect = {"Dynalfos"},
			},
			{
				desc = "Error when page does store any terms (game specified).",
				args = {"Flippityfloppito", "SS"},
				expect = {nil, {"Pages with Invalid or Missing Terms", "Skyward Sword Pages with Invalid or Missing Terms"}}
			},
			{
				desc = "Error when page does store any terms (no game specified).",
				args = {"Flippityfloppityfloo"},
				expect = {nil, {"Pages with Invalid or Missing Terms", "The Legend of Zelda Series Pages with Invalid or Missing Terms"}}
			},
			{
				desc = "Plural",
				args = {"Bubble", "Series", true},
				expect = {"Bubbles", nil},
			},
			{
				desc = "Returns singular when no plural form exists.",
				args = {"A Brother's Roast", "BotW", true},
				expect = { "A Brother's Roast", {
				  "Pages with Invalid or Missing Terms",
				  "Breath of the Wild Pages with Invalid or Missing Terms",
				}}
			}
		},
	},
	fetchSubjects = {
		params = {"term", "game"},
		returns = "Returns the names of wiki pages that store the given term. If game is specified, the function will only return pages that store the term for that game.",
		cases = {
			{
				args = {"Wood"},
				expect = {"Wood", "Wood (Character)"},
			},
			{
				args = {"Wood", "ST"},
				expect = {"Wood (Character)"},
			},
			{
				args = {"Link", "MM"},
				expect = {"Link", "Link (Goron)", "Mr. No Fairy"},
			},
			{
				args = {"Fooloo Limpah"},
				expect = {},
			},
		},
	}
}

p.Templates = {
	Term = {
		purpose = "Returns the proper singular form of a term for any given topic in {{TLoZ|Series}}. Terms are stored using [[:Template:Term/Store]].",
		format = "inline",
		params = {
			[1] = {
				name = "game",
				type = "string",
				enum = Franchise.enum({ includeSeries = true }),
				desc = "The game from which to fetch the term of the given subject. Defaults to <code>Series</code>.",
				trim = true,
				nilIfEmpty = true,
			},
			[2] = {
				name = "page",
				type = "wiki-page-name",
				desc = "The name of the page for said subject.",
				trim = true,
				nilIfEmpty = true,
			},
			[3] = {
				name = "link",
				type = "string",
				desc = "Entering anything in this field will output the result as a link. (Enter <code>link</code> for standardization)",
				trim = true,
				nilIfEmpty = true,
			},
			[4] = {
				name = "plural",
				type = "string",
				deprecated = true,
				desc = "Deprecated.",
				trim = true,
				nilIfEmpty = true,
			},
			display = {
				type = "string",
				desc = "Alternative display text for term.",
				trim = true,
				nilIfEmpty = true,
			},
			section = {
				type = "string",
				desc = "Section of <code>page</code> to link to.",
				trim = true,
				nilIfEmpty = true,
			},
		}
	},
	["Term/Store"] = {
		purpose = "Used in article leads to store [[Guidelines:Terminology|terms]], for use by [[Template:Term]] and other modules.",
		format = "inline",
		paramOrder = {1, 2, 3, 4},
		params = {
			[1] = {
				name = "singularTerm",
				type = "string",
				required = true,
				desc = "The singular form of the term.",
			},
			[2] = {
				name = "pluralTerm",
				type = "string",
				required = true,
				desc = "The plural form of the term. Leave empty for characters or other terms where no plural form applies.",
				trim = true,
			},
			[3] = {
				name = "games",
				type = "string",
				required = true,
				enum = Franchise.enum({ includeSeries = true }),
				desc = "Comma-separated list of games to which the term applies. For example, <code>OoT, SS, BotW</code>.",
				split = true,
				trim = true,
				nilIfEmpty = true,
			},
			[4] = {
				name = "plural",
				type = "string",
				desc = "Entering <code>plural</code> here will make the template output the plural term instead of the singular.",
				canOmit = true,
			},
		},
	},
}
p.Templates.Plural = p.Templates.Term

return p