Module:Nomenclature: Difference between revisions

From Zelda Wiki, the Zelda encyclopedia
Jump to navigation Jump to search
m (Reverted edits by MannedTooth (talk) to last revision by HylianKing)
Tag: Rollback
mNo edit summary
 
(36 intermediate revisions by 6 users not shown)
Line 2: Line 2:
local h = {}
local h = {}


local cargo = mw.ext.cargo
local Franchise = require("Module:Franchise")
local expgame = require('Module:Exp Game')
local Language = require("Module:Language")
local tab2 = require('Module:Tab2')
local Term = require("Module:Term")
local Term = require('Module:Term')
local utilsCargo = require("Module:UtilsCargo")
local translation = require('Module:Translation')
local utilsError = require('Module:UtilsError')
local utilsGame = require('Module:UtilsGame')
local utilsLanguage = require('Module:UtilsLanguage')
local utilsMarkup = require("Module:UtilsMarkup")
local utilsString = require("Module:UtilsString")
local utilsTable = require('Module:UtilsTable')
local utilsTable = require('Module:UtilsTable')
local DISCORD_URL = require("Module:Constants/url/discord")


-- For creating nomenclature tables
-- For creating nomenclature tables
function p.Main( frame )
function p.Main(frame)
local term = frame.args["term"]
local subject = frame.args["term"]
if utilsString.isEmpty(term) then
if subject == nil or subject == "" then
term = mw.title.getCurrentTitle().subpageText
subject = mw.title.getCurrentTitle().subpageText
end
local cargoData = translation.fetchTranslations(term)
local displayGames = false
local skipMeanings = true
for key, row in ipairs(cargoData) do
if cargoData[1]["game"] ~= row["game"] or utilsGame.IsOrHasRemake(row["game"]) == true then
displayGames = true
end
if not utilsString.isEmpty(row["meaning"]) then
skipMeanings = false
end
end
end
local resultTable = h.CreateTable(skipMeanings)
resultTable = h.CreateRows(resultTable, cargoData, skipMeanings, displayGames)
resultTable:node(mw.html.create("tr"):node(mw.html.create("th"):attr("colspan", "4"):wikitext("<small>This table was generated using [[Data:Translations|translation pages]].<br>To request an addition, please <span class='plainlinks'>[https://discord.gg/eJnnvYb contact]</span> a [[Zelda Wiki:Staff|staff member]] with a [[Guidelines:References|reference]].</small>")))
return resultTable
end
--Create an empty table with headers
function h.CreateTable(skipMeanings)
--Table structure
local resultTable = mw.html.create("table")
:addClass("wikitable")
:css{
["margin"] = "1em 0",
["font-size"] = "95%",
}:done()
--Global header
local translations = h.fetchTranslations(subject)
local headerRow = mw.html.create("tr"):done()
local translations, hasMeanings, displayGames = h.formatData(translations)
local headerContent = mw.html.create("th")
local nomenclatureTable = h.printNomenclatureTable(translations, hasMeanings, displayGames)
:wikitext("[[File:TMC Forest Minish Artwork.png|20px|link=]] Names in Other Regions [[File:TMC Jabber Nut Sprite.png|link=]]")
:attr("colspan", "4")
:css{
["font-size"] = "110%",
}:done()
headerRow:node(headerContent)
return nomenclatureTable
resultTable:node(headerRow)
--Individual headers
--Language
headerRow = mw.html.create("tr"):done()
headerContent = mw.html.create("th")
:wikitext("Language")
:attr("colspan", "2"):done()
headerRow:node(headerContent)
--Name
headerContent = mw.html.create("th")
:wikitext("Name"):done()
headerRow:node(headerContent)
--Meaning
if not skipMeanings then
headerContent = mw.html.create("th")
:wikitext("Meaning"):done()
headerRow:node(headerContent)
end
resultTable:node(headerRow)
return resultTable
end
end


