Module:Nomenclature

From Zelda Wiki, the Zelda encyclopedia
Jump to navigation Jump to search

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 Util = {
	cargo = {
		all = require("Module:Util/cargo/all"),
		query = require("Module:Util/cargo/query"),
	},
	tables = {
		find = require("Module:Util/tables/find"),
		groupBy = require("Module:Util/tables/groupBy"),
		_groupBy = require("Module:Util/tables/_groupBy"),
		invert = require("Module:Util/tables/invert"),
		keys = require("Module:Util/tables/keys"),
		mapValues = require("Module:Util/tables/mapValues"),
		sortBy = require("Module:Util/tables/sortBy"),
	},
}
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 = Util.cargo.all({
    	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 " ..Util.cargo.all({
	    	term = term
		}, "subject NOT LIKE '%)'") -- without this, requesting "Wood" would also fetch data for Wood (Character)
	end
	whereClause = Util.cargo.all(whereClause, "translation != 'N/A'")
	local translations = Util.cargo.query("Translations", "game, term, lang, translation, meaning, ref", {
		where = whereClause
	})
	
	return translations
end

function h.formatData(translations)
	local hasMeanings = Util.tables.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
		local superseder = Franchise.superseder(game)
		local hasRemakes = superseder and superseder ~= ""
		if Franchise.isRemake(game) or hasRemakes then
			hasRemakes = true
		end
	end
	local displayGames = gameCount > 1 or hasRemakes
	
	-- Group translations by language and then by name
	local games = Util.tables.keys(seenGames)
	games = Franchise.sort(games)
	local gameOrderLookup = Util.tables.invert(games)
	translations = Util.tables.sortBy(translations, function(translation)
		return gameOrderLookup[translation.game] or 1000
	end)
	translations = Util.tables.groupBy(translations, "lang")
	translations = Util.tables.mapValues(translations, Util.tables._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 = " ",
				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 = Util.tables.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