Module:Foreign names
From the Super Mario Wiki, the Mario encyclopedia
Jump to navigationJump to search
This module creates the {{foreign names}} table. It uses the languages defined here.
-- Module:Foreign names
-- Lua replacement for Template:Foreign names
local p = {}
-- Language definitions loaded from subpage (prefix, name, BCP 47 tag, dir, latin, variant)
local languages = mw.loadJsonData("Module:Foreign names/languages.json")
-- Strip HTML tags (used to clean display names for category sort keys)
local function stripTags(s)
return (s:gsub("<[^>]+>", ""))
end
-- Return the parameter key for entry index idx under a given prefix.
-- Index 1 -> bare prefix ("Jpn")
-- Index 2+ -> prefix .. index ("Jpn2", "Jpn3", ...)
local function paramKey(prefix, idx)
if idx == 1 then return prefix end
return prefix .. idx
end
-- Count how many consecutive value entries exist for a prefix.
local function countEntries(args, prefix)
if not args[prefix] or args[prefix] == "" then return 0 end
local n = 1
while args[prefix .. (n + 1)] and args[prefix .. (n + 1)] ~= "" do
n = n + 1
end
return n
end
-- Resolve a parameter for entry idx, walking back through "^" inherit
-- markers to the nearest real value. suffix = "R" | "RM" | "RC" | "RH" |
-- "M" | "N" — used for romanisations as well as the Meaning/Note columns.
local function resolveR(args, prefix, idx, suffix)
local val = args[paramKey(prefix, idx) .. suffix] or ""
-- Walk back through "^" inherit markers
local i = idx
while val == "^" and i > 1 do
i = i - 1
val = args[paramKey(prefix, i) .. suffix] or ""
end
if val == "^" then val = "" end
return val
end
-- Compute rowspan for the Meaning column starting at entry idx.
-- Consecutive entries with "^" or identical M values are merged.
local function meaningRowspan(args, prefix, idx, total)
local baseM = args[paramKey(prefix, idx) .. "M"] or ""
local span = 1
local j = idx + 1
while j <= total do
local mj = args[paramKey(prefix, j) .. "M"] or ""
-- Merge on an explicit "^" inherit, or on matching non-empty values.
-- Two entries that both simply omit M ("" == "") must NOT merge.
if mj == "^" or (mj == baseM and mj ~= "") then
span = span + 1
j = j + 1
else
break
end
end
return span
end
-- Compute rowspan for the Note column starting at entry idx.
local function noteRowspan(args, prefix, idx, total)
local baseN = args[paramKey(prefix, idx) .. "N"] or ""
local span = 1
local j = idx + 1
while j <= total do
local nj = args[paramKey(prefix, j) .. "N"] or ""
-- Merge on an explicit "^" inherit, or on matching non-empty values.
-- Two entries that both simply omit N ("" == "") must NOT merge.
if nj == "^" or (nj == baseN and nj ~= "") then
span = span + 1
j = j + 1
else
break
end
end
return span
end
-- Test whether a string contains non-ASCII characters, indicating a non-Latin
-- script that needs romanisation. Only called for languages without the latin
-- flag, so we don't need to worry about accented Latin characters.
-- Certain non-ASCII ranges are stripped before checking:
-- U+FF01–U+FF5E Fullwidth ASCII variants (e.g. A, 3)
-- U+2000–U+2BFF Symbols and punctuation (e.g. ☆, →, …)
-- U+3000–U+303F CJK punctuation (e.g. 。、「」)
local function needsRomanisation(s)
local normalized = s
:gsub("\239\188[\129-\191]", "x") -- U+FF01–U+FF3F
:gsub("\239\189[\128-\158]", "x") -- U+FF40–U+FF5E
:gsub("\226[\128-\175][\128-\191]", "x") -- U+2000–U+2BFF
:gsub("\227\128[\128-\191]", "x") -- U+3000–U+303F
return normalized:find("[\128-\255]") ~= nil
end
-- Wrap text in a nowrap span (disabled when wrap mode is active).
local wrapMode = false
local function nw(text)
if wrapMode then return text end
return '<span class="nowrap">' .. text .. '</span>'
end
-- Wrap text in a lang-tagged span.
local function langSpan(tag, text)
return '<span lang="' .. tag .. '">' .. text .. '</span>'
end
-- ---------------------------------------------------------------------------
-- Row builder
-- ---------------------------------------------------------------------------
local function buildLangRows(out, args, prefix, display, langTag, dir,
variant, isContent, nocitMode, isLatin)
local total = countEntries(args, prefix)
if total == 0 then return end
local displayClean = stripTags(display)
-- Countdown trackers so we only emit rowspan cells at the right entries.
local mSpanLeft = 0
local nSpanLeft = 0
for idx = 1, total do
local vKey = paramKey(prefix, idx) -- e.g. "Jpn", "Jpn2"
local value = args[vKey] or ""
-- ----------------------------------------------------------------
-- Name cell content
-- ----------------------------------------------------------------
local nameParts = {}
-- Determine the lang subtag (ChiT may need -HK for Cantonese-only).
-- Resolve each romanisation through the "^" inheritance chain first —
-- checking the raw param would treat an unresolved "^" as "present"
-- even when it ultimately inherits an empty value.
local ltag = langTag
if variant == "chineseT" then
local hasRM = resolveR(args, prefix, idx, "RM") ~= ""
local hasR = resolveR(args, prefix, idx, "R") ~= ""
local hasRC = resolveR(args, prefix, idx, "RC") ~= ""
local hasRH = resolveR(args, prefix, idx, "RH") ~= ""
if hasRC and not hasRM and not hasR and not hasRH then
ltag = "zh-Hant-HK"
end
end
table.insert(nameParts, nw(langSpan(ltag, value)))
if variant == "chinese" or variant == "chineseT" then
-- Chinese: separate Mandarin (RM), Cantonese (RC), and Hokkien (RH) romanisations
local rm = resolveR(args, prefix, idx, "RM")
if rm ~= "" then
table.insert(nameParts, "<br />" .. nw("''" .. rm .. "''") .. " <small>(Mandarin)</small>")
end
local rc = resolveR(args, prefix, idx, "RC")
if rc ~= "" then
table.insert(nameParts, "<br />" .. nw("''" .. rc .. "''") .. " <small>(Cantonese)</small>")
end
local rh = resolveR(args, prefix, idx, "RH")
if rh ~= "" then
table.insert(nameParts, "<br />" .. nw("''" .. rh .. "''") .. " <small>(Hokkien)</small>")
end
local r = resolveR(args, prefix, idx, "R")
if r ~= "" then
table.insert(nameParts, "<br />" .. nw("''" .. r .. "''"))
elseif rm == "" and rc == "" and rh == "" then
if isContent and not isLatin and needsRomanisation(value) then
table.insert(nameParts, "[[Category:Articles with missing foreign name romanizations|" .. displayClean .. "]]")
end
end
else
local r = resolveR(args, prefix, idx, "R")
if r ~= "" then
table.insert(nameParts, "<br />" .. nw("''" .. r .. "''"))
else
if isContent and not isLatin and needsRomanisation(value) then
table.insert(nameParts, "[[Category:Articles with missing foreign name romanizations|" .. displayClean .. "]]")
end
end
end
local nameCell = table.concat(nameParts)
-- ----------------------------------------------------------------
-- Meaning cell (emitted only when a new rowspan begins)
-- ----------------------------------------------------------------
local mCellContent = nil
local mCellSpan = nil
if mSpanLeft <= 0 then
local mspan = meaningRowspan(args, prefix, idx, total)
mSpanLeft = mspan
mCellSpan = mspan
local resolvedM = resolveR(args, prefix, idx, "M")
if resolvedM == "" then
local cat = isContent
and ("[[Category:Articles with unknown foreign name meanings|" .. displayClean .. "]]")
or ""
mCellContent = '<span class="foreign-meaning">?</span>' .. cat
elseif resolvedM == "-" then
mCellContent = '<span class="no-foreign-meaning">-</span>'
else
mCellContent = '<span class="foreign-meaning">' .. resolvedM .. '</span>'
end
end
mSpanLeft = mSpanLeft - 1
-- ----------------------------------------------------------------
-- Note cell (emitted only when a new rowspan begins)
-- ----------------------------------------------------------------
local nCellContent = nil
local nCellSpan = nil
if nSpanLeft <= 0 then
local nspan = noteRowspan(args, prefix, idx, total)
nSpanLeft = nspan
nCellSpan = nspan
local resolvedN = resolveR(args, prefix, idx, "N")
if resolvedN ~= "" then
nCellContent = '<span class="foreign-note"><small>' .. resolvedN .. '</small></span>'
else
nCellContent = '<span class="no-foreign-note"></span>'
end
end
nSpanLeft = nSpanLeft - 1
-- ----------------------------------------------------------------
-- Ref cell
-- ----------------------------------------------------------------
local citation = args[vKey .. "C"] or ""
local refContent
if citation ~= "" then
refContent = '<span class="foreign-ref">' .. nw(citation) .. '</span>'
elseif nocitMode then
refContent = '<span class="no-foreign-ref"></span>'
else
-- nocitMode is only false when isContent is true, so this is
-- always a content-namespace page missing a citation.
local cat = '[[Category:Articles with unsourced ' .. displayClean .. ' names]]'
refContent = '<sup><abbr title="Citation needed">[?]</abbr></sup>' .. cat
end
-- ----------------------------------------------------------------
-- Assemble the table row as raw HTML tags rather than wikitext
-- "|"/"||" table syntax. Cell content here comes from user-supplied
-- template parameters (names, meanings, notes, citations) and may
-- legitimately contain a literal "|" (e.g. a [[Page|Display]] link).
-- Wikitext pipe-delimited cells would misread that as a new cell;
-- HTML <td> tags have no such delimiter, so no content ever needs
-- escaping. Wikitext markup inside each cell (links, italics,
-- categories) still parses normally.
-- ----------------------------------------------------------------
local cells = {}
-- Language label cell: only on first entry, spans all rows
if idx == 1 then
if total > 1 then
table.insert(cells, '<td rowspan="' .. total .. '">' .. nw(display) .. '</td>')
else
table.insert(cells, '<td>' .. nw(display) .. '</td>')
end
end
-- Name cell (has a dir= attribute)
table.insert(cells, '<td dir="' .. dir .. '">' .. nameCell .. '</td>')
-- Meaning cell
if mCellContent ~= nil then
if mCellSpan > 1 then
table.insert(cells, '<td rowspan="' .. mCellSpan .. '">' .. mCellContent .. '</td>')
else
table.insert(cells, '<td>' .. mCellContent .. '</td>')
end
end
-- Note cell
if nCellContent ~= nil then
if nCellSpan > 1 then
table.insert(cells, '<td rowspan="' .. nCellSpan .. '">' .. nCellContent .. '</td>')
else
table.insert(cells, '<td>' .. nCellContent .. '</td>')
end
end
-- Ref cell
table.insert(cells, '<td>' .. refContent .. '</td>')
table.insert(out, '<tr>' .. table.concat(cells) .. '</tr>')
end
end
-- ---------------------------------------------------------------------------
-- Main entry point
-- ---------------------------------------------------------------------------
function p.main(frame)
-- Support both transclusion (parent frame) and direct invocation
local args
if frame:getParent() then
args = frame:getParent().args
else
args = frame.args
end
local title = mw.title.getCurrentTitle()
local contentNamespaces = { [0] = true, [102] = true, [106] = true }
local isContent = contentNamespaces[title.namespace] == true
local nocitMode = (args["citations"] or "") ~= "" or not isContent
wrapMode = (args["wrap"] or "") ~= ""
local out = {}
table.insert(out, '<table id="foreign-names" class="wikitable">')
table.insert(out, '<tr><th>Language</th><th>Name</th><th>Meaning</th><th>Note(s)</th>'
.. '<th><abbr title="Reference(s)">Ref.</abbr></th></tr>')
for _, langDef in ipairs(languages) do
local prefix = langDef.prefix
local display = langDef.name
local tag = langDef.tag
local dir = langDef.dir or "ltr"
local isLatin = langDef.latin ~= false
local variant = langDef.variant
buildLangRows(out, args, prefix, display, tag, dir,
variant, isContent, nocitMode, isLatin)
end
table.insert(out, '</table>')
return table.concat(out, "\n")
end
return p