Module:Nomenclature: Difference between revisions

From Zelda Wiki, the Zelda encyclopedia
Jump to navigation Jump to search
(fix bug for merging name and meaning columns when the name is the same as in English)
mNo edit summary
 
(15 intermediate revisions by 5 users not shown)
Line 2: Line 2:
local h = {}
local h = {}


local cargo = mw.ext.cargocs
local Franchise = require("Module:Franchise")
local expgame = require('Module:Exp Game')
local Language = require("Module:Language")
local Franchise = require('Module:Franchise')
local Term = require("Module:Term")
local translation = require('Module:Translation')
local utilsCargo = require("Module:UtilsCargo")
local utilsLanguage = require('Module:UtilsLanguage')
local utilsString = require("Module:UtilsString")
local utilsTable = require('Module:UtilsTable')
local utilsTable = require('Module:UtilsTable')


-- temporary to remove {{Names}}
local DISCORD_URL = require("Module:Constants/url/discord")
function p.HasTranslationsStored(frame)
local term = frame.args["term"]
if utilsString.isEmpty(term) then
term = mw.title.getCurrentTitle().subpageText
end
if #translation.fetchTranslations(term) > 0 then
return true
else
return false
end
end


-- 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
end
local cargoData = translation.fetchTranslations(term)
local displayGames = false
local translations = h.fetchTranslations(subject)
local skipMeanings = true
local translations, hasMeanings, displayGames = h.formatData(translations)
for key, row in ipairs(cargoData) do
local nomenclatureTable = h.printNomenclatureTable(translations, hasMeanings, displayGames)
if cargoData[1]["game"] ~= row["game"] or Franchise.isRemake(row.game) or Franchise.hasRemakes(row.game) then
displayGames = true
return nomenclatureTable
end
if not utilsString.isEmpty(row["meaning"]) then
skipMeanings = false
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
end


--Create an empty table with headers
function h.fetchTranslations(page)
function h.CreateTable(skipMeanings)
local whereClause = utilsCargo.allOf({
--Table structure
    subject = page
local resultTable = mw.html.create("table")
})
:addClass("wikitable"):done()
-- Fetch translations of synonyms
local term = Term.fetchTerm(page)
--Global header
if term and not string.find(page, "%)$") then -- without this ) check, Wood (Character) would also fetch data for BotW Wood
local headerRow = mw.html.create("tr"):done()
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.
local headerContent = mw.html.create("th")
whereClause = whereClause .. " OR " ..utilsCargo.allOf({
:wikitext("[[File:TMC Forest Minish Artwork.png|20px|link=]] Names in Other Regions [[File:TMC Jabber Nut Sprite.png|link=]]")
    term = term
:attr("colspan", "4")
}, "subject NOT LIKE '%)'") -- without this, requesting "Wood" would also fetch data for Wood (Character)
:css{
["font-size"] = "110%",
}:done()
headerRow:node(headerContent)
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
end
local translations = utilsCargo.query("Translations", "game, term, lang, translation, meaning, ref", {
where = whereClause
})
resultTable:node(headerRow)
return translations
return resultTable
end
end