function h.CreateRows(output, cargoData, skipMeanings, displayGames)
function h.fetchTranslations(page)
h.SortTranslations(cargoData)
local whereClause = utilsCargo.allOf({
for _, row in ipairs(cargoData) do
    subject = page
if not row.skip and row.translation ~= "N/A" then
})
h.ProcessRow(output, cargoData, row, skipMeanings, displayGames)
-- Fetch translations of synonyms
end
local term = Term.fetchTerm(page)
if term and not string.find(page, "%)$") then -- without this ) check, Wood (Character) would also fetch data for BotW Wood
term = string.gsub(term, "#", "") -- terms with # in them are stored in a version of the page without the #, because MediaWiki. Also Cargo doesn't allow queries with # in them.
whereClause = whereClause .. " OR " ..utilsCargo.allOf({
    term = term
}, "subject NOT LIKE '%)'") -- without this, requesting "Wood" would also fetch data for Wood (Character)
end
end
local translations = utilsCargo.query("Translations", "game, term, lang, translation, meaning, ref", {
where = whereClause
})
return output
return translations
end
 
function h.SortTranslations(translations)
local lookupLang = utilsTable.invert(utilsLanguage.getCodes())
local lookupGame = utilsTable.invert(utilsGame.GetSortOrder("canon"))
table.sort(translations, function (a,b)
if (lookupLang[a.language] or 0) == (lookupLang[b.language] or 0) then
return (lookupGame[a.game] or 0) < (lookupGame[b.game] or 0)
else
return (lookupLang[a.language] or 0) < (lookupLang[b.language] or 0)
end
end
)
end
end


function h.ProcessRow(output, cargoData, row, skipMeanings, displayGames)
function h.formatData(translations)
local meanings = h.GetMeanings(cargoData, row)
local hasMeanings = utilsTable.find(translations, function(translation)
local langText, flag = utilsLanguage.printLanguage(row.language)
return translation.meaning == nil and translation.meaning ~= "" and translation.term ~= translation.translation
local tr = output:tag('tr')
end)
:tag("td")
:addClass("nomenclature-flag")
:wikitext(flag)
:done()
:tag("td")
:wikitext(langText)
:done()
h.PrintNames(tr, cargoData, row, displayGames)
h.MarkRowsToSkip(cargoData, row)
if not skipMeanings then
h.PrintMeanings(tr, meanings)
end
end
-- Determine whether to display Exp Game
 
