Module:names
Documentation for this module may be created at Module:names/doc
local m_languages = require("Module:languages")
local m_links = require("Module:links")
local m_utilities = require("Module:utilities")
local m_table = require("Module:table")
local export = {}
local enlang = m_languages.getByCode("en")
local rfind = mw.ustring.find
local rsubn = mw.ustring.gsub
local rsplit = mw.text.split
local force_cat = false -- for testing
--[=[
FIXME:
1. from=the Bible (DONE)
2. origin=18th century [DONE]
3. popular= (DONE)
4. varoftype= (DONE)
5. eqtype= [DONE]
6. dimoftype= [DONE]
7. from=de:Elisabeth (same language) (DONE)
8. blendof=, blendof2= [DONE]
9. varform, dimform [DONE]
10. from=English < Latin [DONE]
11. usage=rare -> categorize as rare?
12. dimeq= (also vareq=?) [DONE]
13. fromtype= [DONE]
14. <tr:...> and similar params [DONE]
]=]
-- version of rsubn() that discards all but the first return value
local function rsub(term, foo, bar)
local retval = rsubn(term, foo, bar)
return retval
end
-- Used in category code
export.personal_name_types = {
"surnames", "male surnames", "female surnames", "common-gender surnames",
"patronymics", "matronymics",
"given names", "male given names", "female given names", "unisex given names",
"diminutives of male given names", "diminutives of female given names",
"diminutives of unisex given names",
"augmentatives of male given names", "augmentatives of female given names",
"augmentatives of unisex given names"
}
local translit_name_type_list = {
"surname", "male given name", "female given name", "unisex given name",
"patronymic"
}
local translit_name_types = m_table.listToSet(translit_name_type_list)
local param_mods = {"t", "alt", "tr", "ts", "pos", "lit", "id", "sc", "g", "q", "eq"}
local param_mod_set = m_table.listToSet(param_mods)
local function track(page)
require("Module:debug").track("names/" .. page)
end
--[=[
Parse a term and associated properties. This works with parameters of the form 'Karlheinz' or
'Kunigunde<q:medieval, now rare>' or 'non:Óláfr' or 'ru:Фру́нзе<tr:Frúnzɛ><q:rare>' where the modifying properties
are contained in <...> specifications after the term. `term` is the full parameter value including any angle brackets
and colons; `pname` is the name of the parameter that this value comes from, for error purposes; `deflang` is a
language object used in the return value when the language isn't specified (e.g. in the examples 'Karlheinz' and
'Kunigunde<q:medieval, now rare>' above); `allow_explicit_lang` indicates whether the language can be explicitly given
(e.g. in the examples 'non:Óláfr' or 'ru:Фру́нзе<tr:Frúnzɛ><q:rare>' above).
Normally the return value is an object with properties '.term' (a terminfo object that can be passed to full_link() in
[[Module:links]]) and '.q' (a qualifier). However, if `allow_multiple_terms` is given, multiple comma-separated names
can be given in `term`, and the return value is a list of objects of the form described just above.
]=]
local function parse_term_with_annotations(term, pname, deflang, allow_explicit_lang, allow_multiple_terms)
local function parse_single_run_with_annotations(run)
local function parse_err(msg)
error(msg .. ": " .. pname .. "= " .. table.concat(run))
end
if #run == 1 and run[1] == "" then
error("Blank form for param '" .. pname .. "' not allowed")
end
local termobj = {term = {}}
local lang, form = run[1]:match("^([^%[%]]-):(.*)$")
if lang and lang ~= "w" then
if not allow_explicit_lang then
parse_err("Explicit language '" .. lang .. "' not allowed for this parameter")
end
termobj.term.lang = m_languages.getByCode(lang, pname, "allow etym lang")
termobj.term.term = form
else
termobj.term.lang = deflang
termobj.term.term = run[1]
end
for i = 2, #run - 1, 2 do
if run[i + 1] ~= "" then
parse_err("Extraneous text '" .. run[i + 1] .. "' after modifier")
end
local modtext = run[i]:match("^<(.*)>$")
if not modtext then
parse_err("Internal error: Modifier '" .. modtext .. "' isn't surrounded by angle brackets")
end
local prefix, arg = modtext:match("^([a-z]+):(.*)$")
if not prefix then
parse_err("Modifier " .. run[i] .. " lacks a prefix, should begin with one of '" ..
table.concat(param_mods, ":', '") .. ":'")
end
if param_mod_set[prefix] then
local obj_to_set
if prefix == "q" or prefix == "eq" then
obj_to_set = termobj
else
obj_to_set = termobj.term
end
if obj_to_set[prefix] then
parse_err("Modifier '" .. prefix .. "' occurs twice, second occurrence " .. run[i])
end
if prefix == "t" then
termobj.term.gloss = arg
elseif prefix == "g" then
termobj.term.genders = rsplit(arg, ",")
elseif prefix == "sc" then
termobj.term.sc = require("Module:scripts").getByCode(arg, pname)
elseif prefix == "eq" then
termobj.eq = parse_term_with_annotations(arg, pname .. ".eq", enlang, false, "allow multiple terms")
else
obj_to_set[prefix] = arg
end
else
parse_err("Unrecognized prefix '" .. prefix .. "' in modifier " .. run[i])
end
end
return termobj
end
local iut = require("Module:inflection utilities")
local run = iut.parse_balanced_segment_run(term, "<", ">")
if allow_multiple_terms then
local comma_separated_runs = iut.split_alternating_runs(run, "%s*,%s*")
local termobjs = {}
for _, comma_separated_run in ipairs(comma_separated_runs) do
table.insert(termobjs, parse_single_run_with_annotations(comma_separated_run))
end
return termobjs
else
return parse_single_run_with_annotations(run)
end
end
--[=[
Link a single term. If `do_language_link` is given and a given term's language is English, the link will be constructed
using language_link() in [[Module:links]]; otherwise, with full_link(). Each term in `terms` is an object as returned
by parse_term_with_annotations(), i.e. it contains fields '.term' (a terminfo structure suitable for passing to
full_link() or language_link()), optional '.q' (a qualifier) and optional '.eq' (a list of objects of the same form as
`termobj`).
]=]
local function link_one_term(termobj, do_language_link)
local link
if do_language_link and termobj.term.lang:getCode() == "en" then
link = m_links.language_link(termobj.term, true)
else
link = m_links.full_link(termobj.term, nil, true)
end
if termobj.q then
link = require("Module:qualifier").format_qualifier(termobj.q) .. " " .. link
end
if termobj.eq then
local eqtext = {}
for _, eqobj in ipairs(termobj.eq) do
table.insert(eqtext, link_one_term(eqobj, true))
end
link = link .. " [=" .. m_table.serialCommaJoin(eqtext, {conj = "or"}) .. "]"
end
return link
end
--[=[
Link the terms in `terms`, and join them using the conjunction in `conj` (defaulting to "or"). Joining is done using
serialCommaJoin() in [[Module:table]], so that e.g. two terms are joined as "TERM or TERM" while three terms are joined
as "TERM, TERM or TERM" with special CSS spans before the final "or" to allow an "Oxford comma" to appear if configured
appropriately. (However, if `conj` is the special value ", ", joining is done directly using that value.)
If `include_langname` is given, the language of the first term will be prepended to the joined terms. If
`do_language_link` is given and a given term's language is English, the link will be constructed using language_link()
in [[Module:links]]; otherwise, with full_link(). Each term in `terms` is an object as returned by
parse_term_with_annotations(), i.e. it contains fields '.term' (a terminfo structure suitable for passing to full_link()
or language_link()), optional '.q' (a qualifier) and optional '.eq' (a list of objects of the same form as in `terms`).
]=]
local function join_terms(terms, include_langname, do_language_link, conj)
local links = {}
local langnametext
for _, termobj in ipairs(terms) do
if include_langname and not langnametext then
langnametext = termobj.term.lang:getCanonicalName() .. " "
end
table.insert(links, link_one_term(termobj, do_language_link))
end
local joined_terms
if conj == ", " then
joined_terms = table.concat(links, conj)
else
joined_terms = m_table.serialCommaJoin(links, {conj = conj or "or"})
end
return (langnametext or "") .. joined_terms
end
--[=[
Gather the parameters for multiple names and link each name using full_link() (for foreign names) or language_link()
(for English names), joining the names using serialCommaJoin() in [[Module:table]] with the conjunction `conj`
(defaulting to "or"). (However, if `conj` is the special value ", ", joining is done directly using that value.)
This can be used, for example, to fetch and join all the masculine equivalent names for a feminine given name. Each
name is specified using parameters beginning with `pname` in `args`, e.g. "m", "m2", "m3", etc. `lang` is a language
object specifying the language of the names (defaulting to English), for use in linking them. If `allow_explicit_lang`
is given, the language of the terms can be specified explicitly by prefixing a term with a language code, e.g.
'sv:Björn' or 'la:[[Nicolaus|Nīcolāī]]'. This function assumes that the parameters have already been parsed by
[[Module:parameters]] and gathered into lists, so that e.g. all "mN" parameters are in a list in args["m"].
]=]
local function join_names(lang, args, pname, conj, allow_explicit_lang)
local termobjs = {}
local do_language_link = false
if not lang then
lang = enlang
do_language_link = true
end
for i, term in ipairs(args[pname]) do
table.insert(termobjs, parse_term_with_annotations(term, pname .. (i == 1 and "" or i), lang, allow_explicit_lang))
end
return join_terms(termobjs, nil, do_language_link, conj), #termobjs
end
local function get_eqtext(args)
local eqsegs = {}
local lastlang = nil
local last_eqseg = {}
for i, term in ipairs(args.eq) do
local termobj = parse_term_with_annotations(term, "eq" .. (i == 1 and "" or i), enlang, "allow explicit lang")
local termlang = termobj.term.lang:getCode()
if lastlang and lastlang ~= termlang then
if #last_eqseg > 0 then
table.insert(eqsegs, last_eqseg)
end
last_eqseg = {}
end
lastlang = termlang
table.insert(last_eqseg, termobj)
end
if #last_eqseg > 0 then
table.insert(eqsegs, last_eqseg)
end
local eqtextsegs = {}
for _, eqseg in ipairs(eqsegs) do
table.insert(eqtextsegs, join_terms(eqseg, "include langname"))
end
return m_table.serialCommaJoin(eqtextsegs, {conj = "or"})
end
local function get_fromtext(lang, args)
local catparts = {}
local fromsegs = {}
local i = 1
local function parse_from(from)
local unrecognized = false
local prefix, suffix
if from == "surnames" or from == "given names" or from == "nicknames" or from == "place names" or from == "common nouns" then
prefix = "transferred from the "
suffix = from:gsub("s$", "")
table.insert(catparts, from)
elseif from == "patronymics" or from == "matronymics" or from == "coinages" then
prefix = "originating "
suffix = "as a " .. from:gsub("s$", "")
table.insert(catparts, from)
elseif from == "occupations" or from == "ethnonyms" then
prefix = "originating "
suffix = "as an " .. from:gsub("s$", "")
table.insert(catparts, from)
elseif from == "the Bible" then
prefix = "originating "
suffix = "from the Bible"
table.insert(catparts, from)
else
prefix = "from "
if from:find(":") then
local termobj = parse_term_with_annotations(from, "from" .. (i == 1 and "" or i), lang, "allow explicit lang")
local fromlangname = ""
if termobj.term.lang:getCode() ~= lang:getCode() then
-- If name is derived from another name in the same language, don't include lang name after text "from "
-- or create a category like "German male given names derived from German".
local canonical_name = termobj.term.lang:getCanonicalName()
fromlangname = canonical_name .. " "
table.insert(catparts, canonical_name)
end
suffix = fromlangname .. link_one_term(termobj)
elseif from:find(" languages$") then
local family = from:match("^(.*) languages$")
if require("Module:families").getByCanonicalName(family) then
table.insert(catparts, from)
else
unrecognized = true
end
suffix = "the " .. from
else
if m_languages.getByCanonicalName(from, nil, "allow etym") then
table.insert(catparts, from)
else
unrecognized = true
end
suffix = from
end
end
if unrecognized then
track("unrecognized from")
track("unrecognized from/" .. from)
end
return prefix, suffix
end
local last_fromseg = nil
while args.from[i] do
local rawfrom = args.from[i]
local froms = rsplit(rawfrom, "%s+<%s+")
if #froms == 1 then
local prefix, suffix = parse_from(froms[1])
if last_fromseg and (last_fromseg.has_multiple_froms or last_fromseg.prefix ~= prefix) then
table.insert(fromsegs, last_fromseg)
last_fromseg = nil
end
if not last_fromseg then
last_fromseg = {prefix = prefix, suffixes = {}}
end
table.insert(last_fromseg.suffixes, suffix)
else
if last_fromseg then
table.insert(fromsegs, last_fromseg)
last_fromseg = nil
end
local first_suffixpart = ""
local rest_suffixparts = {}
for j, from in ipairs(froms) do
local prefix, suffix = parse_from(from)
if j == 1 then
first_suffixpart = prefix .. suffix
else
table.insert(rest_suffixparts, prefix .. suffix)
end
end
local full_suffix = first_suffixpart .. " [in turn " .. table.concat(rest_suffixparts, ", in turn ") .. "]"
last_fromseg = {prefix = "", has_multiple_froms = true, suffixes = {full_suffix}}
end
i = i + 1
end
table.insert(fromsegs, last_fromseg)
local fromtextsegs = {}
for _, fromseg in ipairs(fromsegs) do
table.insert(fromtextsegs, fromseg.prefix .. m_table.serialCommaJoin(fromseg.suffixes, {conj = "or"}))
end
return m_table.serialCommaJoin(fromtextsegs, {conj = "or"}), catparts
end
-- The entry point for {{given name}}.
function export.given_name(frame)
local parent_args = frame:getParent().args
local compat = parent_args.lang
local offset = compat and 0 or 1
local lang_index = compat and "lang" or 1
local params = {
[lang_index] = { required = true, default = "und" },
["gender"] = { default = "unknown-gender" },
[1 + offset] = { alias_of = "gender", default = "unknown-gender" },
-- second gender
["or"] = {},
["usage"] = {},
["origin"] = {},
["popular"] = {},
["populartype"] = {},
["meaning"] = { list = true },
["meaningtype"] = {},
["q"] = {},
-- initial article: A or An
["A"] = {},
["sort"] = {},
["from"] = { list = true },
[2 + offset] = { alias_of = "from", list = true },
["fromtype"] = {},
["xlit"] = { list = true },
["eq"] = { list = true },
["eqtype"] = {},
["varof"] = { list = true },
["varoftype"] = {},
["var"] = { alias_of = "varof", list = true },
["vartype"] = { alias_of = "varoftype" },
["varform"] = { list = true },
["dimof"] = { list = true },
["dimoftype"] = {},
["dim"] = { alias_of = "dimof", list = true },
["dimtype"] = { alias_of = "dimoftype" },
["diminutive"] = { alias_of = "dimof", list = true },
["diminutivetype"] = { alias_of = "dimoftype" },
["dimform"] = { list = true },
["augof"] = { list = true },
["augoftype"] = {},
["aug"] = { alias_of = "augof", list = true },
["augtype"] = { alias_of = "augoftype" },
["augmentative"] = { alias_of = "augof", list = true },
["augmentativetype"] = { alias_of = "augoftype" },
["augform"] = { list = true },
["blend"] = { list = true },
["blendtype"] = {},
["m"] = { list = true },
["mtype"] = {},
["f"] = { list = true },
["ftype"] = {},
}
local args = require("Module:parameters").process(parent_args, params)
local textsegs = {}
local langcode = args[lang_index]
local lang = m_languages.getByCode(langcode, lang_index)
local function fetch_typetext(param)
return args[param] and args[param] .. " " or ""
end
local dimoftext, numdims = join_names(lang, args, "dimof")
local augoftext, numaugs = join_names(lang, args, "augof")
local xlittext = join_names(nil, args, "xlit")
local blendtext = join_names(lang, args, "blend", "and")
local varoftext = join_names(lang, args, "varof")
local mtext = join_names(lang, args, "m")
local ftext = join_names(lang, args, "f")
local varformtext, numvarforms = join_names(lang, args, "varform", ", ")
local dimformtext, numdimforms = join_names(lang, args, "dimform", ", ")
local augformtext, numaugforms = join_names(lang, args, "augform", ", ")
local meaningsegs = {}
for _, meaning in ipairs(args.meaning) do
table.insert(meaningsegs, '“' .. meaning .. '”')
end
local meaningtext = m_table.serialCommaJoin(meaningsegs, {conj = "or"})
local eqtext = get_eqtext(args)
table.insert(textsegs, "<span class='use-with-mention'>")
local dimtype = args.dimtype
local augtype = args.augtype
local article = args.A
local need_an = false
if not article then
if numdims > 0 then
need_an = dimtype and rfind(dimtype, "^[aeiouAEIOU]")
elseif numaugs > 0 then
if augtype then
need_an = rfind(augtype, "^[aeiouAEIOU]")
else
need_an = true -- "augmentative" needs an article
end
else
need_an = args.gender == "unknown-gender"
end
if langcode == "en" then
article = need_an and "An" or "A"
else
article = need_an and "an" or "a"
end
end
table.insert(textsegs, article .. " ")
if numdims > 0 then
table.insert(textsegs,
(dimtype and dimtype .. " " or "") ..
"[[diminutive]]" ..
(xlittext ~= "" and ", " .. xlittext .. "," or "") ..
" of the ")
elseif numaugs > 0 then
table.insert(textsegs,
(augtype and augtype .. " " or "") ..
"[[augmentative]]" ..
(xlittext ~= "" and ", " .. xlittext .. "," or "") ..
" of the ")
end
local genders = {}
table.insert(genders, args.gender)
table.insert(genders, args["or"])
table.insert(textsegs, table.concat(genders, " or ") .. " ")
table.insert(textsegs, (numdims > 1 or numaugs > 1) and "[[given name|given names]]" or
"[[given name]]")
local need_comma = false
if numdims > 0 then
table.insert(textsegs, " " .. dimoftext)
need_comma = true
elseif numaugs > 0 then
table.insert(textsegs, " " .. augoftext)
need_comma = true
elseif xlittext ~= "" then
table.insert(textsegs, ", " .. xlittext)
need_comma = true
end
local from_catparts = {}
if #args.from > 0 then
if need_comma then
table.insert(textsegs, ",")
end
need_comma = true
table.insert(textsegs, " " .. fetch_typetext("fromtype"))
local textseg, this_catparts = get_fromtext(lang, args)
for _, catpart in ipairs(this_catparts) do
m_table.insertIfNot(from_catparts, catpart)
end
table.insert(textsegs, textseg)
end
if meaningtext ~= "" then
if need_comma then
table.insert(textsegs, ",")
end
need_comma = true
table.insert(textsegs, " " .. fetch_typetext("meaningtype") .. "meaning " .. meaningtext)
end
if args.origin then
if need_comma then
table.insert(textsegs, ",")
end
need_comma = true
table.insert(textsegs, " of " .. args.origin .. " origin")
end
if args.usage then
if need_comma then
table.insert(textsegs, ",")
end
need_comma = true
table.insert(textsegs, " of " .. args.usage .. " usage")
end
if varoftext ~= "" then
table.insert(textsegs, ", " ..fetch_typetext("varoftype") .. "variant of " .. varoftext)
end
if blendtext ~= "" then
table.insert(textsegs, ", " .. fetch_typetext("blendtype") .. "blend of " .. blendtext)
end
if args.popular then
table.insert(textsegs, ", " .. fetch_typetext("populartype") .. "popular " .. args.popular)
end
if mtext ~= "" then
table.insert(textsegs, ", " .. fetch_typetext("mtype") .. "masculine equivalent " .. mtext)
end
if ftext ~= "" then
table.insert(textsegs, ", " .. fetch_typetext("ftype") .. "feminine equivalent " .. ftext)
end
if eqtext ~= "" then
table.insert(textsegs, ", " .. fetch_typetext("eqtype") .. "equivalent to " .. eqtext)
end
if args.q then
table.insert(textsegs, ", " .. args.q)
end
if varformtext ~= "" then
table.insert(textsegs, "; variant form" .. (numvarforms > 1 and "s" or "") .. " " .. varformtext)
end
if dimformtext ~= "" then
table.insert(textsegs, "; diminutive form" .. (numdimforms > 1 and "s" or "") .. " " .. dimformtext)
end
if augformtext ~= "" then
table.insert(textsegs, "; augmentative form" .. (numaugforms > 1 and "s" or "") .. " " .. augformtext)
end
table.insert(textsegs, "</span>")
local categories = {}
local langname = lang:getCanonicalName() .. " "
local function insert_cats(dimaugof)
if dimaugof == "" then
-- No category such as "English diminutives of given names"
table.insert(categories, langname .. "given names")
end
local function insert_cats_gender(g)
if g == "unknown-gender" then
track("unknown gender")
return
end
if g ~= "male" and g ~= "female" and g ~= "unisex" then
error("Unrecognized gender: " .. g)
end
if g == "unisex" then
insert_cats_gender("male")
insert_cats_gender("female")
end
table.insert(categories, langname .. dimaugof .. g .. " given names")
for _, catpart in ipairs(from_catparts) do
table.insert(categories, langname .. dimaugof .. g .. " given names from " .. catpart)
end
end
insert_cats_gender(args.gender)
if args["or"] then
insert_cats_gender(args["or"])
end
end
insert_cats("")
if numdims > 0 then
insert_cats("diminutives of ")
elseif numaugs > 0 then
insert_cats("augmentatives of ")
end
return table.concat(textsegs, "") ..
m_utilities.format_categories(categories, lang, args.sort, nil, force_cat)
end
-- The entry point for {{surname}}.
function export.surname(frame)
local parent_args = frame:getParent().args
local compat = parent_args.lang
local offset = compat and 0 or 1
if parent_args.dot or parent_args.nodot then
error("dot= and nodot= are no longer supported in [[Template:surname]] because a trailing period is no longer added by "
.. "default; if you want it, add it explicitly after the template")
end
local lang_index = compat and "lang" or 1
local params = {
[lang_index] = { required = true, default = "und" },
["g"] = {list = true}, -- gender(s)
[1 + offset] = {}, -- adjective/qualifier
["usage"] = {},
["origin"] = {},
["popular"] = {},
["populartype"] = {},
["meaning"] = { list = true },
["meaningtype"] = {},
["q"] = {},
-- initial article: by default A or An (English), a or an (otherwise)
["A"] = {},
["sort"] = {},
["from"] = { list = true },
["fromtype"] = {},
["xlit"] = { list = true },
["eq"] = { list = true },
["eqtype"] = {},
["varof"] = { list = true },
["varoftype"] = {},
["var"] = { alias_of = "varof", list = true },
["vartype"] = { alias_of = "varoftype" },
["varform"] = { list = true },
["blend"] = { list = true },
["blendtype"] = {},
["m"] = { list = true },
["mtype"] = {},
["f"] = { list = true },
["ftype"] = {},
["nocat"] = {type = "boolean"},
}
local args = require("Module:parameters").process(parent_args, params)
local textsegs = {}
local langcode = args[lang_index]
local lang = m_languages.getByCode(langcode, lang_index)
local function fetch_typetext(param)
return args[param] and args[param] .. " " or ""
end
local adj = args[1 + offset]
local xlittext = join_names(nil, args, "xlit")
local blendtext = join_names(lang, args, "blend", "and")
local varoftext = join_names(lang, args, "varof")
local mtext = join_names(lang, args, "m")
local ftext = join_names(lang, args, "f")
local varformtext, numvarforms = join_names(lang, args, "varform", ", ")
local meaningsegs = {}
for _, meaning in ipairs(args.meaning) do
table.insert(meaningsegs, '"' .. meaning .. '"')
end
local meaningtext = m_table.serialCommaJoin(meaningsegs, {conj = "or"})
local eqtext = get_eqtext(args)
table.insert(textsegs, "<span class='use-with-mention'>")
local genders = {}
for _, g in ipairs(args.g) do
local origg = g
if g == "unknown" or g == "unknown gender" or g == "?" then
g = "unknown-gender"
elseif g == "unisex" or g == "common gender" or g == "c" then
g = "common-gender"
elseif g == "m" then
g = "male"
elseif g == "f" then
g = "female"
end
if g == "unknown-gender" then
track("unknown gender")
elseif g ~= "male" and g ~= "female" and g ~= "common-gender" then
error("Unrecognized gender: " .. origg)
end
table.insert(genders, g)
end
local article_a, article_an
if langcode == "en" then
article_a, article_an = "A", "An"
else
article_a, article_an = "a", "an"
end
-- If gender is supplied, it goes before the specified adjective in adj=. The only value of gender that uses "an" is
-- "unknown-gender" (note that "unisex" wouldn't use it but in any case we map "unisex" to "common-gender"). If gender
-- isn't supplied, look at the first letter of the value of adj= if supplied; otherwise, the article is always "a"
-- because the word "surname" follows. Capitalize "A"/"An" if English.
local article = args.A or
#genders > 0 and genders[1] == "unknown-gender" and article_an or
#genders == 0 and adj and rfind(m_links.remove_links(adj), "^[aeiouAEIOU]") and article_an or
article_a
table.insert(textsegs, article .. " ")
if #genders > 0 then
table.insert(textsegs, table.concat(genders, " or ") .. " ")
end
if adj then
table.insert(textsegs, adj .. " ")
end
table.insert(textsegs, "[[surname]]")
local need_comma = false
if xlittext ~= "" then
table.insert(textsegs, ", " .. xlittext)
need_comma = true
end
local from_catparts = {}
if #args.from > 0 then
if need_comma then
table.insert(textsegs, ",")
end
need_comma = true
table.insert(textsegs, " " .. fetch_typetext("fromtype"))
local textseg, this_catparts = get_fromtext(lang, args)
for _, catpart in ipairs(this_catparts) do
m_table.insertIfNot(from_catparts, catpart)
end
table.insert(textsegs, textseg)
end
if meaningtext ~= "" then
if need_comma then
table.insert(textsegs, ",")
end
need_comma = true
table.insert(textsegs, " " .. fetch_typetext("meaningtype") .. "meaning " .. meaningtext)
end
if args.origin then
if need_comma then
table.insert(textsegs, ",")
end
need_comma = true
table.insert(textsegs, " of " .. args.origin .. " origin")
end
if args.usage then
if need_comma then
table.insert(textsegs, ",")
end
need_comma = true
table.insert(textsegs, " of " .. args.usage .. " usage")
end
if varoftext ~= "" then
table.insert(textsegs, ", " ..fetch_typetext("varoftype") .. "variant of " .. varoftext)
end
if blendtext ~= "" then
table.insert(textsegs, ", " .. fetch_typetext("blendtype") .. "blend of " .. blendtext)
end
if args.popular then
table.insert(textsegs, ", " .. fetch_typetext("populartype") .. "popular " .. args.popular)
end
if mtext ~= "" then
table.insert(textsegs, ", " .. fetch_typetext("mtype") .. "masculine equivalent " .. mtext)
end
if ftext ~= "" then
table.insert(textsegs, ", " .. fetch_typetext("ftype") .. "feminine equivalent " .. ftext)
end
if eqtext ~= "" then
table.insert(textsegs, ", " .. fetch_typetext("eqtype") .. "equivalent to " .. eqtext)
end
if args.q then
table.insert(textsegs, ", " .. args.q)
end
if varformtext ~= "" then
table.insert(textsegs, "; variant form" .. (numvarforms > 1 and "s" or "") .. " " .. varformtext)
end
table.insert(textsegs, "</span>")
local text = table.concat(textsegs, "")
if args.nocat then
return text
end
local categories = {}
local langname = lang:getCanonicalName() .. " "
local function insert_cats(g)
g = g and g .. " " or ""
table.insert(categories, langname .. g .. "surnames")
for _, catpart in ipairs(from_catparts) do
table.insert(categories, langname .. g .. "surnames from " .. catpart)
end
end
insert_cats(nil)
local function insert_cats_gender(g)
local origg = g
if g == "unknown-gender" then
return
end
if g == "common-gender" then
insert_cats_gender("male")
insert_cats_gender("female")
end
insert_cats(g)
end
for _, g in ipairs(genders) do
insert_cats_gender(g)
end
return text .. m_utilities.format_categories(categories, lang, args.sort, nil, force_cat)
end
-- The entry point for {{name translit}}, {{name respelling}}, {{name obor}} and {{foreign name}}.
function export.name_translit(frame)
local iparams = {
["desctext"] = {required = true},
["obor"] = {type = "boolean"},
["foreign_name"] = {type = "boolean"},
}
local iargs = require("Module:parameters").process(frame.args, iparams)
local parent_args = frame:getParent().args
local params = {
[1] = { required = true, default = "en" },
[2] = { required = true, default = "ru" },
[3] = { list = true },
["type"] = { required = true, list = true, default = "patronymic" },
["alt"] = { list = true, allow_holes = true },
["t"] = { list = true, allow_holes = true },
["gloss"] = { list = true, alias_of = "t", allow_holes = true },
["tr"] = { list = true, allow_holes = true },
["ts"] = { list = true, allow_holes = true },
["id"] = { list = true, allow_holes = true },
["sc"] = { list = true, allow_holes = true },
["g"] = { list = true, allow_holes = true },
["q"] = { list = true, allow_holes = true },
["xlit"] = { list = true, allow_holes = true },
["eq"] = { list = true, allow_holes = true },
["dim"] = { type = "boolean" },
["aug"] = { type = "boolean" },
["nocap"] = { type = "boolean" },
["sort"] = {},
["pagename"] = {},
}
local args = require("Module:parameters").process(parent_args, params)
local lang = m_languages.getByCode(args[1], 1)
local sources = {}
for _, source in ipairs(rsplit(args[2], "%s*,%s*")) do
local sourcelang = m_languages.getByCode(source, 2, "allow etym")
table.insert(sources, sourcelang)
end
local nametypes = {}
for _, typearg in ipairs(args["type"]) do
for _, ty in ipairs(rsplit(typearg, "%s*,%s*")) do
if not translit_name_types[ty] then
local quoted_types = {}
for _, nametype in ipairs(translit_name_type_list) do
table.insert(quoted_types, "'" .. nametype .. "'")
end
error("Unrecognized type '" .. ty .. "': It should be one of " ..
m_table.serialCommaJoin(quoted_types, {conj = "or"}))
end
table.insert(nametypes, ty)
end
end
-- Find the maximum index among any of the list parameters, to determine how many names are given.
local maxmaxindex = #args[3]
for k, v in pairs(args) do
if type(v) == "table" and v.maxindex and v.maxindex > maxmaxindex then
maxmaxindex = v.maxindex
end
end
local SUBPAGENAME = args.pagename or mw.title.getCurrentTitle().subpageText
local textsegs = {}
table.insert(textsegs, "<span class='use-with-mention'>")
local desctext = iargs.desctext
if not args.nocap then
desctext = mw.getContentLanguage():ucfirst(desctext)
end
table.insert(textsegs, desctext .. " ")
if not iargs.foreign_name then
table.insert(textsegs, "of ")
end
local langsegs = {}
for i, source in ipairs(sources) do
local sourcename = source:getCanonicalName()
local function get_source_link()
local term_to_link = args[3][1] or SUBPAGENAME
-- We link the language name to either the first specified name or the pagename, in the following circumstances:
-- (1) More than one language was given along with at least one name; or
-- (2) We're handling {{foreign name}} or {{name obor}}, and no name was given.
-- The reason for (1) is that if more than one language was given, we want a link to the name
-- in each language, as the name that's displayed is linked only to the first specified language.
-- However, if only one language was given, linking the language to the name is redundant.
-- The reason for (2) is that {{foreign name}} is often used when the name in the destination language
-- is spelled the same as the name in the source language (e.g. [[Clinton]] or [[Obama]] in Italian),
-- and in that case no name will be explicitly specified but we still want a link to the name in the
-- source language. The reason we restrict this to {{foreign name}} or {{name obor}}, not to {{name translit}}
-- or {{name respelling}}, is that {{name translit}} and {{name respelling}} ought to be used for names
-- spelled differently in the destination language (either transliterated or respelled), so assuming the
-- pagename is the name in the source language is wrong.
if args[3][1] and #sources > 1 or (iargs.foreign_name or iargs.obor) and not args[3][1] then
return m_links.language_link({
lang = sources[i], term = term_to_link, alt = sourcename, tr = "-"
}, "allow self link")
else
return sourcename
end
end
if i == 1 and not iargs.foreign_name then
-- If at least one name is given, we say "A transliteration of the LANG surname FOO", linking LANG to FOO.
-- Otherwise we say "A transliteration of a LANG surname".
if maxmaxindex > 0 then
table.insert(langsegs, "the " .. get_source_link())
else
table.insert(langsegs, require("Module:string utilities").add_indefinite_article(sourcename))
end
else
table.insert(langsegs, get_source_link())
end
end
local langseg_text = m_table.serialCommaJoin(langsegs, {conj = "or"})
local augdim_text
if args.dim then
augdim_text = " [[diminutive]]"
elseif args.aug then
augdim_text = " [[augmentative]]"
else
augdim_text = ""
end
local nametype_text = m_table.serialCommaJoin(nametypes) .. augdim_text
if not iargs.foreign_name then
table.insert(textsegs, langseg_text .. " ")
table.insert(textsegs, nametype_text)
if maxmaxindex > 0 then
table.insert(textsegs, " ")
end
else
table.insert(textsegs, nametype_text)
table.insert(textsegs, " in " .. langseg_text)
if maxmaxindex > 0 then
table.insert(textsegs, ", ")
end
end
local names = {}
local embedded_comma = false
for i = 1, maxmaxindex do
local sc = require("Module:scripts").getByCode(args["sc"][i], true)
local terminfo = {
lang = sources[1], term = args[3][i], alt = args["alt"][i], id = args["id"][i], sc = sc,
tr = args["tr"][i], ts = args["ts"][i], gloss = args["t"][i],
genders = args["g"][i] and rsplit(args["g"][i], ",") or {}
}
local linked_term = m_links.full_link(terminfo, "term", "allow self link")
if args["q"][i] then
linked_term = require("Module:qualifier").format_qualifier(args["q"][i]) .. " " .. linked_term
end
if args["xlit"][i] then
embedded_comma = true
linked_term = linked_term .. ", " .. m_links.language_link({ lang = m_languages.getByCode("en"), term = args["xlit"][i] }, "allow self link")
end
if args["eq"][i] then
embedded_comma = true
linked_term = linked_term .. ", equivalent to " .. m_links.language_link({ lang = m_languages.getByCode("en"), term = args["eq"][i] }, "allow self link")
end
table.insert(names, linked_term)
end
if embedded_comma then
table.insert(textsegs, table.concat(names, "; or of "))
else
table.insert(textsegs, m_table.serialCommaJoin(names, {conj = "or"}))
end
table.insert(textsegs, "</span>")
local categories = {}
for _, nametype in ipairs(nametypes) do
local function insert_cats(dimaugof)
local function insert_cats_type(ty)
if ty == "unisex given name" then
insert_cats_type("male given name")
insert_cats_type("female given name")
end
for i, source in ipairs(sources) do
table.insert(categories, lang:getNonEtymologicalCode() .. ":" .. source:getCanonicalName() .. " " .. dimaugof .. ty .. "s")
table.insert(categories, lang:getNonEtymologicalName() .. " terms derived from " .. source:getCanonicalName())
table.insert(categories, lang:getNonEtymologicalName() .. " terms borrowed from " .. source:getCanonicalName())
if iargs.obor then
table.insert(categories, lang:getNonEtymologicalName() .. " orthographic borrowings from " .. source:getCanonicalName())
end
if source:getCode() ~= source:getNonEtymologicalCode() then
-- etymology language
table.insert(categories, lang:getNonEtymologicalCode() .. ":" .. source:getNonEtymologicalName() .. " " .. dimaugof .. ty .. "s")
end
end
end
insert_cats_type(nametype)
end
insert_cats("")
if args.dim then
insert_cats("diminutives of ")
end
if args.aug then
insert_cats("augmentatives of ")
end
end
return table.concat(textsegs, "") ..
m_utilities.format_categories(categories, lang, args.sort, nil, force_cat)
end
return export