function h.CreateRows(output, cargoData, skipMeanings, displayGames)
function h.formatData(translations)
h.SortTranslations(cargoData)
local hasMeanings = utilsTable.find(translations, function(translation)
for _, row in ipairs(cargoData) do
return translation.meaning == nil and translation.meaning ~= "" and translation.term ~= translation.translation
if not row.skip and row.translation ~= "N/A" then
end)
h.ProcessRow(output, cargoData, row, skipMeanings, displayGames)
-- 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
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"))
return output
-- Creates a list of unique translations grouped by language
end
-- 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
function h.SortTranslations(translations)
local hasMeanings = false
local lookupLang = utilsTable.invert(utilsLanguage.enum())
local translationLists = {}
local lookupGame = utilsTable.invert(Franchise.enumGames())
for lang, langTranslations in pairs(translations) do
table.sort(translations, function (a,b)
local translationList = {}
if (lookupLang[a.language] or 0) == (lookupLang[b.language] or 0) then
for translation, translationGames in pairs(langTranslations) do
return (lookupGame[a.game] or 0) < (lookupGame[b.game] or 0)
local translationListItem = {
else
translation = translation,
return (lookupLang[a.language] or 0) < (lookupLang[b.language] or 0)
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
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
return translationLists, hasMeanings, displayGames
end
end


function h.ProcessRow(output, cargoData, row, skipMeanings, displayGames)
local refCount = 0
local meanings = h.GetMeanings(cargoData, row)
local refNamePrefix = "nomenclature-"
local langText, flag = utilsLanguage.printLanguage(row.language)
local seenCitations = {} -- to prevent duplicate citations in the ==References== section on the page
local tr = output:tag('tr')
local seenRefs = {} -- to prevent duplicate reference markers in a given Nomenclature table row (e.g. [[Bomb]] Simplified Chinese)
:tag("td")
function h.printRef(translation, citation)
:addClass("nomenclature-flag")
if citation == nil or citation == "" then
:wikitext(flag)
return ""
:done()
:tag("td")
:wikitext(langText)
:done()
local sameNameAsEnglish = h.PrintNames(tr, cargoData, row, displayGames)
h.MarkRowsToSkip(cargoData, row)
if not skipMeanings and not sameNameAsEnglish then
h.PrintMeanings(tr, meanings)
end
end
seenRefs[translation] = seenRefs[translation] or {}
local refSeen = seenRefs[translation][citation]
local citationIndex = seenCitations[citation]
end
local refContent
 
if refSeen then
function h.GetMeanings(cargoData, row)
-- Do nothing, the ref is already being shown for this translation due to it being the same in multiple games
local ret = { row.meaning }
-- (happens when the source is a book like E)
for _, row2 in ipairs(cargoData) do
return ""
if h.SameLangDifTranslations(row, row2) then
elseif citationIndex then
ret[#ret+1] = row2.meaning
-- refContent stays nil since we're re-using an existing ref
end
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.PrintNames(tr, cargoData, row, displayGames)
-- workaround if Nomenclature is used multiple times per page
-- name and meaning get merged into one column if the name is the same as the English name
local subject = frame.args["term"]
-- See The Legend of Zelda: Tears of the Kingdom, for example
return frame:extensionTag({
local names, sameNameAsEnglish = h.GetNamesAndTheirGames(cargoData, row, displayGames)
name = "ref",
local td = tr:tag('td')
args = { name = refNamePrefix..subject..'-'..citationIndex },
:wikitext(table.concat(names, '<br>'))
content = refContent,
if sameNameAsEnglish then
})
td:attr("colspan", 2)
end
end
return sameNameAsEnglish
return frame:extensionTag({
name = "ref",
args = { name = refNamePrefix..citationIndex },
content = refContent,
})
end
end


function h.GetNamesAndTheirGames(cargoData, row, displayGames)
function h.printNomenclatureTable(translationsByLang, hasMeanings, displayGames)
local sameNameAsEnglish = true
local html = mw.html.create("table")
local nameAndGames, name = h.GetOneNameAndGames(cargoData, row, displayGames)
:addClass("wikitable")
local ret = {nameAndGames}
:tag("tr")
if name ~= row.term then
:tag("th")
sameNameAsEnglish = false
: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
end
for _, row2 in ipairs(cargoData) do
if h.SameLangDifTranslations(row, row2) then
for i, lang in ipairs(Language.enum()) do
games = h.GamesWithSameTranslation(row2, cargoData)
local translations = translationsByLang[lang]
local nameAndGames, name = h.GetOneNameAndGames(cargoData, row2, displayGames)
if translations then
ret[#ret+1] = name
h.addRow(html, hasMeanings, displayGames, lang, translations)
if name ~= row.term then
sameNameAsEnglish = false
end
end
end
end
end
return ret, sameNameAsEnglish
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 h.GetOneNameAndGames(cargoData, row, displayGames)
function h.addRow(html, hasMeanings, displayGames, lang, translations)
local games = h.GamesWithSameTranslation(row, cargoData)
local row = html:tag("tr")
local result = row.translation
local langCell = mw.html.create("td"):addClass("nomenclature__cell nomenclature__cell--language")
if displayGames == true then
local nameCell = mw.html.create("td"):addClass("nomenclature__cell nomenclature__cell--name")
result = result .. " " .. expgame.printGames(games)
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
end
local refs = h.RefsWithSameTranslation(row, cargoData)
names = #names == 1 and names[1] or h.list(names)
for key, ref in ipairs(refs) do
meanings = #meanings == 1 and meanings[1] or h.list(meanings)
if not utilsString.isEmpty(ref) then
result = result .. mw.getCurrentFrame():extensionTag("ref", ref)
nameCell:wikitext(names)
end
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
end
return result, row.translation
end


function h.GamesWithSameTranslation(row, cargoData)
row:node(langCell)
local ret = {}
row:node(nameCell)
for _, row2 in ipairs(cargoData) do
if hasMeanings and meaningCell then
if h.SameLangSameTranslation(row, row2) then
row:node(meaningCell)
ret[#ret+1] = row2.game
end
end
end
return ret
end
end


function h.RefsWithSameTranslation(row, cargoData)
function h.printTranslationName(translationData, displayGames)
local ret = {}
local result = translationData.translation
for _, row2 in ipairs(cargoData) do
if displayGames then
if h.SameLangSameTranslation(row, row2) then
result = result .. " " .. mw.getCurrentFrame():expandTemplate({
ret[#ret+1] = row2.reference
title = "Exp Game",
end
args = {table.concat(translationData.games, ", ")}
end
})
return ret
end
 
function h.SameLangSameTranslation(row1, row2)
return row1.language == row2.language and row1.translation == row2.translation
end
 
function h.SameLangDifTranslations(row1, row2)
return row1.language == row2.language and row1.translation ~= row2.translation
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 result
end
end


function h.ProcessMeanings(meanings)
function h.list(items)
local ret = {}
local list = mw.html.create("ul"):addClass("plainlist")
for k, v in pairs(meanings) do
for i, item in ipairs(items) do
if utilsString.isEmpty(v) then
list:tag("li"):wikitext(item)
ret[#ret+1] = '&nbsp;'
else
ret[#ret+1] = v
end
end
end
return ret
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