local seenGames = {}
function h.GetMeanings(cargoData, row)
local gameCount = 0
local ret = { row.meaning }
local hasRemakes = false
for _, row2 in ipairs(cargoData) do
for i, translation in ipairs(translations) do
if h.SameLangDifTranslations(row, row2) then
local game = translation.game
ret[#ret+1] = row2.meaning
if not seenGames[game] then
gameCount = gameCount + 1
seenGames[game] = true
end
end
end
if Franchise.isRemake(game) or Franchise.hasRemakes(game) then
return ret
hasRemakes = true
end
 
function h.PrintNames(tr, cargoData, row, displayGames)
local td = tr:tag('td')
:wikitext(table.concat(h.GetNamesAndTheirGames(cargoData, row, displayGames), '<br>'))
end
 
function h.GetNamesAndTheirGames(cargoData, row, displayGames)
local ret = { h.GetOneNameAndGames(cargoData, row, displayGames) }
for _, row2 in ipairs(cargoData) do
if h.SameLangDifTranslations(row, row2) then
games = h.GamesWithSameTranslation(row2, cargoData)
ret[#ret+1] = h.GetOneNameAndGames(cargoData, row2, displayGames)
end
end
end
end
return ret
local displayGames = gameCount > 1 or hasRemakes
end
 
-- Group translations by language and then by name
function h.GetOneNameAndGames(cargoData, row, displayGames)
local gameOrderLookup = utilsTable.invert(Franchise.enum())
local games = h.GamesWithSameTranslation(row, cargoData)
translations = utilsTable.sortBy(translations, function(translation)
local result = row.translation
return gameOrderLookup[translation.game] or 1000
if displayGames == true then
end)
result = result .. " " .. expgame.Display(games)
translations = utilsTable.groupBy(translations, "lang")
end
translations = utilsTable.mapValues(translations, utilsTable._groupBy("translation"))
local refs = h.RefsWithSameTranslation(row, cargoData)
-- Creates a list of unique translations grouped by language
for key, ref in ipairs(refs) do
-- For each unique translation, lists which games have that translation, assigns a meaning to it, and creates the refs
if not utilsString.isEmpty(ref) then
-- If multiple games have different meanings for the same translation, we use the latest game
result = result .. mw.getCurrentFrame():extensionTag("ref", ref)
local hasMeanings = false
local translationLists = {}
for lang, langTranslations in pairs(translations) do
local translationList = {}
for translation, translationGames in pairs(langTranslations) do
local translationListItem = {
translation = translation,
games = {},
meaning = "&nbsp;",
term = translationGames[1].term,
refs = "",
}
for i, translationGame in ipairs(translationGames) do
local meaning = translationGame.meaning
if meaning ~= nil and meaning ~= "" then
translationListItem.meaning = meaning
hasMeanings = true
end
table.insert(translationListItem.games, translationGame.game)
translationListItem.refs = translationListItem.refs..h.printRef(translation, translationGame.ref)
end
table.insert(translationList, translationListItem)
end
end
-- Sort translations by their earliest appearance
translationList = utilsTable.sortBy(translationList, function(translation)
return gameOrderLookup[translation.games[1]]
end)
translationLists[lang] = translationList
end
end
return result
return translationLists, hasMeanings, displayGames
end
end


function h.GamesWithSameTranslation(row, cargoData)
local refCount = 0
local ret = {}
local refNamePrefix = "nomenclature-"
for _, row2 in ipairs(cargoData) do
local seenCitations = {} -- to prevent duplicate citations in the ==References== section on the page
if h.SameLangSameTranslation(row, row2) then
local seenRefs = {} -- to prevent duplicate reference markers in a given Nomenclature table row (e.g. [[Bomb]] Simplified Chinese)
ret[#ret+1] = row2.game
function h.printRef(translation, citation)
end
if citation == nil or citation == "" then
return ""
end
end
return ret
seenRefs[translation] = seenRefs[translation] or {}
end
local refSeen = seenRefs[translation][citation]
 
local citationIndex = seenCitations[citation]
function h.RefsWithSameTranslation(row, cargoData)
local ret = {}
local refContent
for _, row2 in ipairs(cargoData) do
if refSeen then
if h.SameLangSameTranslation(row, row2) then
-- Do nothing, the ref is already being shown for this translation due to it being the same in multiple games
ret[#ret+1] = row2.reference
-- (happens when the source is a book like E)
end
return ""
elseif citationIndex then
-- refContent stays nil since we're re-using an existing ref
refContent = nil
seenRefs[translation][citation] = true
else
-- create a new ref
refCount = refCount + 1
citationIndex = refCount
refContent = citation
seenCitations[citation] = citationIndex
seenRefs[translation][citation] = true
end
end
return ret
end
local frame = mw.getCurrentFrame()
 
if frame:getParent().args[1] then
function h.SameLangSameTranslation(row1, row2)
-- workaround if Nomenclature is used multiple times per page
return row1.language == row2.language and row1.translation == row2.translation
local subject = frame.args["term"]
end
return frame:extensionTag({
 
name = "ref",
function h.SameLangDifTranslations(row1, row2)
args = { name = refNamePrefix..subject..'-'..citationIndex },
return row1.language == row2.language and row1.translation ~= row2.translation
content = refContent,
end
})
 
function h.SameLang(row1, row2)
return row1.language == row2.language
end
 
function h.PrintMeanings(tr, meanings)
local meaningsDisplays = h.ProcessMeanings(meanings)
td = tr:tag('td')
:wikitext(table.concat(meaningsDisplays, '<br>'))
end
 
function h.MarkRowsToSkip(cargoData, row)
for _, row2 in ipairs(cargoData) do
if h.SameLang(row, row2) then
row2.skip = true
end
end
end
return frame:extensionTag({
name = "ref",
args = { name = refNamePrefix..citationIndex },
content = refContent,
})
end
end


function h.ProcessMeanings(meanings)
function h.printNomenclatureTable(translationsByLang, hasMeanings, displayGames)
local ret = {}
local html = mw.html.create("table")
for k, v in pairs(meanings) do
:addClass("wikitable")
if utilsString.isEmpty(v) then
:tag("tr")
ret[#ret+1] = '&nbsp;'
:tag("th")
else
:addClass("nomenclature__header")
ret[#ret+1] = v
:attr("colspan", hasMeanings and 3 or 2)
end
:wikitext("[[File:ZW Nomenclature Asset.png|20px|link=]] Names in Other Regions [[File:ZW Nomenclature Asset 2.png|link=]]")
:done()
:done()
:done()
local columns = html:tag("tr")
columns:tag("th"):wikitext("Language")
columns:tag("th"):wikitext("Names")
if hasMeanings then
columns:tag("th"):wikitext("Meanings")
end
end
return ret
end
-- TRANSLATION PAGES
function p._CreateTranslationTables(frame)
local args = frame:getParent().args
local tabs = {}
for i, lang in ipairs(Language.enum()) do
local index = 0
local translations = translationsByLang[lang]
while true do
if translations then
index = index + 1
h.addRow(html, hasMeanings, displayGames, lang, translations)
if utilsString.isEmpty(args["tab" .. index]) then
break
else
table.insert(tabs, {tabName = args["tab" .. index], tabContent = args["subjects"]})
end
end
end
end
return p.CreateTranslationTables(args["game"], args["filetype"], args["header"], tabs, args["subjects"])
local footerText = mw.getCurrentFrame():preprocess("<small>This table was generated using [[Data:Translations|translation pages]].<br>To request an addition, please contact a [[Zelda Wiki:Staff|staff member]] with a [[Guidelines:References|reference]].</small>")
html:tag("tr")
:tag("th")
:attr("colspan", "3")
:wikitext(footerText)
return tostring(html:allDone())
end
end


function p.CreateTranslationTables(game, filetype, header, tabs, subjects)
function h.addRow(html, hasMeanings, displayGames, lang, translations)
subjects = utilsString.split(subjects)
local row = html:tag("tr")
local langCell = mw.html.create("td"):addClass("nomenclature__cell nomenclature__cell--language")
local nameCell = mw.html.create("td"):addClass("nomenclature__cell nomenclature__cell--name")
local meaningCell = mw.html.create("td"):addClass("nomenclature__cell nomenclature__cell--meanings")
if filetype == "Screenshot" then
local lect = Language.getLect(lang)
filetype = ""
langCell:tag("div")
:addClass("nomenclature__language")
:tag("div")
:addClass("nomenclature__language-flags")
:wikitext(unpack(lect.flags))
:done()
:tag("div")
:addClass("nomenclature__language-name")
:wikitext(lect.abbr)
:done()
:done()
local names = {}
local meanings = {}
for i, translation in ipairs(translations) do
names[i] = h.printTranslationName(translation, displayGames)..translation.refs
meanings[i] = translation.meaning
end
end
if utilsString.isEmpty(header) then
names = #names == 1 and names[1] or h.list(names)
header = "Subject"
meanings = #meanings == 1 and meanings[1] or h.list(meanings)
end
nameCell:wikitext(names)
meaningCell:wikitext(meanings)
for key, tab in ipairs(tabs) do
-- When the foreign name is the exact same as the NoA name (see TotK page for example)
local languages = utilsString.split(tab["tabName"], '%s*,%s*')
if #translations == 1 and translations[1].translation == translations[1].term then
--Creating tab contents
nameCell:attr("colspan", 2)
local headerRow = mw.html.create("tr")
meaningCell = nil
:node(mw.html.create("th"):wikitext(header)):done()
for key2, language in ipairs(languages) do
local langText, flag = utilsLanguage.printLanguage(language)
headerRow:node(
mw.html.create("th")
:wikitext(flag .. "<br>" .. langText)
:css("width", 100 / (#languages + 1) .. "%")
)
:done()
end
local content = mw.html.create("table")
:addClass("wikitable")
:css("width", "100%")
:node(headerRow)
--Creating rows
local errs = {}
local translations = translation.fetchTranslationsByGame(game, subjects)
for key2, subject in ipairs(subjects) do
local term, err = Term.fetchTerm(subject, game)
if not term then
utilsError.warn(string.format("Page %s does not store any terms.", utilsMarkup.code(subject)))
errs = utilsTable.concat(errs, err)
else
-- Assume that by default, the file is named according to the term. If term+game cannot uniquely identify the subject, assume the file name matches the page name.
-- Example A: Given subject Wood (Character) and game ST, the filename is inferred to be "ST Wood Model.png". In ST, the term "Wood" uniquely identifies the subject.
-- Example B: Given subject Link (Goron) and game MM, the file name is inferred to be "MM Link (Goron) Model.png. In MM, the term 'Link' refers to two characters and does not uniquely identify the subject.
local fileIdentifier = term
if utilsString.endsWith(subject, ")") and #Term.fetchSubjects(term, game) > 1 then
fileIdentifier = subject
end
local img = utilsMarkup.gameFile(game, fileIdentifier, filetype, {
size = "150x150px"
})
term = Term.printTerm({
page = subject,
game = game,
link = "link",
})
local row = h.printRow(img, term, languages, translations[subject] or {})
content:node(row)
end
end
tab["tabContent"] = tostring(content) .. "<small>Return to [[#top|top]]</small>" .. utilsMarkup.categories(errs)
-- Formatting tab names
for key2, language in ipairs(languages) do
languages[key2] = utilsLanguage.printLanguage(language, true)
end
languages = utilsTable.unique(languages)
tab["tabName"] = table.concat(languages, ", ")
end
end
 
if #tabs == 1 then
row:node(langCell)
return tabs[1]["tabContent"]
row:node(nameCell)
else
if hasMeanings and meaningCell then
return tab2.Main(tabs, 1, "top", #tabs, "", "", "", "left")
row:node(meaningCell)
end
end
end
end


function h.printRow(img, term, languages, translations)
function h.printTranslationName(translationData, displayGames)
local row = mw.html.create("tr")
local result = translationData.translation
:node(mw.html.create("td")
if displayGames then
:addClass("centered")
result = result .. " " .. mw.getCurrentFrame():expandTemplate({
:wikitext(img .. "<br>" .. utilsMarkup.bold(term)))
title = "Exp Game",
args = {table.concat(translationData.games, ", ")}
for _, language in ipairs(languages) do
})
outputs = {}
end
for _, translation in ipairs(translations) do
return result
if translation["language"] == language then
end
table.insert(outputs, translation["translation"])
end
end


local cell = mw.html.create("td"):addClass("centered")
function h.list(items)
if outputs[1] == "N/A" then
local list = mw.html.create("ul"):addClass("plainlist")
cell:css("background-color", "var(--zw-dark-2)")
for i, item in ipairs(items) do
cell:wikitext("")
list:tag("li"):wikitext(item)
else
cell:wikitext(table.concat(outputs, "<br>"))
end
row:node(cell):done()
end
end
return row
return tostring(list)
end
end


return p
return p

Latest revision as of 16:33, 14 April 2024

This is the main module for Template:Nomenclature.


local p = {}
local h = {}

local Franchise = require("Module:Franchise")
local Language = require("Module:Language")
local Term = require("Module:Term")
local utilsCargo = require("Module:UtilsCargo")
local utilsTable = require('Module:UtilsTable')

local DISCORD_URL = require("Module:Constants/url/discord")

-- For creating nomenclature tables
function p.Main(frame)
	local subject = frame.args["term"]
	if subject == nil or subject == "" then
		subject = mw.title.getCurrentTitle().subpageText
	end
	
	local translations = h.fetchTranslations(subject)
	local translations, hasMeanings, displayGames = h.formatData(translations)
	local nomenclatureTable = h.printNomenclatureTable(translations, hasMeanings, displayGames)
	
	return nomenclatureTable
end

function h.fetchTranslations(page)
	local whereClause = utilsCargo.allOf({
    	subject = page
	})
	-- Fetch translations of synonyms
	local term = Term.fetchTerm(page)
	if term and not string.find(page, "%)$") then -- without this ) check, Wood (Character) would also fetch data for BotW Wood
		term = string.gsub(term, "#", "") -- terms with # in them are stored in a version of the page without the #, because MediaWiki. Also Cargo doesn't allow queries with # in them.
		whereClause = whereClause .. " OR " ..utilsCargo.allOf({
	    	term = term
		}, "subject NOT LIKE '%)'") -- without this, requesting "Wood" would also fetch data for Wood (Character)
	end
	local translations = utilsCargo.query("Translations", "game, term, lang, translation, meaning, ref", {
		where = whereClause
	})
	
	return translations
end

function h.formatData(translations)
	local hasMeanings = utilsTable.find(translations, function(translation)
		return translation.meaning == nil and translation.meaning ~= "" and translation.term ~= translation.translation
	end)
	
	-- Determine whether to display Exp Game
	local seenGames = {}
	local gameCount = 0
	local hasRemakes = false
	for i, translation in ipairs(translations) do
		local game = translation.game
		if not seenGames[game] then
			gameCount = gameCount + 1
			seenGames[game] = true
		end
		if Franchise.isRemake(game) or Franchise.hasRemakes(game) then
			hasRemakes = true
		end
	end
	local displayGames = gameCount > 1 or hasRemakes
	
	-- Group translations by language and then by name
	local gameOrderLookup = utilsTable.invert(Franchise.enum())
	translations = utilsTable.sortBy(translations, function(translation)
		return gameOrderLookup[translation.game] or 1000
	end)
	translations = utilsTable.groupBy(translations, "lang")
	translations = utilsTable.mapValues(translations, utilsTable._groupBy("translation"))
	
	-- Creates a list of unique translations grouped by language
	-- For each unique translation, lists which games have that translation, assigns a meaning to it, and creates the refs
	-- If multiple games have different meanings for the same translation, we use the latest game
	local hasMeanings = false
	local translationLists = {}
	for lang, langTranslations in pairs(translations) do
		local translationList = {}
		for translation, translationGames in pairs(langTranslations) do
			local translationListItem = {
				translation = translation,
				games = {},
				meaning = "&nbsp;",
				term = translationGames[1].term,
				refs = "",
			}
			for i, translationGame in ipairs(translationGames) do
				local meaning = translationGame.meaning
				if meaning ~= nil and meaning ~= "" then
					translationListItem.meaning = meaning
					hasMeanings = true
				end
				table.insert(translationListItem.games, translationGame.game)
				translationListItem.refs = translationListItem.refs..h.printRef(translation, translationGame.ref)
			end
			table.insert(translationList, translationListItem)
		end
		-- Sort translations by their earliest appearance
		translationList = utilsTable.sortBy(translationList, function(translation)
			return gameOrderLookup[translation.games[1]]
		end)
		translationLists[lang] = translationList
	end
	return translationLists, hasMeanings, displayGames
end

local refCount = 0
local refNamePrefix = "nomenclature-"
local seenCitations = {} -- to prevent duplicate citations in the ==References== section on the page 
local seenRefs = {} -- to prevent duplicate reference markers in a given Nomenclature table row (e.g. [[Bomb]] Simplified Chinese)
function h.printRef(translation, citation)
	if citation == nil or citation == "" then
		return ""
	end
	seenRefs[translation] = seenRefs[translation] or {}
	local refSeen = seenRefs[translation][citation]
	local citationIndex = seenCitations[citation]
	
	local refContent
	if refSeen then
		 -- Do nothing, the ref is already being shown for this translation due to it being the same in multiple games 
		 -- (happens when the source is a book like E)
		return ""
	elseif citationIndex then
		-- refContent stays nil since we're re-using an existing ref
		refContent = nil
		seenRefs[translation][citation] = true 
	else
		-- create a new ref
		refCount = refCount + 1
		citationIndex = refCount
		refContent = citation
		seenCitations[citation] = citationIndex
		seenRefs[translation][citation] = true 
	end
	
	local frame = mw.getCurrentFrame()
	if frame:getParent().args[1] then
		-- workaround if Nomenclature is used multiple times per page
		local subject = frame.args["term"]
		return frame:extensionTag({
		name = "ref",
		args = { name = refNamePrefix..subject..'-'..citationIndex },
		content = refContent,
	})
	end
	return frame:extensionTag({
		name = "ref",
		args = { name = refNamePrefix..citationIndex },
		content = refContent,
	})
end

function h.printNomenclatureTable(translationsByLang, hasMeanings, displayGames)
	local html = mw.html.create("table")
		:addClass("wikitable")
		:tag("tr")
			:tag("th")
				:addClass("nomenclature__header")
				:attr("colspan", hasMeanings and 3 or 2)
				:wikitext("[[File:ZW Nomenclature Asset.png|20px|link=]] Names in Other Regions [[File:ZW Nomenclature Asset 2.png|link=]]")
				:done()
			:done()
		:done()
	
	local columns = html:tag("tr")
	columns:tag("th"):wikitext("Language")
	columns:tag("th"):wikitext("Names")
	if hasMeanings then
		columns:tag("th"):wikitext("Meanings")
	end
	
	for i, lang in ipairs(Language.enum()) do
		local translations = translationsByLang[lang]
		if translations then
			h.addRow(html, hasMeanings, displayGames, lang, translations)
		end
	end
	
	local footerText = mw.getCurrentFrame():preprocess("<small>This table was generated using [[Data:Translations|translation pages]].<br>To request an addition, please contact a [[Zelda Wiki:Staff|staff member]] with a [[Guidelines:References|reference]].</small>")
	html:tag("tr")
		:tag("th")
		:attr("colspan", "3")
		:wikitext(footerText)
	
	return tostring(html:allDone())		
end

function h.addRow(html, hasMeanings, displayGames, lang, translations)
	local row = html:tag("tr")
	local langCell = mw.html.create("td"):addClass("nomenclature__cell nomenclature__cell--language")
	local nameCell = mw.html.create("td"):addClass("nomenclature__cell nomenclature__cell--name")
	local meaningCell = mw.html.create("td"):addClass("nomenclature__cell nomenclature__cell--meanings")
	
	local lect = Language.getLect(lang)
	langCell:tag("div")
		:addClass("nomenclature__language")
		:tag("div")
			:addClass("nomenclature__language-flags")
			:wikitext(unpack(lect.flags))
			:done()
		:tag("div")
			:addClass("nomenclature__language-name")
			:wikitext(lect.abbr)
			:done()
		:done()
	
	local names = {}
	local meanings = {}
	for i, translation in ipairs(translations) do
		names[i] = h.printTranslationName(translation, displayGames)..translation.refs
		meanings[i] = translation.meaning
	end
	
	names = #names == 1 and names[1] or h.list(names)
	meanings = #meanings == 1 and meanings[1] or h.list(meanings)
	
	nameCell:wikitext(names)
	meaningCell:wikitext(meanings)
	
	-- When the foreign name is the exact same as the NoA name (see TotK page for example)
	if #translations == 1 and translations[1].translation == translations[1].term then
		nameCell:attr("colspan", 2)
		meaningCell = nil
	end

	row:node(langCell)
	row:node(nameCell)
	if hasMeanings and meaningCell then
		row:node(meaningCell)
	end
end

function h.printTranslationName(translationData, displayGames)
	local result = translationData.translation
	if displayGames then
		result = result .. " " .. mw.getCurrentFrame():expandTemplate({
			title = "Exp Game",
			args = {table.concat(translationData.games, ", ")}
		})
	end
	return result
end

function h.list(items)
	local list = mw.html.create("ul"):addClass("plainlist")
	for i, item in ipairs(items) do
		list:tag("li"):wikitext(item)
	end
	return tostring(list)
end

return p