Module:names: Difference between revisions

No edit summary
No edit summary
 
(7 intermediate revisions by 2 users not shown)
Line 1: Line 1:
local export = {}
local m_languages = require("Module:languages")
local m_languages = require("Module:languages")
local m_links = require("Module:links")
local m_links = require("Module:links")
local m_utilities = require("Module:utilities")
local m_utilities = require("Module:utilities")
local m_str_utils = require("Module:string utilities")
local m_table = require("Module:table")
local m_table = require("Module:table")
local en_utilities_module = "Module:en-utilities"
local parameter_utilities_module = "Module:parameter utilities"
local parse_interface_module = "Module:parse interface"
local parse_utilities_module = "Module:parse utilities"
local pron_qualifier_module = "Module:pron qualifier"


local export = {}
local enlang = m_languages.getByCode("en")
 
local rsubn = m_str_utils.gsub
local rsplit = m_str_utils.split
local u = m_str_utils.char


local enlang = m_languages.getByCode("en")
local function rsub(str, from, to)
    return (rsubn(str, from, to))
end


local rfind = mw.ustring.find
local TEMP_LESS_THAN = u(0xFFF2)
local rmatch = mw.ustring.match
local rsubn = mw.ustring.gsub
local rsplit = mw.text.split


local force_cat = false -- for testing
local force_cat = false -- for testing
Line 35: Line 46:
]=]
]=]


-- version of rsubn() that discards all but the first return value
-- Used in category code; name types which are full-word end-matching substrings of longer name types (e.g. "surnames"
local function rsub(term, foo, bar)
-- of "male surnames", but not "male surnames" of "female surnames" because "male" only matches a part of the word
local retval = rsubn(term, foo, bar)
-- "female") should follow the longer name.
return retval
export.personal_name_types = {
end
"male surnames", "female surnames", "common-gender surnames", "surnames",
"patronymics", "matronymics",
}
 
export.personal_name_type_set = m_table.listToSet(export.personal_name_types)


-- Used in category code
export.given_name_genders = {
export.personal_name_types = {
male = {type = "human"},
"surnames", "patronymics", "given names",
female = {type = "human"},
"male given names", "female given names", "unisex given names",
unisex = {type = "human", cat = {"male given names", "female given names", "unisex given names"}, article = "a"},
"diminutives of male given names", "diminutives of female given names",
["unknown-gender"] = {type = "human", cat = {}, track = true},
"diminutives of unisex given names",
animal = {type = "animal", track = true},
"augmentatives of male given names", "augmentatives of female given names",
cat = {type = "animal"},
"augmentatives of unisex given names"
cow = {type = "animal"},
dog = {type = "animal"},
horse = {type = "animal"},
pig = {type = "animal"},
parrot = {type = "animal"}
}
}


local function get_given_name_cats(gender, props)
local cats = props.cat
if not cats then
if props.type == "animal" then
cats = {gender .. " names"}
else
cats = {gender .. " given names"}
end
end
return cats
end
do
local function do_cat(cat)
if not export.personal_name_type_set[cat] then
export.personal_name_type_set[cat] = true
table.insert(export.personal_name_types, cat)
end
end
for gender, props in pairs(export.given_name_genders) do
local cats = get_given_name_cats(gender, props)
for _, cat in ipairs(cats) do
do_cat("diminutives of " .. cat)
do_cat("augmentatives of " .. cat)
do_cat(cat)
end
end
do_cat("given names")
end


local translit_name_type_list = {
local translit_name_type_list = {
Line 56: Line 106:
"patronymic"
"patronymic"
}
}
local translit_name_types = m_table.listToSet(translit_name_type_list)
local function track(page)
require("Module:debug").track("names/" .. page)
end


local param_mods = {"t", "alt", "tr", "ts", "pos", "lit", "id", "sc", "g", "q", "eq"}
local param_mod_set = m_table.listToSet(param_mods)


-- Get raw text, for use in computing the indefinite article. Use get_plaintext() in [[Module:utilities]] and also
-- remove parens that may surround qualifier or label text preceding a term.
local function get_rawtext(text)
text = m_utilities.get_plaintext(text)
text = text:gsub("[()%[%]]", "")
return text
end




Line 67: Line 124:
'Kunigunde<q:medieval, now rare>' or 'non:Óláfr' or 'ru:Фру́нзе<tr:Frúnzɛ><q:rare>' where the modifying properties
'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
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
and colons; `paramname` 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
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
'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).
(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
Normally the return value is a terminfo object that can be passed to full_link() in [[Module:links]]), additionally
[[Module:links]]) and '.q' (a qualifier). However, if `allow_multiple_terms` is given, multiple comma-separated names
with optional fields `.q`, `.qq`, `.l`, `.ll`, `.refs` and `.eq` (a list of objects of the same form as the returned
can be given in `term`, and the return value is a list of objects of the form described just above.
terminfo object. 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_term_with_annotations(term, paramname, deflang, allow_explicit_lang, allow_multiple_terms)
local function parse_single_run_with_annotations(run)
local param_mods = require(parameter_utilities_module).construct_param_mods {
local function parse_err(msg)
{group = {"link", "l", "q", "ref"}},
error(msg .. ": " .. pname .. "= " .. table.concat(run))
{param = "eq", convert = function(eqval, parse_err)
return parse_term_with_annotations(eqval, paramname .. ".eq", enlang, false, "allow multiple terms")
end},
}
local function generate_obj(term, parse_err)
local termlang
if allow_explicit_lang then
local actual_term
actual_term, termlang = require(parse_interface_module).parse_term_with_lang {
term = term,
parse_err = parse_err,
paramname = paramname,
}
term = actual_term or term
end
end
if #run == 1 and run[1] == "" then
return {
error("Blank form for param '" .. pname .. "' not allowed")
term = term,
end
lang = termlang or deflang,
local termobj = {term = {}}
}
local lang, form = run[1]:match("^(.-):(.*)$")
if lang 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
return require(parse_interface_module).parse_inline_modifiers(term, {
param_mods = param_mods,
paramname = paramname,
generate_obj = generate_obj,
splitchar = allow_multiple_terms and "," or nil,
})
end
end


Line 155: Line 168:
--[=[
--[=[
Link a single term. If `do_language_link` is given and a given term's language is English, the link will be constructed
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
using language_link() in [[Module:links]]; otherwise, with full_link(). `termobj` is an object as returned by
by parse_term_with_annotations(), i.e. it contains fields '.term' (a terminfo structure suitable for passing to
parse_term_with_annotations(), i.e. it is suitable for passing to [[Module:links]] and additionally contains optional
full_link() or language_link()), optional '.q' (a qualifier) and optional '.eq' (a list of objects of the same form as
fields `.q`, `.qq`, `.l`, `.ll`, `.refs` and `.eq` (a list of objects of the same form as `termobj`).
`termobj`).
]=]
]=]
local function link_one_term(termobj, do_language_link)
local function link_one_term(termobj, do_language_link)
termobj.term.lang = m_languages.getNonEtymological(termobj.term.lang)
local link
local link
if do_language_link and termobj.term.lang:getCode() == "en" then
if do_language_link and termobj.lang:getCode() == "en" then
link = m_links.language_link(termobj.term, nil, true)
link = m_links.language_link(termobj)
else
else
link = m_links.full_link(termobj.term, nil, true)
link = m_links.full_link(termobj)
end
end
if termobj.q then
if termobj.q and termobj.q[1] or termobj.qq and termobj.qq[1] or
link = require("Module:qualifier").format_qualifier(termobj.q) .. " " .. link
termobj.l and termobj.l[1] or termobj.ll and termobj.ll[1] or termobj.refs and termobj.refs[1] then
link = require(pron_qualifier_module).format_qualifiers {
lang = termobj.lang,
text = link,
q = termobj.q,
qq = termobj.qq,
l = termobj.l,
ll = termobj.ll,
refs = termobj.refs,
}
end
end
if termobj.eq then
if termobj.eq then
Line 190: Line 210:
`do_language_link` is given and a given term's language is English, the link will be constructed using language_link()
`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
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()
parse_term_with_annotations().
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 function join_terms(terms, include_langname, do_language_link, conj)
Line 198: Line 217:
for _, termobj in ipairs(terms) do
for _, termobj in ipairs(terms) do
if include_langname and not langnametext then
if include_langname and not langnametext then
langnametext = termobj.term.lang:getCanonicalName() .. " "
langnametext = termobj.lang:getCanonicalName() .. " "
end
end
table.insert(links, link_one_term(termobj, do_language_link))
table.insert(links, link_one_term(termobj, do_language_link))
Line 231: Line 250:
end
end


for i, term in ipairs(args[pname]) do
local function process_one_term(term, i)
table.insert(termobjs, parse_term_with_annotations(term, pname .. (i == 1 and "" or i), lang, allow_explicit_lang))
for _, termobj in ipairs(parse_term_with_annotations(term, pname .. (i == 1 and "" or i), lang,
allow_explicit_lang, "allow multiple terms")) do
table.insert(termobjs, termobj)
end
end
 
if not args[pname] then
return "", 0
elseif type(args[pname]) == "table" then
for i, term in ipairs(args[pname]) do
process_one_term(term, i)
end
else
process_one_term(args[pname], 1)
end
end
return join_terms(termobjs, nil, do_language_link, conj), #termobjs
return join_terms(termobjs, nil, do_language_link, conj), #termobjs
Line 242: Line 274:
local lastlang = nil
local lastlang = nil
local last_eqseg = {}
local last_eqseg = {}
for i, term in ipairs(args.eq) do
local function process_one_term(term, i)
local termobj = parse_term_with_annotations(term, "eq" .. (i == 1 and "" or i), enlang, "allow explicit lang")
for _, termobj in ipairs(parse_term_with_annotations(term, "eq" .. (i == 1 and "" or i), enlang,
local termlang = termobj.term.lang:getCode()
"allow explicit lang", "allow multiple terms")) do
if lastlang and lastlang ~= termlang then
local termlang = termobj.lang:getCode()
if #last_eqseg > 0 then
if lastlang and lastlang ~= termlang then
table.insert(eqsegs, last_eqseg)
if #last_eqseg > 0 then
table.insert(eqsegs, last_eqseg)
end
last_eqseg = {}
end
end
last_eqseg = {}
lastlang = termlang
table.insert(last_eqseg, termobj)
end
end
if type(args.eq) == "table" then
for i, term in ipairs(args.eq) do
process_one_term(term, i)
end
end
lastlang = termlang
elseif type(args.eq) == "string" then
table.insert(last_eqseg, termobj)
process_one_term(args.eq, 1)
end
end
if #last_eqseg > 0 then
if #last_eqseg > 0 then
Line 273: Line 314:
local unrecognized = false
local unrecognized = false
local prefix, suffix
local prefix, suffix
if from == "surnames" then
if from == "surnames" or from == "given names" or from == "nicknames" or from == "place names" or from == "common nouns" or from == "month names" then
prefix = "transferred from the "
prefix = "transferred from the "
suffix = "surname"
suffix = from:gsub("s$", "")
table.insert(catparts, from)
table.insert(catparts, from)
elseif from == "place names" then
elseif from == "patronymics" or from == "matronymics" or from == "coinages" then
prefix = "transferred from the "
prefix = "originating "
suffix = "place name"
suffix = "as a " .. from:gsub("s$", "")
table.insert(catparts, from)
table.insert(catparts, from)
elseif from == "coinages" then
elseif from == "occupations" or from == "ethnonyms" then
prefix = "originating "
prefix = "originating "
suffix = "as a coinage"
suffix = "as an " .. from:gsub("s$", "")
table.insert(catparts, from)
table.insert(catparts, from)
elseif from == "the Bible" then
elseif from == "the Bible" then
Line 292: Line 333:
prefix = "from "
prefix = "from "
if from:find(":") then
if from:find(":") then
local termobj = parse_term_with_annotations(from, "from" .. (i == 1 and "" or i), lang, "allow explicit lang")
local termobj = parse_term_with_annotations(from, "from" .. (i == 1 and "" or i), lang,
"allow explicit lang")
local fromlangname = ""
local fromlangname = ""
if termobj.term.lang:getCode() ~= lang:getCode() then
if termobj.lang:getCode() ~= lang:getCode() then
-- If name is derived from another name in the same language, don't include lang name after text "from "
-- If name is derived from another name in the same language, don't include lang name after text
-- or create a category like "German male given names derived from German".
-- "from " or create a category like "German male given names derived from German".
local canonical_name = termobj.term.lang:getCanonicalName()
local canonical_name = termobj.lang:getCanonicalName()
fromlangname = canonical_name .. " "
fromlangname = canonical_name .. " "
table.insert(catparts, canonical_name)
table.insert(catparts, canonical_name)
end
end
termobj.term.lang = m_languages.getNonEtymological(termobj.term.lang)
suffix = fromlangname .. link_one_term(termobj)
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
else
if m_languages.getByCanonicalName(from, nil, "allow etym") then
local family = from:match("^(.+) languages$") or
table.insert(catparts, from)
from:match("^.+ Languages$") or
from:match("^.+ [Ll]ects$")
if family then
if require("Module:families").getByCanonicalName(family) then
table.insert(catparts, from)
else
unrecognized = true
end
suffix = "the " .. from
else
else
unrecognized = true
if m_languages.getByCanonicalName(from, nil, "allow etym") then
table.insert(catparts, from)
else
unrecognized = true
end
suffix = from
end
end
suffix = from
end
end
end
if unrecognized then
track("unrecognized from")
track("unrecognized from/" .. from)
end
end
return prefix, suffix
return prefix, suffix
Line 324: Line 373:


local last_fromseg = nil
local last_fromseg = nil
while args.from[i] do
local put = require(parse_utilities_module)
local rawfrom = args.from[i]
local from_args = args.from or {}
local froms = rsplit(rawfrom, "%s+<%s+")
if type(from_args) == "string" then
if #froms == 1 then
from_args = {from_args}
local prefix, suffix = parse_from(froms[1])
end
if last_fromseg and (last_fromseg.has_multiple_froms or last_fromseg.prefix ~= prefix) then
while from_args[i] do
table.insert(fromsegs, last_fromseg)
-- We may have multiple comma-separated items, each of which may have multiple items separated by a
last_fromseg = nil
-- space-delimited < sign, each of which may have inline modifiers with embedded commas in them. To handle
end
-- this correctly, first replace space-delimited < signs with a special character, then split on balanced
if not last_fromseg then
-- <...> and [...] signs, then split on comma, then rejoin the stuff between commas. We will then split on
last_fromseg = {prefix = prefix, suffixes = {}}
-- TEMP_LESS_THAN (the replacement for space-delimited < signs) and reparse.
end
local rawfroms = rsub(from_args[i], "%s+<%s+", TEMP_LESS_THAN)
table.insert(last_fromseg.suffixes, suffix)
        local segments = put.parse_multi_delimiter_balanced_segment_run(rawfroms, {{"<", ">"}, {"[", "]"}})
else
        local comma_separated_groups = put.split_alternating_runs_on_comma(segments)
if last_fromseg then
        for j, comma_separated_group in ipairs(comma_separated_groups) do
table.insert(fromsegs, last_fromseg)
        comma_separated_groups[j] = table.concat(comma_separated_group)
last_fromseg = nil
        end
end
for _, rawfrom in ipairs(comma_separated_groups) do
local first_suffixpart = ""
local froms = rsplit(rawfrom, TEMP_LESS_THAN)
local rest_suffixparts = {}
if #froms == 1 then
for j, from in ipairs(froms) do
local prefix, suffix = parse_from(froms[1])
local prefix, suffix = parse_from(from)
if last_fromseg and (last_fromseg.has_multiple_froms or last_fromseg.prefix ~= prefix) then
if j == 1 then
table.insert(fromsegs, last_fromseg)
first_suffixpart = prefix .. suffix
last_fromseg = nil
else
end
table.insert(rest_suffixparts, prefix .. suffix)
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
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
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
end
i = i + 1
i = i + 1
Line 360: Line 426:
local fromtextsegs = {}
local fromtextsegs = {}
for _, fromseg in ipairs(fromsegs) do
for _, fromseg in ipairs(fromsegs) do
table.insert(fromtextsegs, fromseg.prefix .. m_table.serialCommaJoin(fromseg.suffixes, {conj = "or"}))
table.insert(fromtextsegs, fromseg.prefix .. m_table.serialCommaJoin(fromseg.suffixes, {conj = "or"}))
end
end
return m_table.serialCommaJoin(fromtextsegs, {conj = "or"}), catparts
return m_table.serialCommaJoin(fromtextsegs, {conj = "or"}), catparts
end
local function parse_given_name_genders(genderspec)
if export.given_name_genders[genderspec] then -- optimization
return {{
type = genderspec,
props = export.given_name_genders[genderspec],
}}, export.given_name_genders[genderspec].type == "animal"
end
local genders = {}
local is_animal = nil
local param_mods = require(parameter_utilities_module).construct_param_mods {
{group = {"l", "q", "ref"}},
{param = {"text", "article"}},
}
local function generate_obj(term, parse_err)
if not export.given_name_genders[term] then
local valid_genders = {}
for k, _ in pairs(export.given_name_genders) do
table.insert(valid_genders, k)
end
table.sort(valid_genders)
parse_err(("Unrecognized gender '%s': valid genders are %s"):format(
term, table.concat(valid_genders, ", ")))
end
return {
type = term,
props = export.given_name_genders[term],
}
end
local retval = require(parse_interface_module).parse_inline_modifiers(genderspec, {
param_mods = param_mods,
paramname = "2",
generate_obj = generate_obj,
splitchar = ",",
})
for _, spec in ipairs(retval) do
local this_is_animal = spec.props.type == "animal"
if is_animal == nil then
is_animal = this_is_animal
elseif is_animal ~= this_is_animal then
error("Can't mix animal and human genders")
end
end
return retval, is_animal
end
local function generate_given_name_genders(lang, genders)
local parts = {}
for _, spec in ipairs(genders) do
local text
if spec.text then
-- NOTE: This assumes no % sign in the gender type, which seems safe.
text = spec.text:gsub("%+", spec.type)
else
if spec.props.type == "animal" then
text = "[[wikt:" .. spec.type .. "|" .. spec.type .. "]]"
else
text = spec.type
end
end
if spec.q and spec.q[1] or spec.qq and spec.qq[1] or spec.l and spec.l[1] or spec.ll and spec.ll[1] or
spec.refs and spec.refs[1] then
text = require(pron_qualifier_module).format_qualifiers {
lang = lang,
text = text,
q = spec.q,
qq = spec.qq,
l = spec.l,
ll = spec.ll,
refs = spec.refs,
raw = true,
}
end
table.insert(parts, text)
end
local retval = m_table.serialCommaJoin(parts, {conj = "or"})
local article = genders[1].article
if not article and not genders[1].text and not genders[1].q and not genders[1].l then
article = genders[1].props.article
end
if not article then
article = require(en_utilities_module).get_indefinite_article(get_rawtext(retval))
end
return retval, article
end
end


Line 372: Line 525:
local offset = compat and 0 or 1
local offset = compat and 0 or 1


local params = {
local lang_index = compat and "lang" or 1
[compat and "lang" or 1] = { required = true, default = "und" },
 
["gender"] = { default = "unknown-gender" },
local list = {list = true}
[1 + offset] = { alias_of = "gender", default = "unknown-gender" },
local args = require("Module:parameters").process(parent_args, {
-- second gender
[lang_index] = {required = true, type = "language", default = "und"},
["or"] = {},
["gender"] = {default = "unknown-gender"},
["usage"] = {},
[1 + offset] = {alias_of = "gender"},
["origin"] = {},
["usage"] = true,
["popular"] = {},
["origin"] = true,
["populartype"] = {},
["popular"] = true,
["meaning"] = { list = true },
["populartype"] = true,
["meaningtype"] = {},
["meaning"] = list,
["q"] = {},
["meaningtype"] = true,
["addl"] = true,
-- initial article: A or An
-- initial article: A or An
["A"] = {},
["A"] = true,
["sort"] = {},
["sort"] = true,
["from"] = { list = true },
["from"] = true,
[2 + offset] = { alias_of = "from", list = true },
[2 + offset] = {alias_of = "from"},
["fromtype"] = {},
["fromtype"] = true,
["xlit"] = { list = true },
["xlit"] = true,
["eq"] = { list = true },
["eq"] = true,
["eqtype"] = {},
["eqtype"] = true,
["varof"] = { list = true },
["varof"] = true,
["varoftype"] = {},
["varoftype"] = true,
["var"] = { alias_of = "varof", list = true },
["var"] = {alias_of = "varof"},
["vartype"] = { alias_of = "varoftype" },
["vartype"] = {alias_of = "varoftype"},
["varform"] = { list = true },
["varform"] = true,
["dimof"] = { list = true },
["varformtype"] = true,
["dimoftype"] = {},
["dimof"] = true,
["dim"] = { alias_of = "dimof", list = true },
["dimoftype"] = true,
["dimtype"] = { alias_of = "dimoftype" },
["dim"] = {alias_of = "dimof"},
["diminutive"] = { alias_of = "dimof", list = true },
["dimtype"] = {alias_of = "dimoftype"},
["diminutivetype"] = { alias_of = "dimoftype" },
["dimform"] = true,
["dimform"] = { list = true },
["dimformtype"] = true,
["blend"] = { list = true },
["augof"] = true,
["blendtype"] = {},
["augoftype"] = true,
["m"] = { list = true },
["aug"] = {alias_of = "augof"},
["mtype"] = {},
["augtype"] = {alias_of = "augoftype"},
["f"] = { list = true },
["augform"] = true,
["ftype"] = {},
["augformtype"] = true,
}
["clipof"] = true,
["clipoftype"] = true,
["blend"] = true,
["blendtype"] = true,
["m"] = true,
["mtype"] = true,
["f"] = true,
["ftype"] = true,
})


local args = require("Module:parameters").process(parent_args, params)
local textsegs = {}
local textsegs = {}
local lang = m_languages.getByCode(args[compat and "lang" or 1], compat and "lang" or 1)
local lang = args[lang_index]
local langcode = lang:getCode()


local function fetch_typetext(param)
local function fetch_typetext(param)
Line 423: Line 584:
end
end


local dimoftext, numdims = join_names(lang, args, "dimof")
local genders, is_animal = parse_given_name_genders(args.gender)
 
local dimoftext, numdimofs = join_names(lang, args, "dimof")
local augoftext, numaugofs = join_names(lang, args, "augof")
local xlittext = join_names(nil, args, "xlit")
local xlittext = join_names(nil, args, "xlit")
local blendtext = join_names(lang, args, "blend", "and")
local blendtext = join_names(lang, args, "blend", "and")
local varoftext = join_names(lang, args, "varof")
local varoftext = join_names(lang, args, "varof")
local clipoftext = join_names(lang, args, "clipof")
local mtext = join_names(lang, args, "m")
local mtext = join_names(lang, args, "m")
local ftext = join_names(lang, args, "f")
local ftext = join_names(lang, args, "f")
local varformtext, numvarforms = join_names(lang, args, "varform", ", ")
local varformtext, numvarforms = join_names(lang, args, "varform", ", ")
local dimformtext, numdimforms = join_names(lang, args, "dimform", ", ")
local dimformtext, numdimforms = join_names(lang, args, "dimform", ", ")
local augformtext, numaugforms = join_names(lang, args, "augform", ", ")
local meaningsegs = {}
local meaningsegs = {}
for _, meaning in ipairs(args.meaning) do
for _, meaning in ipairs(args.meaning) do
table.insert(meaningsegs, '"' .. meaning .. '"')
table.insert(meaningsegs, '' .. meaning .. '')
end
end
local meaningtext = m_table.serialCommaJoin(meaningsegs, {conj = "or"})
local meaningtext = m_table.serialCommaJoin(meaningsegs, {conj = "or"})
local eqtext = get_eqtext(args)
local eqtext = get_eqtext(args)


table.insert(textsegs, "<span class='use-with-mention'>")
local function ins(txt)
local dimtype = args.dimtype
table.insert(textsegs, txt)
local article = args.A or
end
dimtype and rfind(dimtype, "^[aeiouAEIOU]") and "An" or
local dimoftype = args.dimoftype
args.gender == "unknown-gender" and "An" or
local augoftype = args.augoftype
"A"
added_text = nil
if numdimofs > 0 then
added_text = (dimoftype and dimoftype .. " " or "") .. "[[wikt:diminutive|diminutive]]" ..
(xlittext ~= "" and ", " .. xlittext .. "," or "") .. " of "
elseif numaugofs > 0 then
added_text = (augoftype and augoftype .. " " or "") .. "[[wikt:augmentative|augmentative]]" ..
(xlittext ~= "" and ", " .. xlittext .. "," or "") .. " of "
end
force_plural = false
if added_text ~= nil then
if args.dimof == "-" then
dimoftext = ""
force_plural = true
else
added_text = added_text .. "the "
end
ins(added_text)
end
local article = args.A
if not article and textsegs[1] then
article = require(en_utilities_module).get_indefinite_article(textsegs[1])
end
if not is_animal then
local gendertext, gender_article = generate_given_name_genders(lang, genders)
article = article or gender_article
ins(gendertext)
ins(" ")
end
ins((numdimofs > 1 or numaugofs > 1 or force_plural) and "[[wikt:given name|given names]]" or "[[wikt:given name|given name]]")
article = article or "a" -- if no article set yet, it's "a" based on "given name"
if langcode == "en" then
article = mw.getContentLanguage():ucfirst(article)
end


table.insert(textsegs, article .. " ")
if numdims > 0 then
table.insert(textsegs,
(dimtype and dimtype .. " " or "") ..
"diminutive" ..
(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 and "given names" or
"given name")
local need_comma = false
local need_comma = false
if numdims > 0 then
if numdimofs > 0 then
table.insert(textsegs, " " .. dimoftext)
ins(" " .. dimoftext)
need_comma = not is_animal
elseif numaugofs > 0 then
ins(" " .. augoftext)
need_comma = not is_animal
elseif xlittext ~= "" then
ins(", " .. xlittext)
need_comma = true
need_comma = true
elseif xlittext ~= "" then
end
table.insert(textsegs, ", " .. xlittext)
 
if is_animal then
if need_comma then
ins(",")
end
need_comma = true
need_comma = true
ins(" for ")
local gendertext, gender_article = generate_given_name_genders(lang, genders)
ins(gender_article)
ins(" ")
ins(gendertext)
end
end
local from_catparts = {}
local from_catparts = {}
if #args.from > 0 then
if args.from then
if need_comma then
if need_comma then
table.insert(textsegs, ",")
ins(",")
end
end
need_comma = true
need_comma = true
table.insert(textsegs, " " .. fetch_typetext("fromtype"))
ins(" " .. fetch_typetext("fromtype"))
local textseg, this_catparts = get_fromtext(lang, args)
local textseg, this_catparts = get_fromtext(lang, args)
for _, catpart in ipairs(this_catparts) do
for _, catpart in ipairs(this_catparts) do
m_table.insertIfNot(from_catparts, catpart)
m_table.insertIfNot(from_catparts, catpart)
end
end
table.insert(textsegs, textseg)
ins(textseg)
end
end
if meaningtext ~= "" then
if meaningtext ~= "" then
if need_comma then
if need_comma then
table.insert(textsegs, ",")
ins(",")
end
end
need_comma = true
need_comma = true
table.insert(textsegs, " " .. fetch_typetext("meaningtype") .. "meaning " .. meaningtext)
ins(" " .. fetch_typetext("meaningtype") .. "meaning " .. meaningtext)
end
end
if args.origin then
if args.origin then
if need_comma then
if need_comma then
table.insert(textsegs, ",")
ins(",")
end
end
need_comma = true
need_comma = true
table.insert(textsegs, " of " .. args.origin .. " origin")
ins(" of " .. args.origin .. " origin")
end
end
if args.usage then
if args.usage then
if need_comma then
if need_comma then
table.insert(textsegs, ",")
ins(",")
end
end
need_comma = true
need_comma = true
table.insert(textsegs, " of " .. args.usage .. " usage")
ins(" of " .. args.usage .. " usage")
end
end
if varoftext ~= "" then
if varoftext ~= "" then
table.insert(textsegs, ", " ..fetch_typetext("varoftype") .. "variant of " .. varoftext)
ins(", " ..fetch_typetext("varoftype") .. "variant of " .. varoftext)
end
if clipoftext ~= "" then
ins(", " .. fetch_typetext("clipoftype") .. "clipping of " .. clipoftext)
end
end
if blendtext ~= "" then
if blendtext ~= "" then
table.insert(textsegs, ", " .. fetch_typetext("blendtype") .. "blend of " .. blendtext)
ins(", " .. fetch_typetext("blendtype") .. "blend of " .. blendtext)
end
end
if args.popular then
if args.popular then
table.insert(textsegs, ", " .. fetch_typetext("populartype") .. "popular " .. args.popular)
ins(", " .. fetch_typetext("populartype") .. "popular " .. args.popular)
end
end
if mtext ~= "" then
if mtext ~= "" then
table.insert(textsegs, ", " .. fetch_typetext("mtype") .. "masculine equivalent " .. mtext)
ins(", " .. fetch_typetext("mtype") .. "masculine equivalent " .. mtext)
end
end
if ftext ~= "" then
if ftext ~= "" then
table.insert(textsegs, ", " .. fetch_typetext("ftype") .. "feminine equivalent " .. ftext)
ins(", " .. fetch_typetext("ftype") .. "feminine equivalent " .. ftext)
end
end
if eqtext ~= "" then
if eqtext ~= "" then
table.insert(textsegs, ", " .. fetch_typetext("eqtype") .. "equivalent to " .. eqtext)
ins(", " .. fetch_typetext("eqtype") .. "equivalent to " .. eqtext)
end
end
if args.q then
if args.addl then
table.insert(textsegs, ", " .. args.q)
if args.addl:find("^;") then
ins(args.addl)
elseif args.addl:find("^_") then
ins(" " .. args.addl:sub(2))
else
ins(", " .. args.addl)
end
end
end
if varformtext ~= "" then
if varformtext ~= "" then
table.insert(textsegs, "; variant form" .. (numvarforms > 1 and "s" or "") .. " " .. varformtext)
ins("; " .. fetch_typetext("varformtype") .. "variant form" .. (numvarforms > 1 and "s" or "") .. " " ..
varformtext)
end
end
if dimformtext ~= "" then
if dimformtext ~= "" then
table.insert(textsegs, "; diminutive form" .. (numdimforms > 1 and "s" or "") .. " " .. dimformtext)
ins("; " .. fetch_typetext("dimformtype") .. "diminutive form" .. (numdimforms > 1 and "s" or "") .. " " ..
dimformtext)
end
if augformtext ~= "" then
ins("; " .. fetch_typetext("augformtype") .. "augmentative form" .. (numaugforms > 1 and "s" or "") .. " " ..
augformtext)
end
end
table.insert(textsegs, "</span>")
textsegs = "<span class='use-with-mention'>" .. article .. " " .. table.concat(textsegs) .. "</span>"


local categories = {}
local categories = {}
local langname = lang:getCanonicalName() .. " "
local langname = lang:getCanonicalName() .. " "
local function insert_cats(isdim)
local function insert_cats(dimaugof)
if isdim == "" then
if dimaugof == "" and genders[1].props.type == "human" then
-- No category such as "English diminutives of given names"
-- No category such as "English diminutives of given names"
table.insert(categories, langname .. isdim .. "given names")
table.insert(categories, langname .. "given names")
end
end
local function insert_cats_gender(g)
local function insert_cat(cat)
if g ~= "male" and g ~= "female" and g ~= "unisex" then
table.insert(categories, langname .. dimaugof .. cat)
error("Unrecognized gender: " .. g)
for _, catpart in ipairs(from_catparts) do
table.insert(categories, langname .. dimaugof .. cat .. " from " .. catpart)
end
end
if g == "unisex" then
end
insert_cats_gender("male")
for _, spec in ipairs(genders) do
insert_cats_gender("female")
local typ = spec.type
if spec.props.track then
track(typ)
end
end
table.insert(categories, langname .. isdim .. g .. " given names")
local cats = get_given_name_cats(spec.type, spec.props)
for _, catpart in ipairs(from_catparts) do
for _, cat in ipairs(cats) do
table.insert(categories, langname .. isdim .. g .. " given names from " .. catpart)
insert_cat(cat)
end
end
end
insert_cats_gender(args.gender)
if args["or"] then
insert_cats_gender(args["or"])
end
end
end
end
insert_cats("")
insert_cats("")
if numdims > 0 then
if numdimofs > 0 then
insert_cats("diminutives of ")
insert_cats("diminutives of ")
elseif numaugofs > 0 then
insert_cats("augmentatives of ")
end
end


return table.concat(textsegs, "") ..
return textsegs .. m_utilities.format_categories(categories, lang, args.sort, nil, force_cat)
m_utilities.format_categories(categories, lang, args.sort, nil, force_cat)
end
end


-- The entry point for {{name translit}} and {{name respelling}}.
-- The entry point for {{surname}}, {{patronymic}} and {{matronymic}}.
function export.name_translit(frame)
function export.surname(frame)
    local iparams = {
local iargs = require("Module:parameters").process(frame.args, {
        ["desctext"] = {required = true},
["type"] = {required = true, set = {"surname", "patronymic", "matronymic"}},
    }
})
    local iargs = require("Module:parameters").process(frame.args, iparams)


local parent_args = frame:getParent().args
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:" .. iargs.type .. "]] because a trailing " ..
"period is no longer added by default; if you want it, add it explicitly after the template")
end


local params = {
local lang_index = compat and "lang" or 1
[1] = { required = true, default = "en" },
[2] = { required = true, default = "ru" },
local list = {list = true}
[3] = { list = true },
local gender_arg = iargs.type == "surname" and "g" or 1 + offset
["type"] = { required = true, list = true, default = "patronymic" },
local adj_arg = iargs.type == "surname" and 1 + offset or 2 + offset
["alt"] = { list = true, allow_holes = true },
local args = require("Module:parameters").process(parent_args, {
["t"] = { list = true, allow_holes = true },
[lang_index] = {required = true, type = "language", template_default = "und"},
["gloss"] = { list = true, alias_of = "t", allow_holes = true },
[gender_arg] = iargs.type == "surname" and true or {required = true, template_default = "unknown"}, -- gender(s)
["tr"] = { list = true, allow_holes = true },
[adj_arg] = true, -- adjective/qualifier
["ts"] = { list = true, allow_holes = true },
["usage"] = true,
["id"] = { list = true, allow_holes = true },
["origin"] = true,
["sc"] = { list = true, allow_holes = true },
["popular"] = true,
["g"] = { list = true, allow_holes = true },
["populartype"] = true,
["q"] = { list = true, allow_holes = true },
["meaning"] = list,
["xlit"] = { list = true, allow_holes = true },
["meaningtype"] = true,
["eq"] = { list = true, allow_holes = true },
["parent"] = true,
["dim"] = { type = "boolean" },
["addl"] = true,
["aug"] = { type = "boolean" },
-- initial article: by default A or An (English), a or an (otherwise)
["nocap"] = { type = "boolean" },
["A"] = true,
["sort"] = {},
["sort"] = true,
}
["from"] = true,
["fromtype"] = true,
["xlit"] = true,
["eq"] = true,
["eqtype"] = true,
["varof"] = true,
["varoftype"] = true,
["var"] = {alias_of = "varof"},
["vartype"] = {alias_of = "varoftype"},
["varform"] = true,
["varformtype"] = true,
["clipof"] = true,
["clipoftype"] = true,
["blend"] = true,
["blendtype"] = true,
["m"] = true,
["mtype"] = true,
["f"] = true,
["ftype"] = true,
["nocat"] = {type = "boolean"},
})
local args = require("Module:parameters").process(parent_args, params)
local textsegs = {}
local lang = m_languages.getByCode(args[1], 1)
local lang = args[lang_index]
local sources = {}
local langcode = lang:getCode()
local source_non_etym_langs = {}
 
for _, source in ipairs(rsplit(args[2], "%s*,%s*")) do
local function fetch_typetext(param)
local sourcelang = m_languages.getByCode(source, 2, "allow etym")
return args[param] and args[param] .. " " or ""
table.insert(sources, sourcelang)
table.insert(source_non_etym_langs, m_languages.getNonEtymological(sourcelang))
end
end


local nametypes = {}
local saw_male = false
for _, typearg in ipairs(args["type"]) do
local saw_female = false
for _, ty in ipairs(rsplit(typearg, "%s*,%s*")) do
local genders = {}
if not translit_name_types[ty] then
if args[gender_arg] then
local quoted_types = {}
for _, g in ipairs(require(parse_interface_module).split_on_comma(args[gender_arg])) do
for _, nametype in ipairs(translit_name_type_list) do
if g == "unknown" or g == "unknown gender" or g == "unknown-gender" or g == "?" then
table.insert(quoted_types, "'" .. nametype .. "'")
g = "unknown-gender"
end
track("unknown gender")
error("Unrecognized type '" .. ty .. "': It should be one of " ..
elseif g == "unisex" or g == "common gender" or g == "common-gender" or g == "c" then
m_table.serialCommaJoin(quoted_types, {conj = "or"}))
g = "common-gender"
saw_male = true
saw_female = true
elseif g == "m" or g == "male" then
g = "male"
saw_male = true
elseif g == "f" or g == "female" then
g = "female"
saw_female = true
else
error("Unrecognized gender: " .. g)
end
end
table.insert(nametypes, ty)
table.insert(genders, g)
end
end
 
local adj = args[adj_arg]
local xlittext = join_names(nil, args, "xlit")
local blendtext = join_names(lang, args, "blend", "and")
local varoftext = join_names(lang, args, "varof")
local clipoftext = join_names(lang, args, "clipof")
local mtext = join_names(lang, args, "m")
local ftext = join_names(lang, args, "f")
local parenttext = join_names(lang, args, "parent", nil, "allow explicit lang")
local varformtext, numvarforms = join_names(lang, args, "varform", ", ")
local meaningsegs = {}
for _, meaning in ipairs(args.meaning) do
table.insert(meaningsegs, '“' .. meaning .. '”')
end
if parenttext ~= "" then
local child = saw_male and not saw_female and "son" or saw_female and not saw_male and "daughter" or
"son/daughter"
table.insert(meaningsegs, ("“%s of %s”"):format(child, parenttext))
end
 
local meaningtext = m_table.serialCommaJoin(meaningsegs, {conj = "or"})
local eqtext = get_eqtext(args)
 
local function ins(txt)
table.insert(textsegs, txt)
end
 
ins("<span class='use-with-mention'>")
 
-- 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", "patronymic" or "matronymic" follows. Capitalize "A"/"An" if English.
local article
if args.A then
article = args.A
else
article = #genders > 0 and genders[1] == "unknown-gender" and "an" or
#genders == 0 and adj and require(en_utilities_module).get_indefinite_article(adj) or
"a"
if langcode == "en" then
article = mw.getContentLanguage():ucfirst(article)
end
end
ins(article .. " ")
 
if #genders > 0 then
ins(table.concat(genders, " or ") .. " ")
end
if adj then
ins(adj .. " ")
end
ins("[[wikt:" .. iargs.type .. "|" .. iargs.type .. "]]")
local need_comma = false
if xlittext ~= "" then
ins(", " .. xlittext)
need_comma = true
end
local from_catparts = {}
if args.from then
if need_comma then
ins(",")
end
need_comma = true
ins(" " .. 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
ins(textseg)
end
if meaningtext ~= "" then
if need_comma then
ins(",")
end
need_comma = true
ins(" " .. fetch_typetext("meaningtype") .. "meaning " .. meaningtext)
end
if args.origin then
if need_comma then
ins(",")
end
need_comma = true
ins(" of " .. args.origin .. " origin")
end
if args.usage then
if need_comma then
ins(",")
end
need_comma = true
ins(" of " .. args.usage .. " usage")
end
if varoftext ~= "" then
ins(", " ..fetch_typetext("varoftype") .. "variant of " .. varoftext)
end
if clipoftext ~= "" then
ins(", " .. fetch_typetext("clipoftype") .. "clipping of " .. clipoftext)
end
if blendtext ~= "" then
ins(", " .. fetch_typetext("blendtype") .. "blend of " .. blendtext)
end
if args.popular then
ins(", " .. fetch_typetext("populartype") .. "popular " .. args.popular)
end
if mtext ~= "" then
ins(", " .. fetch_typetext("mtype") .. "masculine equivalent " .. mtext)
end
if ftext ~= "" then
ins(", " .. fetch_typetext("ftype") .. "feminine equivalent " .. ftext)
end
if eqtext ~= "" then
ins(", " .. fetch_typetext("eqtype") .. "equivalent to " .. eqtext)
end
if args.addl then
if args.addl:find("^;") then
ins(args.addl)
elseif args.addl:find("^_") then
ins(" " .. args.addl:sub(2))
else
ins(", " .. args.addl)
end
end
end
if varformtext ~= "" then
ins("; " .. fetch_typetext("varformtype") .. "variant form" ..
(numvarforms > 1 and "s" or "") .. " " .. varformtext)
end
ins("</span>")
local text = table.concat(textsegs, "")
if args.nocat then
return text
end
end


-- Find the maximum index among any of the list parameters, to determine how many names are given.
local categories = {}
local maxmaxindex = #args[3]
local langname = lang:getCanonicalName() .. " "
for k, v in pairs(args) do
local function insert_cats(g)
if type(v) == "table" and v.maxindex and v.maxindex > maxmaxindex then
g = g and g .. " " or ""
maxmaxindex = v.maxindex
table.insert(categories, langname .. g .. iargs.type .. "s")
for _, catpart in ipairs(from_catparts) do
table.insert(categories, langname .. g .. iargs.type .. "s from " .. catpart)
end
end
insert_cats(nil)
local function insert_cats_gender(g)
if g == "unknown-gender" then
return
end
if g == "common-gender" then
insert_cats_gender("male")
insert_cats_gender("female")
end
end
insert_cats(g)
end
for _, g in ipairs(genders) do
insert_cats_gender(g)
end
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 boolean = {type = "boolean"}
local iargs = require("Module:parameters").process(frame.args, {
["desctext"] = {required = true},
["obor"] = boolean,
["foreign_name"] = boolean,
})
local parent_args = frame:getParent().args
local params = {
[1] = {required = true, type = "language", template_default = "en"},
[2] = {required = true, type = "language", sublist = true, template_default = "ru"},
[3] = {list = true, allow_holes = true},
["type"] = {required = true, set = translit_name_type_list, sublist = true, default = "patronymic"},
["dim"] = boolean,
["aug"] = boolean,
["nocap"] = boolean,
["addl"] = true,
["sort"] = true,
["pagename"] = true,
}
local m_param_utils = require(parameter_utilities_module)
local param_mods = m_param_utils.construct_param_mods {
{group = {"link", "q", "l", "ref"}},
{param = {"xlit", "eq"}},
}
local names, args = m_param_utils.parse_list_with_inline_modifiers_and_separate_params {
params = params,
param_mods = param_mods,
raw_args = parent_args,
termarg = 3,
track_module = "names/name translit",
disallow_custom_separators = true,
-- Use the first source language as the language of the specified names.
lang = function(args) return args[2][1] end,
sc = "sc.default",
}
local lang = args[1]
local langcode = lang:getCode()
local sources = args[2]
local pagename = args.pagename or mw.loadData("Module:headword/data").pagename
local textsegs = {}
local textsegs = {}
table.insert(textsegs, "<span class='use-with-mention'>")
local function ins(txt)
table.insert(textsegs, txt)
end
ins("<span class='use-with-mention'>")
local desctext = iargs.desctext
local desctext = iargs.desctext
if not args.nocap then
if langcode == "en" and not args.nocap then
desctext = mw.getContentLanguage():ucfirst(desctext)
desctext = mw.getContentLanguage():ucfirst(desctext)
end
end
table.insert(textsegs, desctext)
ins(desctext .. " ")
table.insert(textsegs, " of ")
if not iargs.foreign_name then
ins("of ")
end
local langsegs = {}
local langsegs = {}
for i, source in ipairs(sources) do
for i, source in ipairs(sources) do
local sourcename = source:getCanonicalName()
local sourcename = source:getCanonicalName()
local function get_source_link()
local function get_source_link()
if args[3][1] then
local term_to_link = names[1] and names[1].term or pagename
return m_links.language_link {
-- We link the language name to either the first specified name or the pagename, in the following
lang = source_non_etym_langs[i], term = args[3][1], alt = sourcename, tr = "-"
-- 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 names[1] and #sources > 1 or (iargs.foreign_name or iargs.obor) and not names[1] then
return m_links.language_link{
lang = sources[i], term = term_to_link, alt = sourcename, tr = "-"
}
}
else
else
Line 650: Line 1,119:
end
end
if i == 1 then
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.
-- 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".
-- Otherwise we say "A transliteration of a LANG surname".
if maxmaxindex > 0 then
if names[1] then
table.insert(langsegs, "the " .. get_source_link())
table.insert(langsegs, "the " .. get_source_link())
else
else
table.insert(langsegs, require("Module:string utilities").add_indefinite_article(sourcename))
table.insert(langsegs, require(en_utilities_module).add_indefinite_article(sourcename))
end
end
else
else
Line 662: Line 1,131:
end
end
end
end
table.insert(textsegs, m_table.serialCommaJoin(langsegs, {conj = "or"}))
local langseg_text = m_table.serialCommaJoin(langsegs, {conj = "or"})
table.insert(textsegs, " " .. m_table.serialCommaJoin(nametypes))
local augdim_text
if args.dim then
if args.dim then
table.insert(textsegs, " diminutive")
augdim_text = " [[wikt:diminutive|diminutive]]"
elseif args.aug then
elseif args.aug then
table.insert(textsegs, " augmentative")
augdim_text = " [[wikt:augmentative|augmentative]]"
else
augdim_text = ""
end
local nametype_linked = {}
for _, nametype in ipairs(args["type"]) do
if nametype == "surname" or nametype == "patronymic" then
table.insert(nametype_linked, "[[wikt:" .. nametype .. "|" .. nametype .. "]]")
elseif nametype == "male given name" then
table.insert(nametype_linked, "male [[wikt:given name|given name]]")
elseif nametype == "female given name" then
table.insert(nametype_linked, "female [[wikt:given name|given name]]")
elseif nametype == "unisex given name" then
table.insert(nametype_linked, "unisex [[wikt:given name|given name]]")
else
table.insert(nametype_linked, nametype)
end
end
local nametype_text = m_table.serialCommaJoin(nametype_linked) .. augdim_text
 
if not iargs.foreign_name then
ins(langseg_text .. " ")
ins(nametype_text)
if names[1] then
ins(" ")
end
else
ins(nametype_text)
ins(" in " .. langseg_text)
if names[1] then
ins(", ")
end
end
end
table.insert(textsegs, " ")
local names = {}


local linked_names = {}
local embedded_comma = false
local embedded_comma = false


for i = 1, maxmaxindex do
for _, name in ipairs(names) do
local sc = require("Module:scripts").getByCode(args["sc"][i], true)
local linked_name = m_links.full_link(name, "term")
if name.q and name.q[1] or name.qq and name.qq[1] or name.l and name.l[1] or name.ll and name.ll[1] or
local terminfo = {
name.refs and name.refs[1] then
lang = source_non_etym_langs[1], term = args[3][i], alt = args["alt"][i], id = args["id"][i], sc = sc,
linked_name = require(pron_qualifier_module).format_qualifiers {
tr = args["tr"][i], ts = args["ts"][i], gloss = args["t"][i],
lang = name.lang,
genders = args["g"][i] and rsplit(args["g"][i], ",") or {}
text = linked_name,
}
q = name.q,
local linked_term = m_links.full_link(terminfo, "term")
qq = name.qq,
if  args["q"][i] then
l = name.l,
linked_term = require("Module:qualifier").format_qualifier(args["q"][i]) .. " " .. linked_term
ll = name.ll,
refs = name.refs,
raw = true,
}
end
end
if args["xlit"][i] then
if name.xlit then
embedded_comma = true
embedded_comma = true
linked_term = linked_term .. ", " .. m_links.language_link({ lang = m_languages.getByCode("en"), term = args["xlit"][i] })
linked_name = linked_name .. ", " .. m_links.language_link { lang = enlang, term = name.xlit }
end
end
if args["eq"][i] then
if name.eq then
embedded_comma = true
embedded_comma = true
linked_term = linked_term .. ", equivalent to " .. m_links.language_link({ lang = m_languages.getByCode("en"), term = args["eq"][i] })
linked_name = linked_name .. ", equivalent to " .. m_links.language_link { lang = enlang, term = name.eq }
end
end
table.insert(names, linked_term)
table.insert(linked_names, linked_name)
end
end


if embedded_comma then
if embedded_comma then
table.insert(textsegs, table.concat(names, "; or of "))
ins(table.concat(linked_names, "; or of "))
else
else
table.insert(textsegs, m_table.serialCommaJoin(names, {conj = "or"}))
ins(m_table.serialCommaJoin(linked_names, {conj = "or"}))
end
if args.addl then
if args.addl:find("^;") then
ins(args.addl)
elseif args.addl:find("^_") then
ins(" " .. args.addl:sub(2))
else
ins(", " .. args.addl)
end
end
end
table.insert(textsegs, "</span>")
ins("</span>")


local categories = {}
local categories = {}
for _, nametype in ipairs(nametypes) do
local function inscat(cat)
local function insert_cats(isdim)
table.insert(categories, lang:getFullName() .. " " .. cat)
end
 
for _, nametype in ipairs(args.type) do
local function insert_cats(dimaugof)
local function insert_cats_type(ty)
local function insert_cats_type(ty)
if ty == "unisex given name" then
if ty == "unisex given name" then
Line 712: Line 1,227:
insert_cats_type("female given name")
insert_cats_type("female given name")
end
end
for i, source in ipairs(sources) do
for _, source in ipairs(sources) do
table.insert(categories, lang:getCode() .. ":" .. source:getCanonicalName() .. " " .. isdim .. ty .. "s")
inscat("renderings of " .. source:getCanonicalName() .. " " .. dimaugof .. ty .. "s")
local sourcelang = source_non_etym_langs[i]
inscat("terms derived from " .. source:getCanonicalName())
if source:getCode() ~= sourcelang:getCode() then
inscat("terms borrowed from " .. source:getCanonicalName())
if iargs.obor then
inscat("orthographic borrowings from " .. source:getCanonicalName())
end
if source:getCode() ~= source:getFullCode() then
-- etymology language
-- etymology language
table.insert(categories, lang:getCode() .. ":" .. sourcelang:getCanonicalName() .. " " .. isdim .. ty .. "s")
inscat("renderings of " .. source:getFullName() .. " " .. dimaugof .. ty .. "s")
end
end
end
end
Line 732: Line 1,251:
end
end


return table.concat(textsegs, "") ..
return table.concat(textsegs, "") .. m_utilities.format_categories(categories, lang, args.sort, nil, force_cat)
m_utilities.format_categories(categories, lang, args.sort, nil, force_cat)
end
end


return export
return export