Wiksiyonaryo
tlwiktionary
https://tl.wiktionary.org/wiki/Wiksiyonaryo:Unang_Pahina
MediaWiki 1.48.0-wmf.3
case-sensitive
Midya
Natatangi
Usapan
Tagagamit
Usapang tagagamit
Wiksiyonaryo
Usapang Wiksiyonaryo
Talaksan
Usapang talaksan
MediaWiki
Usapang MediaWiki
Padron
Usapang padron
Tulong
Usapang tulong
Kategorya
Usapang kategorya
TimedText
TimedText talk
Module
Module talk
Event
Event talk
aklat
0
2799
178628
178593
2026-11-09T06:18:29Z
GinawaSaHapon
3989
/* Salin */ (using [[wikt:en:MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]])
178628
wikitext
text/x-wiki
=={{=tl=}}==
[[Image:Pieni2.jpg|thumb|isang uri ng aklat]]
===Alternatibong pagbaybay===
* {{alt|tl|aclat||obsolete|Abecedario}}
===Pagbigkas===
{{tl-pr|aklát}}
===Pangngalan===
{{tl-noun|aklát|b=+}}
{{wp|}}
# anumang [[koleksiyon]] ng mga [[papel]] na [[bigkis|binigkis]] sa isang bahagi at naglalaman ng [[limbag|nilimbag]] o [[sulat|sinulat]] na [[teksto]], [[larawan]], atbp.
#: {{syn|tl|libro}}
# anumang [[manuskrito|manuskritong]] nilimbag, madalas [[prosa]] kagaya ng [[nobela]], na nilalagyan ng [[pabalat]]
#: {{syn|tl|libro}}
# isang [[bahagi]] ng [[mahaba|mahabang]] [[gawa]] kagaya ng [[Bibliya]]
#: {{syn|tl|tomo|bolyum}}
# {{lb|tl|obsolete}} [[buklat|pagbuklat]] {{lb|tl|ng aklat}}
====Hinangong kataga====
{{col3|tl|aklatan|aklat-pampaaralan|aklat-dasalan|aklat-sanayan|aklat-tuosan|paaklatan|lunsad-aklat|Aklat ni Mormon|aklat-katitikan}}
====Salin====
{{trans-top|manuskrito o koleksiyon}}
* {{=ast=}}: {{t+|ast|llibru|m}}
* {{=es=}}: {{t+|es|libro|m}}, {{t+|es|libraco|m}} {{qualifier|pampanira}}
* {{=gl=}}: {{t+|gl|libro|m}}, {{t+|gl|livro|m}} {{qualifier|reintegrasyonista}}
* {{=ja=}}: {{t+|ja|本|tr=hon}}, {{t+|ja|書籍|tr=shoseki}}, {{t+|ja|ブック|tr=bukku}}
* {{=en=}}: {{t+|en|book}}
* {{=ca=}}: {{t+|ca|llibre|m}}
* {{=la=}}: {{t+|la|liber|m}}
* {{=osp=}}: {{t+|osp|libro|m}}
* {{=pt=}}: {{t+|pt|livro|m}}
* {{=fr=}}: {{t+|fr|livre|m}}
{{trans-bottom}}
{{trans-top|bahagi ng mahabang gawa}}
* {{=en=}}: {{t|en|book}}, {{t|en|volume}}, {{t|en|tome}}
{{trans-bottom}}
====Tingnan din====
* [[buklat]]
===Magbasa pa===
* {{R:Pambansang Diksiyonaryo}}
===Anagrama===
* {{anagrams|tl|a=aaklt|talak|takal|katal|latak|kalat}}
{{C|tl|Aklat|Paglilimbag}}
7w0h3hyp2u0vawkh0eza1s0pzvb48qs
Module:etymology
828
31239
178620
174212
2026-11-09T03:49:08Z
GinawaSaHapon
3989
178620
Scribunto
text/plain
local export = {}
-- For testing
local force_cat = false
local debug_track_module = "Module:debug/track"
local languages_module = "Module:languages"
local links_module = "Module:links"
local pron_qualifier_module = "Module:pron qualifier"
local table_module = "Module:table"
local utilities_module = "Module:utilities"
local concat = table.concat
local insert = table.insert
local new_title = mw.title.new
local function debug_track(...)
debug_track = require(debug_track_module)
return debug_track(...)
end
local function format_categories(...)
format_categories = require(utilities_module).format_categories
return format_categories(...)
end
local function format_qualifiers(...)
format_qualifiers = require(pron_qualifier_module).format_qualifiers
return format_qualifiers(...)
end
local function full_link(...)
full_link = require(links_module).full_link
return full_link(...)
end
local function get_language_data_module_name(...)
get_language_data_module_name = require(languages_module).getDataModuleName
return get_language_data_module_name(...)
end
local function get_link_page(...)
get_link_page = require(links_module).get_link_page
return get_link_page(...)
end
local function language_link(...)
language_link = require(links_module).language_link
return language_link(...)
end
local function serial_comma_join(...)
serial_comma_join = require(table_module).serialCommaJoin
return serial_comma_join(...)
end
local function shallow_copy(...)
shallow_copy = require(table_module).shallowCopy
return shallow_copy(...)
end
local function track(page, code)
local tracking_page = "etymology/" .. page
debug_track(tracking_page)
if code then
debug_track(tracking_page .. "/" .. code)
end
end
local function join_segs(segs, conj)
if not segs[2] then
return segs[1]
elseif conj == "and" or conj == "or" or conj == "at" or conj == "o" then
return serial_comma_join(segs, {conj = conj})
end
local sep
if conj == "," or conj == ";" then
sep = conj .. " "
elseif conj == "/" then
sep = "/"
elseif conj == "~" then
sep = " ~ "
elseif conj then
error(("Internal error: Unrecognized conjunction \"%s\""):format(conj))
else
error(("Internal error: No value supplied for conjunction"):format(conj))
end
return concat(segs, sep)
end
-- Returns true if `lang` is the same as `source`, or a variety of it.
local function lang_is_source(lang, source)
return lang:getCode() == source:getCode() or lang:hasParent(source)
end
-- Format one or more links as specified in `termobjs`, a list of term objects of the format accepted by full_link() in
-- [[Module:links]], additionally with optional qualifiers, labels and references. `conj` is used to join multiple
-- terms and must be specified if there is more than one term. `template_name` is the template name used in debug
-- tracking and must be specified. The return value begins with a space if there is anything to display (which is always
-- the case unless there is a single term with the value "-").
function export.format_links(termobjs, conj, template_name)
for i, termobj in ipairs(termobjs) do
local term = termobj.term
if termobj.lang:hasType("family") then
if term and term ~= "-" then
debug_track(template_name .. "/family-with-term")
end
term = "-"
termobj.term = term
end
template_name = template_name or "derived"
if term == "-" then
--[=[
[[Special:WhatLinksHere/Wiktionary:Tracking/cognate/no-term]]
[[Special:WhatLinksHere/Wiktionary:Tracking/derived/no-term]]
[[Special:WhatLinksHere/Wiktionary:Tracking/borrowed/no-term]]
[[Special:WhatLinksHere/Wiktionary:Tracking/calque/no-term]]
]=]
debug_track(template_name .. "/no-term")
termobjs[i] = ""
else
termobjs[i] = full_link(termobj, "term", nil, "show qualifiers")
end
end
local retval = join_segs(termobjs, conj)
if retval ~= "" then
retval = " " .. retval
end
return retval
end
function export.get_display_and_cat_name(source, raw)
local display, cat_name
if source:getCode() == "und" then
display = "undetermined"
cat_name = "ibang wika" --TLCHANGE "other languages"
elseif source:getCode() == "mul" then
display = raw and "translingual" or "[[w:Translingualism|translingual]]"
cat_name = "Maramihang wika" --TLCHANGE "Translingual"
elseif source:getCode() == "mul-tax" then
display = raw and "taxonomic name" or "[[w:Biological nomenclature|taxonomic name]]"
cat_name = "taxonomic names"
else
display = raw and source:getCanonicalName() or source:makeWikipediaLink()
cat_name = source:getDisplayForm()
end
return display, cat_name
end
function export.insert_source_cat_get_display(data)
local categories, lang, source = data.categories, data.lang, data.source
local display, cat_name = export.get_display_and_cat_name(source, data.raw)
if lang and not data.nocat then
-- Add the category, but only if there is a current language
if not categories then
categories = {}
end
local langname = lang:getFullName()
-- If `lang` is an etym-only language, we need to check both it and its parent full language against `source`.
-- Otherwise if e.g. `lang` is Medieval Latin and `source` is Latin, we'll end up wrongly constructing a
-- category 'Latin terms derived from Latin'.
insert(categories, langname .. (
lang_is_source(lang, source) and " terms borrowed back into " .. cat_name or
--TLCHANGE " " .. (data.borrowing_type or "terms derived") .. " from " .. cat_name
" " .. (data.borrowing_type or "na salitang hinango") .. " mula sa " .. cat_name
))
end
return display, categories
end
function export.format_source(data)
local lang, sort_key = data.lang, data.sort_key
-- [[Special:WhatLinksHere/Wiktionary:Tracking/etymology/sortkey]]
if sort_key then
track("sortkey")
end
local display, categories = export.insert_source_cat_get_display(data)
if lang and not data.nocat then
-- Format categories, but only if there is a current language; {{cog}} currently gets no categories
categories = format_categories(categories, lang, sort_key, nil, data.force_cat or force_cat)
else
categories = ""
end
return "<span class=\"etyl\">" .. display .. categories .. "</span>"
end
--[==[
Format sources for etymology templates such as {{tl|bor}}, {{tl|der}}, {{tl|inh}} and {{tl|cog}}. There may potentially
be more than one source language (except currently {{tl|inh}}, which doesn't support it because it doesn't really
make sense). In that case, all but the last source language is linked to the first term, but only if there is such a
term and this linking makes sense, i.e. either (1) the term page exists after stripping diacritics according to the
source language in question, or (2) the result of stripping diacritics according to the source language in question
results in a different page from the same process applied with the last source language. For example, {{m|ru|соля́нка}}
will link to [[солянка]] but {{m|en|соля́нка}} will link to [[соля́нка]] with an accent, and since they are different
pages, the use of English as a non-final source with term 'соля́нка' will link to [[соля́нка]] even though it doesn't
exist, on the assumption that it is merely a redlink that might exist. If none of the above criteria apply, a non-final
source language will be linked to the Wikipedia entry for the language, just as final source languages always are.
`data` contains the following fields:
* `lang`: The destination language object into which the terms were borrowed, inherited or otherwise derived. Used for
categorization and can be nil, as with {{tl|cog}}.
* `sources`: List of source objects. Most commonly there is only one. If there are multiple, the non-final ones are
handled specially; see above.
* `terms`: List of term objects. Most commonly there is only one. If there are multiple source objects as well as
multiple term objects, the non-final source objects link to the first term object.
* `sort_key`: Sort key for categories. Usually nil.
* `categories`: Categories to add to the page. Additional categories may be added to `categories` based on the source
languages ('''in which case `categories` is destructively modified'''). If `lang` is nil, no categories
will be added.
* `nocat`: Don't add any categories to the page.
* `sourceconj`: Conjunction used to separate multiple source languages. Defaults to {"and"}.
* `borrowing_type`: Borrowing type used in categories, such as {"learned borrowings"}. Defaults to {"terms derived"}.
* `force_cat`: Force category generation on non-mainspace pages.
]==]
function export.format_sources(data)
local lang, sources, terms, borrowing_type, sort_key, categories, nocat =
data.lang, data.sources, data.terms, data.borrowing_type, data.sort_key, data.categories, data.nocat
local term1, sources_n, source_segs, final_link_page = terms[1], #sources, {}
local term1_term, term1_sc = term1.term, term1.sc
if sources_n > 1 and term1_term and term1_term ~= "-" then
final_link_page = get_link_page(term1_term, sources[sources_n], term1_sc)
end
for i, source in ipairs(sources) do
local seg, display_term
if i < sources_n and term1_term and term1_term ~= "-" then
local link_page = get_link_page(term1_term, source, term1_sc)
display_term = (link_page ~= final_link_page) or (link_page and new_title(link_page).exists)
end
-- TODO: if the display forms or transliterations are different, display the terms separately.
if display_term then
local display, this_cats = export.insert_source_cat_get_display{
lang = lang,
source = source,
borrowing_type = borrowing_type,
raw = true,
categories = categories,
nocat = nocat,
}
seg = language_link{
lang = source,
term = term1_term,
alt = display,
tr = "-",
}
if lang and not nocat then
-- Format categories, but only if there is a current language; {{cog}} currently gets no categories
this_cats = format_categories(this_cats, lang, sort_key, nil, data.force_cat or force_cat)
else
this_cats = ""
end
seg = "<span class=\"etyl\">" .. seg .. this_cats .. "</span>"
else
seg = export.format_source{
lang = lang,
source = source,
borrowing_type = borrowing_type,
sort_key = sort_key,
categories = categories,
nocat = nocat,
}
end
insert(source_segs, seg)
end
return join_segs(source_segs, data.sourceconj or "at")
end
-- Internal implementation of {{cognate}}/{{cog}} template.
function export.format_cognate(data)
return export.format_derived{
sources = data.sources,
terms = data.terms,
sort_key = data.sort_key,
sourceconj = data.sourceconj,
conj = data.conj,
template_name = "cognate",
force_cat = data.force_cat,
}
end
-- Internal implementation of {{derived}}/{{der}} template. This is called externally from [[Module:affix]],
-- [[Module:affixusex]] and [[Module:see]] and needs to support qualifiers, labels and references on the outside
-- of the sources for use by those modules.
function export.format_derived(data)
local terms = data.terms
local result = export.format_sources(data) .. export.format_links(terms, data.conj, data.template_name)
local q, qq, l, ll, refs = data.q, data.qq, data.l, data.ll, data.refs
if q and q[1] or qq and qq[1] or l and l[1] or ll and ll[1] or refs and refs[1] then
result = format_qualifiers{
lang = terms[1].lang,
text = result,
q = q,
qq = qq,
l = l,
ll = ll,
refs = refs,
}
end
return result
end
function export.insert_borrowed_cat(categories, lang, source)
if lang_is_source(lang, source) then
return
end
-- If both are the same, we want e.g. [[:Category:English terms borrowed back into English]] not
-- [[:Category:English terms borrowed from English]]; the former is inserted automatically by format_source().
-- The second parameter here doesn't matter as it only affects `display`, which we don't use.
--TLCHANGE insert(categories, lang:getFullName() .. " terms borrowed from " .. select(2, export.get_display_and_cat_name(source, "raw")))
insert(categories, lang:getFullName() .. " na salitang hiniram mula sa " .. select(2, export.get_display_and_cat_name(source, "raw")))
end
-- Internal implementation of {{borrowed}}/{{bor}} template.
function export.format_borrowed(data)
local categories = {}
if not data.nocat then
local lang = data.lang
for _, source in ipairs(data.sources) do
export.insert_borrowed_cat(categories, lang, source)
end
end
data = shallow_copy(data)
data.categories = categories
return export.format_sources(data) .. export.format_links(data.terms, data.conj, "borrowed")
end
do
-- Generate the non-ancestor error message.
local function show_language(lang)
local retval = ("%s (%s)"):format(lang:makeCategoryLink(), lang:getCode())
if lang:hasType("etymology-only") then
retval = retval .. (" (an etymology-only language whose regular parent is %s)"):format(
show_language(lang:getParent()))
end
return retval
end
-- Check that `lang` has `otherlang` (which may be an etymology-only language) as an ancestor. Throw an error if
-- not.
function export.check_ancestor(lang, otherlang)
-- FIXME: I don't know if this function works correctly with etym-only languages in `lang`. I have fixed up
-- the module link code appropriately (June 2024) but the remaining logic is untouched.
if lang:hasAncestor(otherlang) then
-- [[Special:WhatLinksHere/Wiktionary:Tracking/etymology/variety]]
-- Track inheritance from varieties of Latin that shouldn't have any descendants (everything except Old Latin, Classical Latin and Vulgar Latin).
if otherlang:getFullCode() == "la" then
otherlang = otherlang:getCode()
if not (otherlang == "itc-ola" or otherlang == "la-cla" or otherlang == "la-vul") then
track("bad ancestor", otherlang)
end
end
return
end
local ancestors, postscript = lang:getAncestors()
local etym_module_link = lang:hasType("etymology-only") and "[[Module:etymology languages/data]] or " or ""
local module_link = "[[" .. get_language_data_module_name(lang:getFullCode()) .. "]]"
if not ancestors[1] then
postscript = show_language(lang) .. " has no ancestors."
else
local ancestor_list = {}
for _, ancestor in ipairs(ancestors) do
insert(ancestor_list, show_language(ancestor))
end
postscript = ("The ancestor%s of %s %s %s."):format(
ancestors[2] and "s" or "", lang:getCanonicalName(),
ancestors[2] and "are" or "is", concat(ancestor_list, " and "))
end
error(("%s is not set as an ancestor of %s in %s%s. %s")
:format(show_language(otherlang), show_language(lang), etym_module_link, module_link, postscript))
end
end
-- Internal implementation of {{inherited}}/{{inh}} template.
function export.format_inherited(data)
local lang, terms, sort_key, nocat = data.lang, data.terms, data.sort_key, data.nocat
local source = terms[1].lang
local categories = {}
if not nocat then
--TLCHANGE insert(categories, lang:getFullName() .. " terms inherited from " .. source:getCanonicalName())
insert(categories, lang:getFullName() .. " na salitang minana mula sa " .. source:getCanonicalName())
end
export.check_ancestor(lang, source)
return export.format_source{
lang = lang,
source = source,
sort_key = sort_key,
categories = categories,
nocat = nocat,
force_cat = data.force_cat,
} .. export.format_links(terms, data.conj, "inherited")
end
-- Internal implementation of "misc variant" templates such as {{abbrev}}, {{clipping}}, {{reduplication}} and the like.
function export.format_misc_variant(data)
local lang, notext, terms, cats, parts = data.lang, data.notext, data.terms, data.cats, {}
if not notext then
insert(parts, data.text)
end
if terms[1] then
if not notext then
insert(parts, " " .. (data.oftext or "ng") .. " ") --TLCHANGE data.oftext or "of"
end
insert(parts, export.format_links(terms, data.conj, "misc_variant"))
end
local categories = {}
if not data.nocat and cats then
for _, cat in ipairs(cats) do
insert(categories, lang:getFullName() .. " " .. cat)
end
end
if #categories > 0 then
insert(parts, format_categories(categories, lang, data.sort_key, nil, data.force_cat or force_cat))
end
return concat(parts)
end
-- Implementation of miscellaneous templates such as {{unknown}} and {{onomatopoeia}} that have no associated terms.
function export.format_misc_variant_no_term(data)
local parts = {}
if not data.notext then
insert(parts, data.title)
end
if not data.nocat and data.cat then
local lang, categories = data.lang, {}
insert(categories, lang:getFullName() .. " " .. data.cat)
insert(parts, format_categories(categories, lang, data.sort_key, nil, data.force_cat or force_cat))
end
return concat(parts)
end
return export
bx1mx44mtokv24kjmjbtkqdscizmqa6
Module:etymology/templates
828
31240
178618
168150
2026-11-09T03:46:44Z
GinawaSaHapon
3989
178618
Scribunto
text/plain
local export = {}
local require_when_needed = require("Module:require when needed")
local concat = table.concat
local format_categories = require_when_needed("Module:utilities", "format_categories")
local insert = table.insert
local process_params = require_when_needed("Module:parameters", "process")
local trim = mw.text.trim
local lower = mw.ustring.lower
local dump = mw.dumpObject
local etymology_module = "Module:etymology"
local etymology_specialized_module = "Module:etymology/specialized"
local parameter_utilities_module = "Module:parameter utilities"
-- For testing
local force_cat = false
local allowed_conjs = {"at", "o", ",", "/", "~", ";"}
local function parse_etym_args(parent_args, base_params, has_dest_lang)
local m_param_utils = require(parameter_utilities_module)
local param_mods = m_param_utils.construct_param_mods {
{group = {"link", "q", "l", "ref"}},
}
local sourcearg, termarg
if has_dest_lang then
sourcearg, termarg = 2, 3
else
sourcearg, termarg = 1, 2
end
local terms, args = m_param_utils.parse_term_with_inline_modifiers_and_separate_params {
params = base_params,
param_mods = param_mods,
raw_args = parent_args,
termarg = termarg,
track_module = "etymology",
lang = function(args)
return args[sourcearg][#args[sourcearg]]
end,
sc = "sc",
-- Don't do this, doesn't seem to make sense.
-- parse_lang_prefix = true,
make_separate_g_into_list = true,
splitchar = ",",
subitem_param_handling = "last",
}
-- If term param 3= is empty, there will be no terms in terms.terms. To facilitate further code and for
-- compatibility,, insert one. It will display as <small>[Term?]</small>.
if not terms.terms[1] then
terms.terms[1] = {
lang = args[sourcearg][#args[sourcearg]],
sc = args.sc,
}
end
return terms.terms, args
end
function export.parse_2_lang_args(parent_args, has_text, no_family)
local boolean = {type = "boolean"}
local params = {
[1] = {
required = true,
type = "language",
default = "und"
},
[2] = {
required = true,
sublist = true,
type = "language",
family = not no_family,
default = "und"
},
[3] = true,
[4] = {alias_of = "alt"},
[5] = {alias_of = "t"},
["senseid"] = true,
["nocat"] = boolean,
["sort"] = true,
["sourceconj"] = true,
["conj"] = {set = allowed_conjs, default = ","},
}
if has_text then
params["notext"] = boolean
params["nocap"] = boolean
end
return parse_etym_args(parent_args, params, "has dest lang")
end
-- Implementation of deprecated {{etyl}}. Provided to make histories more legible.
function export.etyl(frame)
local params = {
[1] = {required = true, type = "language", default = "und"},
[2] = {type = "language", default = "en"},
["sort"] = {},
}
-- Empty language means English, but "-" means no language. Yes, confusing...
local args = frame:getParent().args
if args[2] and trim(args[2]) == "-" then
params[2] = nil
args = process_params({
[1] = args[1],
["sort"] = args.sort
}, params)
else
args = process_params(args, params)
end
return require(etymology_module).format_source {
lang = args[2],
source = args[1],
sort_key = args.sort,
force_cat = force_cat,
}
end
-- Implementation of {{derived}}/{{der}}.
function export.derived(frame)
local parent_args = frame:getParent().args
local terms, args = export.parse_2_lang_args(parent_args)
return require(etymology_module).format_derived {
lang = args[1],
sources = args[2],
terms = terms,
sort_key = args.sort,
nocat = args.nocat,
sourceconj = args.sourceconj,
conj = args.conj,
template_name = "derived",
force_cat = force_cat,
}
end
-- Implementation of {{borrowed}}/{{bor}}.
function export.borrowed(frame)
local parent_args = frame:getParent().args
local terms, args = export.parse_2_lang_args(parent_args)
return require(etymology_module).format_borrowed {
lang = args[1],
sources = args[2],
terms = terms,
sort_key = args.sort,
nocat = args.nocat,
sourceconj = args.sourceconj,
conj = args.conj,
force_cat = force_cat,
}
end
function export.inherited(frame)
local parent_args = frame:getParent().args
local terms, args = export.parse_2_lang_args(parent_args)
local sources = args[2]
if sources[2] then
-- Because this doesn't really make sense.
error("[[Template:inherited]] doesn't support multiple comma-separated sources")
end
return require(etymology_module).format_inherited {
lang = args[1],
terms = terms,
sort_key = args.sort,
nocat = args.nocat,
conj = args.conj,
force_cat = force_cat,
}
end
function export.cognate(frame)
local params = {
[1] = {
required = true,
sublist = true,
type = "language",
family = true,
default = "und"
},
[2] = true,
[3] = {alias_of = "alt"},
[4] = {alias_of = "t"},
sourceconj = true,
["conj"] = {set = allowed_conjs, default = ","},
sort = true,
}
local parent_args = frame:getParent().args
local terms, args = parse_etym_args(parent_args, params, false)
return require(etymology_module).format_cognate {
sources = args[1],
terms = terms,
sort_key = args.sort,
sourceconj = args.sourceconj,
conj = args.conj,
force_cat = force_cat,
}
end
function export.noncognate(frame)
return export.cognate(frame)
end
-- Supports various specialized types of borrowings, according to `frame.args.bortype`:
-- "learned" = {{lbor}}/{{learned borrowing}}
-- "semi-learned" = {{slbor}}/{{semi-learned borrowing}}
-- "orthographic" = {{obor}}/{{orthographic borrowing}}
-- "unadapted" = {{ubor}}/{{unadapted borrowing}}
-- "calque" = {{cal}}/{{calque}}
-- "partial-calque" = {{pcal}}/{{partial calque}}
-- "semantic-loan" = {{sl}}/{{semantic loan}}
-- "transliteration" = {{translit}}/{{transliteration}}
-- "phono-semantic-matching" = {{psm}}/{{phono-semantic matching}}
function export.specialized_borrowing(frame)
local parent_args = frame:getParent().args
local terms, args = export.parse_2_lang_args(parent_args, "has text")
local m_etymology_specialized = require(etymology_specialized_module)
return m_etymology_specialized.specialized_borrowing {
bortype = frame.args.bortype,
lang = args[1],
sources = args[2],
terms = terms,
sort_key = args.sort,
nocap = args.nocap,
notext = args.notext,
nocat = args.nocat,
sourceconj = args.sourceconj,
conj = args.conj,
senseid = args.senseid,
force_cat = force_cat,
}
end
-- Implementation of miscellaneous templates such as {{abbrev}}, {{back-formation}}, {{clipping}}, {{ellipsis}},
-- {{rebracketing}} and {{reduplication}} that have a single associated term.
function export.misc_variant(frame)
local iparams = {
["ignore-params"] = true,
text = {required = true},
oftext = true,
cat = {list = true}, -- allow and compress holes
conj = true,
}
local iargs = process_params(frame.args, iparams)
local boolean = {type = "boolean"}
local params = {
[1] = {required = true, type = "language", default = "und"},
[2] = true,
[3] = {alias_of = "alt"},
[4] = {alias_of = "t"},
nocap = boolean, -- should be processed in the template itself
notext = boolean,
nocat = boolean,
conj = {set = allowed_conjs},
sort = true,
}
-- |ignore-params= parameter to module invocation specifies
-- additional parameter names to allow in template invocation, separated by
-- commas. They must consist of ASCII letters or numbers or hyphens.
local ignore_params = iargs["ignore-params"]
if ignore_params then
ignore_params = trim(ignore_params)
if not ignore_params:match("^[%w%-,]+$") then
error("Invalid characters in |ignore-params=: " .. ignore_params:gsub("[%w%-,]+", ""))
end
for param in ignore_params:gmatch("[%w%-]+") do
if params[param] then
error("Duplicate param |" .. param
.. " in |ignore-params=: already specified in params")
end
params[param] = true
end
end
local m_param_utils = require(parameter_utilities_module)
local param_mods = m_param_utils.construct_param_mods {
{group = {"link", "q", "l", "ref"}},
}
local parent_args = frame:getParent().args
local terms, args = m_param_utils.parse_term_with_inline_modifiers_and_separate_params {
params = params,
param_mods = param_mods,
raw_args = parent_args,
termarg = 2,
track_module = "etymology",
lang = 1,
sc = "sc",
parse_lang_prefix = true,
make_separate_g_into_list = true,
splitchar = ",",
subitem_param_handling = "last",
}
return require(etymology_module).format_misc_variant {
lang = args[1],
notext = args.notext,
text = iargs.text,
oftext = iargs.oftext,
terms = terms.terms,
sort_key = args.sort,
conj = args.conj or iargs.conj or "and",
nocat = args.nocat,
cats = iargs.cat,
force_cat = force_cat,
}
end
-- Implementation of miscellaneous templates such as {{doublet}} that can take multiple terms. Doesn't handle {{blend}}
-- or {{univerbation}}, which display + signs between elements and use compound_like in [[Module:affix/templates]].
function export.misc_variant_multiple_terms(frame)
local iparams = {
text = {required = true},
oftext = true,
cat = {list = true}, -- allow and compress holes
conj = true,
}
local iargs = process_params(frame.args, iparams)
local boolean = {type = "boolean"}
local params = {
[1] = {required = true, type = "language", template_default = "und"},
[2] = {list = true, allow_holes = true},
nocap = boolean, -- should be processed in the template itself
notext = boolean,
nocat = boolean,
conj = {set = allowed_conjs},
sort = true,
}
local m_param_utils = require(parameter_utilities_module)
local param_mods = m_param_utils.construct_param_mods {
-- We want to require an index for all params.
{default = true, require_index = true},
{group = {"link", "q", "l", "ref"}},
}
local parent_args = frame:getParent().args
local terms, args = m_param_utils.parse_list_with_inline_modifiers_and_separate_params {
params = params,
param_mods = param_mods,
raw_args = parent_args,
termarg = 2,
parse_lang_prefix = true,
track_module = "etymology-templates-doublet",
disallow_custom_separators = true,
-- For compatibility, we need to not skip completely unspecified items. It is common, for example, to do
-- {{suffix|lang||foo}} to generate "+ -foo".
dont_skip_items = true,
lang = 1,
sc = "sc.default",
}
return require(etymology_module).format_misc_variant {
lang = args[1],
notext = args.notext,
text = iargs.text,
oftext = iargs.oftext,
terms = terms,
sort_key = args.sort,
conj = args.conj or iargs.conj or "and",
nocat = args.nocat,
cats = iargs.cat,
force_cat = force_cat,
}
end
-- Implementation of miscellaneous templates such as {{unknown}} that have no associated terms.
do
local function get_args(frame)
local boolean = {type = "boolean"}
local params = {
[1] = {required = true, type = "language", default = "und"},
["title"] = true,
["nocap"] = boolean, -- should be processed in the template itself
["notext"] = boolean,
["nocat"] = boolean,
["sort"] = true,
}
if frame.args.title2_alias then
params[2] = {alias_of = "title"}
end
return process_params(frame:getParent().args, params)
end
function export.misc_variant_no_term(frame)
local args = get_args(frame)
return require(etymology_module).format_misc_variant_no_term {
lang = args[1],
notext = args.notext,
title = args.title or frame.args.text,
nocat = args.nocat,
cat = frame.args.cat,
sort_key = args.sort,
force_cat = force_cat,
}
end
-- This function works similarly to misc_variant_no_term(), but with some automatic linking to the glossary in
-- `title`.
function export.onomatopoeia(frame)
local args = get_args(frame)
local title = args.title
if title and (lower(title) == "imitative" or lower(title) == "imitation") then
title = "[[Appendix:Glossary#imitative|" .. title .. "]]"
end
return require(etymology_module).format_misc_variant_no_term {
lang = args[1],
notext = args.notext,
title = title or frame.args.text,
nocat = args.nocat,
cat = frame.args.cat,
sort_key = args.sort,
force_cat = force_cat,
}
end
end
return export
r9tjd3bj9iqbx8bwujnzfpf197skx50
178619
178618
2026-11-09T03:47:52Z
GinawaSaHapon
3989
178619
Scribunto
text/plain
local export = {}
local require_when_needed = require("Module:require when needed")
local concat = table.concat
local format_categories = require_when_needed("Module:utilities", "format_categories")
local insert = table.insert
local process_params = require_when_needed("Module:parameters", "process")
local trim = mw.text.trim
local lower = mw.ustring.lower
local dump = mw.dumpObject
local etymology_module = "Module:etymology"
local etymology_specialized_module = "Module:etymology/specialized"
local parameter_utilities_module = "Module:parameter utilities"
-- For testing
local force_cat = false
local allowed_conjs = {"and", "or", ",", "/", "~", ";"}
local function parse_etym_args(parent_args, base_params, has_dest_lang)
local m_param_utils = require(parameter_utilities_module)
local param_mods = m_param_utils.construct_param_mods {
{group = {"link", "q", "l", "ref"}},
}
local sourcearg, termarg
if has_dest_lang then
sourcearg, termarg = 2, 3
else
sourcearg, termarg = 1, 2
end
local terms, args = m_param_utils.parse_term_with_inline_modifiers_and_separate_params {
params = base_params,
param_mods = param_mods,
raw_args = parent_args,
termarg = termarg,
track_module = "etymology",
lang = function(args)
return args[sourcearg][#args[sourcearg]]
end,
sc = "sc",
-- Don't do this, doesn't seem to make sense.
-- parse_lang_prefix = true,
make_separate_g_into_list = true,
splitchar = ",",
subitem_param_handling = "last",
}
-- If term param 3= is empty, there will be no terms in terms.terms. To facilitate further code and for
-- compatibility,, insert one. It will display as <small>[Term?]</small>.
if not terms.terms[1] then
terms.terms[1] = {
lang = args[sourcearg][#args[sourcearg]],
sc = args.sc,
}
end
return terms.terms, args
end
function export.parse_2_lang_args(parent_args, has_text, no_family)
local boolean = {type = "boolean"}
local params = {
[1] = {
required = true,
type = "language",
default = "und"
},
[2] = {
required = true,
sublist = true,
type = "language",
family = not no_family,
default = "und"
},
[3] = true,
[4] = {alias_of = "alt"},
[5] = {alias_of = "t"},
["senseid"] = true,
["nocat"] = boolean,
["sort"] = true,
["sourceconj"] = true,
["conj"] = {set = allowed_conjs, default = ","},
}
if has_text then
params["notext"] = boolean
params["nocap"] = boolean
end
return parse_etym_args(parent_args, params, "has dest lang")
end
-- Implementation of deprecated {{etyl}}. Provided to make histories more legible.
function export.etyl(frame)
local params = {
[1] = {required = true, type = "language", default = "und"},
[2] = {type = "language", default = "en"},
["sort"] = {},
}
-- Empty language means English, but "-" means no language. Yes, confusing...
local args = frame:getParent().args
if args[2] and trim(args[2]) == "-" then
params[2] = nil
args = process_params({
[1] = args[1],
["sort"] = args.sort
}, params)
else
args = process_params(args, params)
end
return require(etymology_module).format_source {
lang = args[2],
source = args[1],
sort_key = args.sort,
force_cat = force_cat,
}
end
-- Implementation of {{derived}}/{{der}}.
function export.derived(frame)
local parent_args = frame:getParent().args
local terms, args = export.parse_2_lang_args(parent_args)
return require(etymology_module).format_derived {
lang = args[1],
sources = args[2],
terms = terms,
sort_key = args.sort,
nocat = args.nocat,
sourceconj = args.sourceconj,
conj = args.conj,
template_name = "derived",
force_cat = force_cat,
}
end
-- Implementation of {{borrowed}}/{{bor}}.
function export.borrowed(frame)
local parent_args = frame:getParent().args
local terms, args = export.parse_2_lang_args(parent_args)
return require(etymology_module).format_borrowed {
lang = args[1],
sources = args[2],
terms = terms,
sort_key = args.sort,
nocat = args.nocat,
sourceconj = args.sourceconj,
conj = args.conj,
force_cat = force_cat,
}
end
function export.inherited(frame)
local parent_args = frame:getParent().args
local terms, args = export.parse_2_lang_args(parent_args)
local sources = args[2]
if sources[2] then
-- Because this doesn't really make sense.
error("[[Template:inherited]] doesn't support multiple comma-separated sources")
end
return require(etymology_module).format_inherited {
lang = args[1],
terms = terms,
sort_key = args.sort,
nocat = args.nocat,
conj = args.conj,
force_cat = force_cat,
}
end
function export.cognate(frame)
local params = {
[1] = {
required = true,
sublist = true,
type = "language",
family = true,
default = "und"
},
[2] = true,
[3] = {alias_of = "alt"},
[4] = {alias_of = "t"},
sourceconj = true,
["conj"] = {set = allowed_conjs, default = ","},
sort = true,
}
local parent_args = frame:getParent().args
local terms, args = parse_etym_args(parent_args, params, false)
return require(etymology_module).format_cognate {
sources = args[1],
terms = terms,
sort_key = args.sort,
sourceconj = args.sourceconj,
conj = args.conj,
force_cat = force_cat,
}
end
function export.noncognate(frame)
return export.cognate(frame)
end
-- Supports various specialized types of borrowings, according to `frame.args.bortype`:
-- "learned" = {{lbor}}/{{learned borrowing}}
-- "semi-learned" = {{slbor}}/{{semi-learned borrowing}}
-- "orthographic" = {{obor}}/{{orthographic borrowing}}
-- "unadapted" = {{ubor}}/{{unadapted borrowing}}
-- "calque" = {{cal}}/{{calque}}
-- "partial-calque" = {{pcal}}/{{partial calque}}
-- "semantic-loan" = {{sl}}/{{semantic loan}}
-- "transliteration" = {{translit}}/{{transliteration}}
-- "phono-semantic-matching" = {{psm}}/{{phono-semantic matching}}
function export.specialized_borrowing(frame)
local parent_args = frame:getParent().args
local terms, args = export.parse_2_lang_args(parent_args, "has text")
local m_etymology_specialized = require(etymology_specialized_module)
return m_etymology_specialized.specialized_borrowing {
bortype = frame.args.bortype,
lang = args[1],
sources = args[2],
terms = terms,
sort_key = args.sort,
nocap = args.nocap,
notext = args.notext,
nocat = args.nocat,
sourceconj = args.sourceconj,
conj = args.conj,
senseid = args.senseid,
force_cat = force_cat,
}
end
-- Implementation of miscellaneous templates such as {{abbrev}}, {{back-formation}}, {{clipping}}, {{ellipsis}},
-- {{rebracketing}} and {{reduplication}} that have a single associated term.
function export.misc_variant(frame)
local iparams = {
["ignore-params"] = true,
text = {required = true},
oftext = true,
cat = {list = true}, -- allow and compress holes
conj = true,
}
local iargs = process_params(frame.args, iparams)
local boolean = {type = "boolean"}
local params = {
[1] = {required = true, type = "language", default = "und"},
[2] = true,
[3] = {alias_of = "alt"},
[4] = {alias_of = "t"},
nocap = boolean, -- should be processed in the template itself
notext = boolean,
nocat = boolean,
conj = {set = allowed_conjs},
sort = true,
}
-- |ignore-params= parameter to module invocation specifies
-- additional parameter names to allow in template invocation, separated by
-- commas. They must consist of ASCII letters or numbers or hyphens.
local ignore_params = iargs["ignore-params"]
if ignore_params then
ignore_params = trim(ignore_params)
if not ignore_params:match("^[%w%-,]+$") then
error("Invalid characters in |ignore-params=: " .. ignore_params:gsub("[%w%-,]+", ""))
end
for param in ignore_params:gmatch("[%w%-]+") do
if params[param] then
error("Duplicate param |" .. param
.. " in |ignore-params=: already specified in params")
end
params[param] = true
end
end
local m_param_utils = require(parameter_utilities_module)
local param_mods = m_param_utils.construct_param_mods {
{group = {"link", "q", "l", "ref"}},
}
local parent_args = frame:getParent().args
local terms, args = m_param_utils.parse_term_with_inline_modifiers_and_separate_params {
params = params,
param_mods = param_mods,
raw_args = parent_args,
termarg = 2,
track_module = "etymology",
lang = 1,
sc = "sc",
parse_lang_prefix = true,
make_separate_g_into_list = true,
splitchar = ",",
subitem_param_handling = "last",
}
return require(etymology_module).format_misc_variant {
lang = args[1],
notext = args.notext,
text = iargs.text,
oftext = iargs.oftext,
terms = terms.terms,
sort_key = args.sort,
conj = args.conj or iargs.conj or "and",
nocat = args.nocat,
cats = iargs.cat,
force_cat = force_cat,
}
end
-- Implementation of miscellaneous templates such as {{doublet}} that can take multiple terms. Doesn't handle {{blend}}
-- or {{univerbation}}, which display + signs between elements and use compound_like in [[Module:affix/templates]].
function export.misc_variant_multiple_terms(frame)
local iparams = {
text = {required = true},
oftext = true,
cat = {list = true}, -- allow and compress holes
conj = true,
}
local iargs = process_params(frame.args, iparams)
local boolean = {type = "boolean"}
local params = {
[1] = {required = true, type = "language", template_default = "und"},
[2] = {list = true, allow_holes = true},
nocap = boolean, -- should be processed in the template itself
notext = boolean,
nocat = boolean,
conj = {set = allowed_conjs},
sort = true,
}
local m_param_utils = require(parameter_utilities_module)
local param_mods = m_param_utils.construct_param_mods {
-- We want to require an index for all params.
{default = true, require_index = true},
{group = {"link", "q", "l", "ref"}},
}
local parent_args = frame:getParent().args
local terms, args = m_param_utils.parse_list_with_inline_modifiers_and_separate_params {
params = params,
param_mods = param_mods,
raw_args = parent_args,
termarg = 2,
parse_lang_prefix = true,
track_module = "etymology-templates-doublet",
disallow_custom_separators = true,
-- For compatibility, we need to not skip completely unspecified items. It is common, for example, to do
-- {{suffix|lang||foo}} to generate "+ -foo".
dont_skip_items = true,
lang = 1,
sc = "sc.default",
}
return require(etymology_module).format_misc_variant {
lang = args[1],
notext = args.notext,
text = iargs.text,
oftext = iargs.oftext,
terms = terms,
sort_key = args.sort,
conj = args.conj or iargs.conj or "and",
nocat = args.nocat,
cats = iargs.cat,
force_cat = force_cat,
}
end
-- Implementation of miscellaneous templates such as {{unknown}} that have no associated terms.
do
local function get_args(frame)
local boolean = {type = "boolean"}
local params = {
[1] = {required = true, type = "language", default = "und"},
["title"] = true,
["nocap"] = boolean, -- should be processed in the template itself
["notext"] = boolean,
["nocat"] = boolean,
["sort"] = true,
}
if frame.args.title2_alias then
params[2] = {alias_of = "title"}
end
return process_params(frame:getParent().args, params)
end
function export.misc_variant_no_term(frame)
local args = get_args(frame)
return require(etymology_module).format_misc_variant_no_term {
lang = args[1],
notext = args.notext,
title = args.title or frame.args.text,
nocat = args.nocat,
cat = frame.args.cat,
sort_key = args.sort,
force_cat = force_cat,
}
end
-- This function works similarly to misc_variant_no_term(), but with some automatic linking to the glossary in
-- `title`.
function export.onomatopoeia(frame)
local args = get_args(frame)
local title = args.title
if title and (lower(title) == "imitative" or lower(title) == "imitation") then
title = "[[Appendix:Glossary#imitative|" .. title .. "]]"
end
return require(etymology_module).format_misc_variant_no_term {
lang = args[1],
notext = args.notext,
title = title or frame.args.text,
nocat = args.nocat,
cat = frame.args.cat,
sort_key = args.sort,
force_cat = force_cat,
}
end
end
return export
o84i2nin3san3d1rnyxpzahej7as8js
178621
178619
2026-11-09T03:49:26Z
GinawaSaHapon
3989
178621
Scribunto
text/plain
local export = {}
local require_when_needed = require("Module:require when needed")
local concat = table.concat
local format_categories = require_when_needed("Module:utilities", "format_categories")
local insert = table.insert
local process_params = require_when_needed("Module:parameters", "process")
local trim = mw.text.trim
local lower = mw.ustring.lower
local dump = mw.dumpObject
local etymology_module = "Module:etymology"
local etymology_specialized_module = "Module:etymology/specialized"
local parameter_utilities_module = "Module:parameter utilities"
-- For testing
local force_cat = false
local allowed_conjs = {"and", "at", "o", "or", ",", "/", "~", ";"}
local function parse_etym_args(parent_args, base_params, has_dest_lang)
local m_param_utils = require(parameter_utilities_module)
local param_mods = m_param_utils.construct_param_mods {
{group = {"link", "q", "l", "ref"}},
}
local sourcearg, termarg
if has_dest_lang then
sourcearg, termarg = 2, 3
else
sourcearg, termarg = 1, 2
end
local terms, args = m_param_utils.parse_term_with_inline_modifiers_and_separate_params {
params = base_params,
param_mods = param_mods,
raw_args = parent_args,
termarg = termarg,
track_module = "etymology",
lang = function(args)
return args[sourcearg][#args[sourcearg]]
end,
sc = "sc",
-- Don't do this, doesn't seem to make sense.
-- parse_lang_prefix = true,
make_separate_g_into_list = true,
splitchar = ",",
subitem_param_handling = "last",
}
-- If term param 3= is empty, there will be no terms in terms.terms. To facilitate further code and for
-- compatibility,, insert one. It will display as <small>[Term?]</small>.
if not terms.terms[1] then
terms.terms[1] = {
lang = args[sourcearg][#args[sourcearg]],
sc = args.sc,
}
end
return terms.terms, args
end
function export.parse_2_lang_args(parent_args, has_text, no_family)
local boolean = {type = "boolean"}
local params = {
[1] = {
required = true,
type = "language",
default = "und"
},
[2] = {
required = true,
sublist = true,
type = "language",
family = not no_family,
default = "und"
},
[3] = true,
[4] = {alias_of = "alt"},
[5] = {alias_of = "t"},
["senseid"] = true,
["nocat"] = boolean,
["sort"] = true,
["sourceconj"] = true,
["conj"] = {set = allowed_conjs, default = ","},
}
if has_text then
params["notext"] = boolean
params["nocap"] = boolean
end
return parse_etym_args(parent_args, params, "has dest lang")
end
-- Implementation of deprecated {{etyl}}. Provided to make histories more legible.
function export.etyl(frame)
local params = {
[1] = {required = true, type = "language", default = "und"},
[2] = {type = "language", default = "en"},
["sort"] = {},
}
-- Empty language means English, but "-" means no language. Yes, confusing...
local args = frame:getParent().args
if args[2] and trim(args[2]) == "-" then
params[2] = nil
args = process_params({
[1] = args[1],
["sort"] = args.sort
}, params)
else
args = process_params(args, params)
end
return require(etymology_module).format_source {
lang = args[2],
source = args[1],
sort_key = args.sort,
force_cat = force_cat,
}
end
-- Implementation of {{derived}}/{{der}}.
function export.derived(frame)
local parent_args = frame:getParent().args
local terms, args = export.parse_2_lang_args(parent_args)
return require(etymology_module).format_derived {
lang = args[1],
sources = args[2],
terms = terms,
sort_key = args.sort,
nocat = args.nocat,
sourceconj = args.sourceconj,
conj = args.conj,
template_name = "derived",
force_cat = force_cat,
}
end
-- Implementation of {{borrowed}}/{{bor}}.
function export.borrowed(frame)
local parent_args = frame:getParent().args
local terms, args = export.parse_2_lang_args(parent_args)
return require(etymology_module).format_borrowed {
lang = args[1],
sources = args[2],
terms = terms,
sort_key = args.sort,
nocat = args.nocat,
sourceconj = args.sourceconj,
conj = args.conj,
force_cat = force_cat,
}
end
function export.inherited(frame)
local parent_args = frame:getParent().args
local terms, args = export.parse_2_lang_args(parent_args)
local sources = args[2]
if sources[2] then
-- Because this doesn't really make sense.
error("[[Template:inherited]] doesn't support multiple comma-separated sources")
end
return require(etymology_module).format_inherited {
lang = args[1],
terms = terms,
sort_key = args.sort,
nocat = args.nocat,
conj = args.conj,
force_cat = force_cat,
}
end
function export.cognate(frame)
local params = {
[1] = {
required = true,
sublist = true,
type = "language",
family = true,
default = "und"
},
[2] = true,
[3] = {alias_of = "alt"},
[4] = {alias_of = "t"},
sourceconj = true,
["conj"] = {set = allowed_conjs, default = ","},
sort = true,
}
local parent_args = frame:getParent().args
local terms, args = parse_etym_args(parent_args, params, false)
return require(etymology_module).format_cognate {
sources = args[1],
terms = terms,
sort_key = args.sort,
sourceconj = args.sourceconj,
conj = args.conj,
force_cat = force_cat,
}
end
function export.noncognate(frame)
return export.cognate(frame)
end
-- Supports various specialized types of borrowings, according to `frame.args.bortype`:
-- "learned" = {{lbor}}/{{learned borrowing}}
-- "semi-learned" = {{slbor}}/{{semi-learned borrowing}}
-- "orthographic" = {{obor}}/{{orthographic borrowing}}
-- "unadapted" = {{ubor}}/{{unadapted borrowing}}
-- "calque" = {{cal}}/{{calque}}
-- "partial-calque" = {{pcal}}/{{partial calque}}
-- "semantic-loan" = {{sl}}/{{semantic loan}}
-- "transliteration" = {{translit}}/{{transliteration}}
-- "phono-semantic-matching" = {{psm}}/{{phono-semantic matching}}
function export.specialized_borrowing(frame)
local parent_args = frame:getParent().args
local terms, args = export.parse_2_lang_args(parent_args, "has text")
local m_etymology_specialized = require(etymology_specialized_module)
return m_etymology_specialized.specialized_borrowing {
bortype = frame.args.bortype,
lang = args[1],
sources = args[2],
terms = terms,
sort_key = args.sort,
nocap = args.nocap,
notext = args.notext,
nocat = args.nocat,
sourceconj = args.sourceconj,
conj = args.conj,
senseid = args.senseid,
force_cat = force_cat,
}
end
-- Implementation of miscellaneous templates such as {{abbrev}}, {{back-formation}}, {{clipping}}, {{ellipsis}},
-- {{rebracketing}} and {{reduplication}} that have a single associated term.
function export.misc_variant(frame)
local iparams = {
["ignore-params"] = true,
text = {required = true},
oftext = true,
cat = {list = true}, -- allow and compress holes
conj = true,
}
local iargs = process_params(frame.args, iparams)
local boolean = {type = "boolean"}
local params = {
[1] = {required = true, type = "language", default = "und"},
[2] = true,
[3] = {alias_of = "alt"},
[4] = {alias_of = "t"},
nocap = boolean, -- should be processed in the template itself
notext = boolean,
nocat = boolean,
conj = {set = allowed_conjs},
sort = true,
}
-- |ignore-params= parameter to module invocation specifies
-- additional parameter names to allow in template invocation, separated by
-- commas. They must consist of ASCII letters or numbers or hyphens.
local ignore_params = iargs["ignore-params"]
if ignore_params then
ignore_params = trim(ignore_params)
if not ignore_params:match("^[%w%-,]+$") then
error("Invalid characters in |ignore-params=: " .. ignore_params:gsub("[%w%-,]+", ""))
end
for param in ignore_params:gmatch("[%w%-]+") do
if params[param] then
error("Duplicate param |" .. param
.. " in |ignore-params=: already specified in params")
end
params[param] = true
end
end
local m_param_utils = require(parameter_utilities_module)
local param_mods = m_param_utils.construct_param_mods {
{group = {"link", "q", "l", "ref"}},
}
local parent_args = frame:getParent().args
local terms, args = m_param_utils.parse_term_with_inline_modifiers_and_separate_params {
params = params,
param_mods = param_mods,
raw_args = parent_args,
termarg = 2,
track_module = "etymology",
lang = 1,
sc = "sc",
parse_lang_prefix = true,
make_separate_g_into_list = true,
splitchar = ",",
subitem_param_handling = "last",
}
return require(etymology_module).format_misc_variant {
lang = args[1],
notext = args.notext,
text = iargs.text,
oftext = iargs.oftext,
terms = terms.terms,
sort_key = args.sort,
conj = args.conj or iargs.conj or "and",
nocat = args.nocat,
cats = iargs.cat,
force_cat = force_cat,
}
end
-- Implementation of miscellaneous templates such as {{doublet}} that can take multiple terms. Doesn't handle {{blend}}
-- or {{univerbation}}, which display + signs between elements and use compound_like in [[Module:affix/templates]].
function export.misc_variant_multiple_terms(frame)
local iparams = {
text = {required = true},
oftext = true,
cat = {list = true}, -- allow and compress holes
conj = true,
}
local iargs = process_params(frame.args, iparams)
local boolean = {type = "boolean"}
local params = {
[1] = {required = true, type = "language", template_default = "und"},
[2] = {list = true, allow_holes = true},
nocap = boolean, -- should be processed in the template itself
notext = boolean,
nocat = boolean,
conj = {set = allowed_conjs},
sort = true,
}
local m_param_utils = require(parameter_utilities_module)
local param_mods = m_param_utils.construct_param_mods {
-- We want to require an index for all params.
{default = true, require_index = true},
{group = {"link", "q", "l", "ref"}},
}
local parent_args = frame:getParent().args
local terms, args = m_param_utils.parse_list_with_inline_modifiers_and_separate_params {
params = params,
param_mods = param_mods,
raw_args = parent_args,
termarg = 2,
parse_lang_prefix = true,
track_module = "etymology-templates-doublet",
disallow_custom_separators = true,
-- For compatibility, we need to not skip completely unspecified items. It is common, for example, to do
-- {{suffix|lang||foo}} to generate "+ -foo".
dont_skip_items = true,
lang = 1,
sc = "sc.default",
}
return require(etymology_module).format_misc_variant {
lang = args[1],
notext = args.notext,
text = iargs.text,
oftext = iargs.oftext,
terms = terms,
sort_key = args.sort,
conj = args.conj or iargs.conj or "and",
nocat = args.nocat,
cats = iargs.cat,
force_cat = force_cat,
}
end
-- Implementation of miscellaneous templates such as {{unknown}} that have no associated terms.
do
local function get_args(frame)
local boolean = {type = "boolean"}
local params = {
[1] = {required = true, type = "language", default = "und"},
["title"] = true,
["nocap"] = boolean, -- should be processed in the template itself
["notext"] = boolean,
["nocat"] = boolean,
["sort"] = true,
}
if frame.args.title2_alias then
params[2] = {alias_of = "title"}
end
return process_params(frame:getParent().args, params)
end
function export.misc_variant_no_term(frame)
local args = get_args(frame)
return require(etymology_module).format_misc_variant_no_term {
lang = args[1],
notext = args.notext,
title = args.title or frame.args.text,
nocat = args.nocat,
cat = frame.args.cat,
sort_key = args.sort,
force_cat = force_cat,
}
end
-- This function works similarly to misc_variant_no_term(), but with some automatic linking to the glossary in
-- `title`.
function export.onomatopoeia(frame)
local args = get_args(frame)
local title = args.title
if title and (lower(title) == "imitative" or lower(title) == "imitation") then
title = "[[Appendix:Glossary#imitative|" .. title .. "]]"
end
return require(etymology_module).format_misc_variant_no_term {
lang = args[1],
notext = args.notext,
title = title or frame.args.text,
nocat = args.nocat,
cat = frame.args.cat,
sort_key = args.sort,
force_cat = force_cat,
}
end
end
return export
4wvqr2kkoek5dv9cknt3vkgcyu038h2
Module:languages/data/3/m
828
32628
178624
178568
2026-11-09T06:06:53Z
GinawaSaHapon
3989
178624
Scribunto
text/plain
local m_langdata = require("Module:languages/data")
-- Loaded on demand, as it may not be needed (depending on the data).
local function u(...)
u = require("Module:string utilities").char
return u(...)
end
local c = m_langdata.chars
local p = m_langdata.puaChars
local s = m_langdata.shared
local m = {}
m["maa"] = {
"San Jerónimo Tecóatl Mazatec",
7692927,
"omq-maz",
"Latn",
}
m["mab"] = {
"Yutanduchi Mixtec",
12645448,
"omq-mxt",
"Latn",
}
m["mad"] = {
"Madurese",
36213,
"poz-msa",
"Latn, Java",
}
m["mae"] = {
"Bo-Rukul",
34967,
"nic-ple",
"Latn",
}
m["maf"] = {
"Mafa",
35819,
"cdc-cbm",
"Latn",
}
m["mag"] = {
"Magahi", -- Not to be confused with Magadhi Prakrit (pra-mag)
33728,
"inc-bih",
"Deva, Kthi",
translit = {
Deva = "bho-translit",
Kthi = "bho-Kthi-translit",
},
}
m["mai"] = {
"Maithili",
36109,
"inc-bih",
"Deva, Tirh, Kthi, Newa",
translit = {
Deva = "mai-translit",
Tirh = "mai-Tirh-translit",
Kthi = "bho-Kthi-translit",
},
}
m["maj"] = {
"Jalapa de Díaz Mazatec",
3915999,
"omq-maz",
"Latn",
}
m["mak"] = {
"Makasar",
33643,
"poz-ssw",
"Latn, Bugi, Maka",
}
m["mam"] = {
"Mam",
33467,
"myn",
"Latn",
}
m["man"] = {
"Mandingo",
35772,
"dmn-man",
"Latn",
}
m["maq"] = {
"Chiquihuitlán Mazatec",
5101757,
"omq-maz",
"Latn",
}
m["mas"] = {
"Maasai",
35787,
"sdv-lma",
"Latn",
}
m["mat"] = {
"Matlatzinca",
12953704,
"omq",
"Latn",
}
m["mau"] = {
"Huautla Mazatec",
36230,
"omq-maz",
"Latn",
}
m["mav"] = {
"Sateré-Mawé",
6794475,
"tup",
"Latn",
}
m["maw"] = {
"Mampruli",
35804,
"nic-wov",
"Latn",
}
m["max"] = {
"North Moluccan Malay",
7056136,
"crp",
"Latn",
ancestors = "ms",
}
m["maz"] = {
"Central Mazahua",
36228,
"oto",
"Latn",
}
m["mba"] = {
"Higaunon", --"Higaonon",
5753411,
"mno",
"Latn",
is_official_kwf_name = "https://kwfwikaatkultura.ph/higaunon/", --TLCHANGE
english_name = "Higaonon", --TLCHANGE
}
m["mbb"] = {
"Menuvu", --"Western Bukidnon Manobo",
7987643,
"mno",
"Latn",
is_official_kwf_name = "https://kwfwikaatkultura.ph/menuvu/", --TLCHANGE
english_name = "Western Bukidnon Manobo", --TLCHANGE
}
m["mbc"] = {
"Macushi",
56633,
"sai-pem",
"Latn",
}
m["mbd"] = {
"Manobo Dibabawon", --"Dibabawon Manobo",
18755523,
"mno",
"Latn",
is_official_kwf_name = "https://kwfwikaatkultura.ph/manobo-dibabawon/", --TLCHANGE
english_name = "Dibabawon Manobo", --TLCHANGE
}
m["mbe"] = {
"Molale",
3319444,
"nai-plp",
"Latn",
}
m["mbf"] = {
"Baba Malay",
18642798,
"crp",
"Latn",
ancestors = "ms",
}
m["mbh"] = {
"Mangseng",
6749147,
"poz-ocw",
"Latn",
}
m["mbi"] = {
"Manobo Ilyanen", --"Ilianen Manobo",
14916911,
"mno",
"Latn",
is_official_kwf_name = "https://kwfwikaatkultura.ph/manobo-ilyanen/", --TLCHANGE
english_name = "Ilianen Manobo", --TLCHANGE
}
m["mbj"] = {
"Nadëb",
3335011,
"sai-nad",
"Latn",
}
m["mbk"] = {
"Malol",
6744477,
"poz-ocw",
"Latn",
}
m["mbl"] = {
"Maxakalí",
3029682,
"sai-mje",
"Latn",
}
m["mbm"] = {
"Ombamba",
36407,
"bnt-mbt",
"Latn",
}
m["mbn"] = {
"Macaguán",
3273980,
"sai-guh",
"Latn",
}
m["mbo"] = { -- is, like 'bqz', 'bsi' and 'bss', a dialect of Manenguba
"Mbo (Cameroon)",
36011,
"bnt-mne",
"Latn",
}
m["mbp"] = {
"Wiwa",
3012604,
"cba",
"Latn",
}
m["mbq"] = {
"Maisin",
3448149,
nil,
"Latn",
}
m["mbr"] = {
"Nukak Makú",
3346228,
"sai-nad",
"Latn",
}
m["mbs"] = {
"Manobo Saranggani", --"Sarangani Manobo",
7423093,
"mno",
"Latn",
is_official_kwf_name = "https://kwfwikaatkultura.ph/manobo-saranggani/", --TLCHANGE
english_name = "Sarangani Manobo", --TLCHANGE
}
m["mbt"] = {
"Manobo Matigsalug", --"Matigsalug Manobo",
6787447,
"mno",
"Latn",
is_official_kwf_name = "https://kwfwikaatkultura.ph/manobo-matigsalug/", --TLCHANGE
english_name = "Matigsalug Manobo", --TLCHANGE
}
m["mbu"] = {
"Mbula-Bwazza",
3913324,
"nic-jrn",
"Latn",
}
m["mbv"] = {
"Mbulungish",
36003,
"alv-nal",
"Latn",
}
m["mbw"] = {
"Maring",
3293280,
"ngf-jim",
"Latn",
}
m["mbx"] = {
"Mari (Sepik)",
6760942,
"paa-sep",
"Latn",
}
m["mby"] = {
"Memoni",
4180871,
"inc-snd",
"Gujr, ur-Arab",
}
m["mbz"] = {
"Amoltepec Mixtec",
13583504,
"omq-mxt",
"Latn",
}
m["mca"] = {
"Maca",
3281043,
"sai-mtc",
"Latn",
}
m["mcb"] = {
"Machiguenga",
3915441,
"awd",
"Latn",
}
m["mcc"] = {
"Bitur",
4919173,
"paa-tir",
"Latn",
}
m["mcd"] = {
"Sharanahua",
12953881,
"sai-pan",
"Latn",
}
m["mce"] = {
"Itundujia Mixtec",
12953727,
"omq-mxt",
"Latn",
}
m["mcf"] = {
"Matsés",
2981620,
"sai-pan",
"Latn",
}
m["mcg"] = {
"Mapoyo",
56946,
"sai-map",
"Latn",
}
m["mch"] = {
"Ye'kwana",
3082027,
"sai-car",
"Latn",
sort_key = {
remove_diacritics = "%-%s",
from = {"'", "ñ", "ö", "sh", "ü"},
to = {"’", "n" .. p[1], "o" .. p[1], "s" .. p[1], "u" .. p[1]}
}
}
m["mci"] = {
"Mese",
6821190,
"ngf-san",
"Latn",
}
m["mcj"] = {
"Mvanip",
3913281,
"nic-mmb",
"Latn",
}
m["mck"] = {
"Mbunda",
34170,
"bnt-clu",
"Latn",
}
m["mcl"] = {
"Macaguaje",
6722435,
"sai-tuc",
"Latn",
}
m["mcm"] = {
"Kristang",
2669169,
"crp",
"Latn",
ancestors = "pt",
}
m["mcn"] = {
"Masana",
56668,
"cdc-mas",
}
m["mco"] = {
"Coatlán Mixe",
25559716,
"nai-miz",
"Latn",
}
m["mcp"] = {
"Makaa",
35803,
"bnt-mka",
}
m["mcq"] = {
"Ese",
5397551,
"ngf-koi",
"Latn",
}
m["mcr"] = {
"Menya",
11732444,
"ngf-kme",
"Latn",
}
m["mcs"] = {
"Mambai",
6748872,
"alv-mbm",
}
m["mcu"] = {
"Cameroon Mambila",
19359039,
"nic-mmb",
"Latn",
}
-- mcv (Minanibai) merged into ffi (Foia Foia) per Glottolog
m["mcw"] = {
"Mawa",
3441333,
"cdc-est",
"Latn",
}
m["mcx"] = {
"Mpiemo",
35908,
"bnt-bek",
}
m["mcy"] = {
"South Watut",
12953293,
"poz-ocw",
"Latn",
}
m["mcz"] = {
"Mawan",
11732429,
"ngf-han",
"Latn",
}
m["mda"] = {
"Mada (Nigeria)",
3915843,
"nic-nin",
"Latn",
}
m["mdb"] = {
"Morigi",
6912195,
"paa-kiw",
"Latn",
}
m["mdc"] = {
"Male",
6742927,
"ngf-min",
"Latn",
}
m["mdd"] = {
"Mbum",
36170,
"alv-mbm",
}
m["mde"] = {
"Bura Mabang",
35860,
"ssa",
"Arab, Latn",
}
m["mdf"] = {
"Moksha",
13343,
"urj-mdv",
"Cyrl",
translit = "mdf-translit",
strip_diacritics = {remove_diacritics = c.acute},
override_translit = true,
sort_key = "mdf-sortkey",
}
m["mdg"] = {
"Massalat",
759984,
}
m["mdh"] = {
"Magindanawon", --"Maguindanao",
33717,
"phi",
"Latn, Arab",
is_official_kwf_name = "https://kwfwikaatkultura.ph/magindanawon/", --TLCHANGE
english_name = "Maguindanao", --TLCHANGE
}
m["mdi"] = {
"Mamvu",
3033594,
"csu-mle",
}
m["mdj"] = {
"Mangbetu",
56327,
"csu-maa",
"Latn",
}
m["mdk"] = {
"Mangbutu",
6748877,
"csu-mle",
}
m["mdl"] = {
"Maltese Sign Language",
6744816,
"sgn",
}
m["mdm"] = {
"Mayogo",
6797580,
"nic-nke",
"Latn",
}
m["mdn"] = {
"Mbati",
36165,
"bnt-ngn",
}
m["mdp"] = {
"Mbala",
6799583,
"bnt-pen",
}
m["mdq"] = {
"Mbole",
6799727,
"bnt-mbe",
}
m["mdr"] = {
"Mandar",
35995,
"poz-ssw",
"Bugi, Latn",
}
m["mds"] = {
"Maria",
3448673,
"paa-man",
"Latn",
}
m["mdt"] = {
"Mbere",
36062,
"bnt-mbt",
}
m["mdu"] = {
"Mboko",
36058,
"bnt-mbo",
}
m["mdv"] = {
"Santa Lucía Monteverde Mixtec",
12953722,
"omq-mxt",
"Latn",
}
m["mdw"] = {
"Mbosi",
36035,
"bnt-mbo",
}
m["mdx"] = {
"Dizin",
35313,
"omv-diz",
"Ethi, Latn",
}
m["mdy"] = {
"Maale",
795327,
"omv-ome",
}
m["mdz"] = {
"Suruí Do Pará",
10322149,
"tup-gua",
"Latn",
}
m["mea"] = {
"Menka",
36078,
"nic-grs",
"Latn",
}
m["meb"] = {
"Ikobi-Mena",
11732241,
"paa-tki",
"Latn",
}
m["mec"] = {
"Mara",
6772774,
}
m["med"] = {
"Melpa",
36166,
"ngf-hag",
"Latn",
}
m["mee"] = {
"Mengen",
3305831,
"poz-ocw",
"Latn",
}
m["mef"] = {
"Megam",
6808589,
}
m["meh"] = {
"Southwestern Tlaxiaco Mixtec",
7070686,
"omq-mxt",
"Latn",
}
m["mei"] = {
"Midob",
36007,
"nub",
"Latn",
}
m["mej"] = {
"Meyah",
11732436,
"paa-ebh",
"Latn",
}
m["mek"] = {
"Mekeo",
3304803,
"poz-ocw",
"Latn",
}
m["mel"] = {
"Central Melanau",
18638319,
"poz-swa",
"Latn",
}
m["mem"] = {
"Mangala",
6748664,
}
m["men"] = {
"Mende (Sierra Leone)",
1478672,
"dmn-msw",
"Latn, Mend",
}
m["meo"] = {
"Kedah Malay",
4925684,
"poz-mly",
"Latn, ms-Arab, Thai",
strip_diacritics = {
from = {u(0xF70F)},
to = {"ญ"}
},
sort_key = {Thai = "Thai-sortkey"},
}
m["mep"] = {
"Miriwoong",
3111847,
"aus-jar",
"Latn",
}
m["meq"] = {
"Merey",
3502314,
"cdc-cbm",
"Latn",
}
m["mer"] = {
"Meru",
13313,
"bnt-kka",
"Latn",
}
m["mes"] = {
"Masmaje",
3440448,
}
m["met"] = {
"Mato",
3299190,
"poz-ocw",
"Latn",
}
m["meu"] = {
"Motu",
33516,
"poz-ocw",
"Latn",
}
m["mev"] = {
"Mano",
3913286,
"dmn-mda",
"Latn",
}
m["mew"] = {
"Maaka",
3438764,
"cdc-wst",
"Latn",
}
m["mey"] = {
"Hassaniya Arabic",
56231,
"sem-arb",
"Arab",
}
m["mez"] = {
"Menominee",
13363,
"alg",
"Latn",
sort_key = {remove_diacritics = "·"},
}
m["mfa"] = {
"Pattani Malay",
1199751,
"poz-mly",
"Latn, ms-Arab, Thai",
strip_diacritics = {
from = {u(0xF70F)},
to = {"ญ"}
},
sort_key = {Thai = "Thai-sortkey"},
}
m["mfb"] = {
"Bangka",
3258818,
"poz-mly",
"Latn, Arab",
}
m["mfc"] = {
"Mba",
4286464,
"nic-mbc",
"Latn",
}
m["mfd"] = {
"Mendankwe-Nkwen",
11129537,
"nic-nge",
"Latn",
}
m["mfe"] = {
"Kriyolo ng Maurisyo",
33661,
"crp",
"Latn",
ancestors = "fr",
sort_key = s["roa-oil-sortkey"],
}
m["mff"] = {
"Naki",
36083,
"nic-bbe",
"Latn",
}
m["mfg"] = {
"Mixifore",
3914478,
"dmn-mok",
}
m["mfh"] = {
"Matal",
3501751,
"cdc-cbm",
"Latn",
}
m["mfi"] = {
"Wandala",
3441249,
"cdc-cbm",
"Latn",
}
m["mfj"] = {
"Mefele",
3501871,
"cdc-cbm",
}
m["mfk"] = {
"North Mofu",
56303,
"cdc-cbm",
"Latn",
}
m["mfl"] = {
"Putai",
56291,
}
m["mfm"] = {
"Marghi South",
56248,
}
m["mfn"] = {
"Cross River Mbembe",
3915395,
"nic-uce",
"Latn",
}
m["mfo"] = {
"Mbe",
36075,
"nic-eko",
"Latn",
}
m["mfp"] = {
"Makassar Malay",
12952776,
"qfa-mix",
"Latn",
ancestors = "ms, mak"
}
m["mfq"] = {
"Moba",
19921578,
"nic-grm",
"Latn",
}
m["mfr"] = {
"Marrithiyel",
6773014,
"aus-dal",
"Latn",
}
m["mfs"] = {
"Mexican Sign Language",
3915511,
"sgn",
"Latn", -- when documented
}
m["mft"] = {
"Mokerang",
3319387,
"poz-aay",
"Latn",
}
m["mfu"] = {
"Mbwela",
11004988,
"bnt-clu",
ancestors = "lch",
}
m["mfv"] = {
"Mandjak",
35822,
"alv-pap",
}
m["mfw"] = {
"Mulaha",
6933720,
"paa-kwa",
"Latn",
}
m["mfx"] = {
"Melo",
6813268,
"omv-nom",
}
m["mfy"] = {
"Mayo",
56729,
"azc-trc",
"Latn",
sort_key = {remove_diacritics = c.acute},
}
m["mfz"] = {
"Mabaan",
20526385,
"sdv",
"Latn",
}
m["mga"] = {
"Middle Irish",
36116,
"cel-gae",
"Latn",
ancestors = "sga",
strip_diacritics = {remove_diacritics = c.dotabove .. c.diaer .. "·"},
sort_key = "mga-sortkey",
}
m["mgb"] = {
"Mararit",
56359,
"sdv-tmn",
}
m["mgc"] = {
"Morokodo",
6913216,
"csu-bbk",
"Latn",
}
m["mgd"] = {
"Moru",
6915014,
"csu-mma",
"Latn, Arab",
}
m["mge"] = {
"Mango",
713659,
"csu-sar",
"Latn",
}
m["mgf"] = {
"Maklew",
6739816,
"paa-bul",
"Latn",
}
m["mgg"] = {
"Mpongmpong",
35924,
"bnt-bek",
}
m["mgh"] = {
"Makhuwa-Meetto",
33604,
"bnt-mak",
"Latn",
ancestors = "vmw",
}
m["mgi"] = {
"Jili",
3914497,
"nic-pls",
}
m["mgj"] = {
"Abureni",
3441256,
"nic-cde",
"Latn",
}
m["mgk"] = {
"Mawes",
6794395,
"qfa-dis", -- Papuan; isolate in Glottolog, Foley (2018) and Hammarström (2010); in the Tor-Kwerba languages per
-- Usher (2020)
"Latn",
}
m["mgl"] = {
"Maleu-Kilenge",
3281884,
}
m["mgm"] = {
"Mambae",
35774,
"poz-tim",
"Latn",
}
m["mgn"] = {
"Mbangi",
11017443,
"nic-ngd",
"Latn",
}
m["mgo"] = {
"Meta'",
36054,
"nic-mom",
"Latn",
}
m["mgp"] = {
"Eastern Magar",
12952758,
"sit-gma",
"Deva, Latn",
}
m["mgq"] = {
"Malila",
6743679,
"bnt-mby",
"Latn",
}
m["mgr"] = {
"Mambwe-Lungu",
626210,
"bnt-mwi",
"Latn",
}
m["mgs"] = {
"Manda (Tanzania)",
16939267,
"bnt-bki",
}
m["mgt"] = {
"Mwakai",
11260674,
"paa-wke",
"Latn",
}
m["mgu"] = {
"Mailu",
3278246,
"paa-mal",
"Latn",
}
m["mgv"] = {
"Matengo",
6786446,
"bnt-mbi",
"Latn",
}
m["mgw"] = {
"Matumbi",
6791974,
"bnt-mbi",
"Latn",
}
m["mgy"] = {
"Mbunga",
6799817,
"bnt-kil",
}
m["mgz"] = {
"Mbugwe",
3426367,
"bnt-mra",
}
m["mha"] = {
"Manda (India)",
56760,
"dra-kki",
"Orya",
translit = "kxv-translit",
}
m["mhb"] = {
"Mahongwe",
35816,
"bnt-kel",
}
m["mhc"] = {
"Mocho",
1941682,
"myn",
}
m["mhd"] = {
"Mbugu",
36152,
"qfa-mix",
"Latn",
ancestors = "asa",
}
m["mhe"] = {
"Besisi",
2742262,
"mkh-asl",
"Latn",
}
m["mhf"] = {
"Mamaa",
6745346,
"ngf-era",
"Latn",
}
m["mhg"] = {
"Marrgu",
6772812,
}
m["mhi"] = {
"Ma'di",
56670,
"csu-mma",
"Latn",
strip_diacritics = {remove_diacritics = c.acute .. c.grave .. c.tilde .. c.dotbelow},
}
m["mhj"] = {
"Mogholi",
13336,
"xgn",
"fa-Arab, Latn",
translit = "fa-cls-translit",
strip_diacritics = {
["fa-Arab"] = "ar-stripdiacritics",
},
}
m["mhk"] = {
"Mungaka",
36068,
"nic-nun",
}
m["mhl"] = {
"Mauwake",
6794095,
"ngf-kum",
"Latn",
}
m["mhm"] = {
"Makhuwa-Moniga",
6900145,
"bnt-mak",
}
m["mhn"] = {
"Mòcheno",
268130,
"gmw-hgm",
"Latn",
ancestors = "bar",
sort_key = {remove_diacritics = c.grave},
}
m["mho"] = {
"Mashi",
10962737,
"bnt-kav",
"Latn",
}
m["mhp"] = {
"Balinese Malay",
12473441,
"crp",
"Latn, Bali, ms-Arab",
}
m["mhq"] = {
"Mandan",
1957120,
"sio",
"Latn",
}
m["mhr"] = {
"Eastern Mari",
3906614,
"chm",
"Cyrl",
translit = "chm-translit",
override_translit = true,
strip_diacritics = {remove_diacritics = c.grave .. c.acute},
sort_key = {
from = {"ё", "ҥ", "ӧ", "ӱ"},
to = {"е" .. p[1], "н" .. p[1], "о" .. p[1], "у" .. p[1]}
}
}
m["mhs"] = {
"Buru (Indonesia)",
2928650,
"poz-cma",
"Latn",
}
m["mht"] = {
"Mandahuaca",
6747924,
"awd-nwk",
}
m["mhu"] = {
"Taraon",
56400,
"sit-gsi",
"Latn",
}
m["mhw"] = {
"Mbukushu",
2691548,
"bnt",
"Latn",
}
m["mhx"] = {
"Lhao Vo",
11149315,
"tbq-brm",
"Latn",
}
m["mhy"] = {
"Ma'anyan",
2328761,
"poz-bre",
"Latn",
}
m["mhz"] = {
"Mor (Austronesian)",
2122792,
"poz-hce",
"Latn",
}
m["mia"] = {
"Miami",
56523,
"alg",
"Latn",
}
m["mib"] = {
"Atatláhuca Mixtec",
32093046,
"omq-mxt",
"Latn",
}
m["mic"] = {
"Mi'kmaq",
13321,
"alg-eas",
"Latn",
}
m["mid"] = {
"Mandaic",
6991742,
"sem-ase",
"Mand",
ancestors = "myz",
translit = {
Mand = "Mand-translit",
},
strip_diacritics = {
Mand = "Mand-stripdiacritics",
}
}
m["mie"] = {
"Ocotepec Mixtec",
25559575,
"omq-mxt",
"Latn",
}
m["mif"] = {
"Mofu-Gudur",
1365132,
"cdc-cbm",
"Latn",
}
m["mig"] = {
"San Miguel el Grande Mixtec",
12953719,
"omq-mxt",
"Latn",
}
m["mih"] = {
"Chayuco Mixtec",
13583510,
"omq-mxt",
"Latn",
}
m["mii"] = {
"Chigmecatitlán Mixtec",
12953724,
"omq-mxt",
"Latn",
}
m["mij"] = {
"Mungbam",
34725,
"nic-beb",
"Latn",
}
m["mik"] = {
"Mikasuki",
13316,
"nai-mus",
"Latn",
}
m["mil"] = {
"Peñoles Mixtec",
42411307,
"omq-mxt",
"Latn",
}
m["mim"] = {
"Alacatlatzala Mixtec",
14697894,
"omq-mxt",
"Latn",
}
m["min"] = {
"Minangkabau",
13324,
"poz-mly",
"Latn, Arab",
}
m["mio"] = {
"Pinotepa Nacional Mixtec",
7196415,
"omq-mxt",
"Latn",
}
m["mip"] = {
"Apasco-Apoala Mixtec",
13583505,
"omq-mxt",
"Latn",
}
m["miq"] = {
"Miskito",
1516803,
"nai-min",
"Latn",
strip_diacritics = {remove_diacritics = c.circ},
}
m["mir"] = {
"Isthmus Mixe",
6088873,
"nai-miz",
"Latn",
}
m["mit"] = {
"Southern Puebla Mixtec",
7570345,
"omq-mxt",
"Latn",
}
m["miu"] = {
"Cacaloxtepec Mixtec",
12953723,
"omq-mxt",
"Latn",
}
m["miw"] = {
"Akoye",
3327462,
"ngf-taa",
"Latn",
}
m["mix"] = {
"Mixtepec Mixtec",
6884125,
"omq-mxt",
"Latn",
}
m["miy"] = {
"Ayutla Mixtec",
13583508,
"omq-mxt",
"Latn",
}
m["miz"] = {
"Coatzospan Mixtec",
3317290,
"omq-mxt",
"Latn",
}
m["mjb"] = {
"Makalero",
35729,
"paa-alp",
"Latn",
}
m["mjc"] = {
"San Juan Colorado Mixtec",
12953718,
"omq-mxt",
"Latn",
}
m["mjd"] = {
"Northwest Maidu",
3198700,
"nai-mdu",
"Latn",
}
m["mje"] = {
"Muskum",
3913334,
}
-- mjg "Monguor" is not recognized as a language, but it is a family code
m["mji"] = {
"Kim Mun",
1115317,
"hmx-mie",
"Latn",
}
m["mjj"] = {
"Mawak",
11732427,
"ngf-tib",
"Latn",
}
m["mjk"] = {
"Matukar",
6791963,
"poz-ocw",
"Latn",
}
m["mjl"] = {
"Mandeali",
6747931,
"him",
"Deva, Takr",
translit = "hi-translit",
}
m["mjm"] = {
"Medebur",
6805227,
"poz-ocw",
"Latn",
}
m["mjn"] = {
"Mebu",
6804364,
"ngf-yup",
"Latn",
}
m["mjo"] = {
"Malankuravan",
14916887,
"dra-mal",
}
m["mjp"] = {
"Malapandaram",
10575729,
"dra-tam",
}
m["mjq"] = {
"Malaryan",
12952773,
"dra-mal",
}
m["mjr"] = {
"Malavedan",
12952775,
"dra-mal",
"Mlym",
-- Mlym translit in [[Module:scripts/data]]
}
m["mjs"] = {
"Miship",
3441264,
"cdc-wst",
"Latn",
}
m["mjt"] = {
"Sawriya Paharia",
33907,
"dra-mlo",
"Beng, Deva",
}
m["mju"] = {
"Manna-Dora",
10576453,
"dra-tel",
}
m["mjv"] = {
"Mannan",
3286037,
"dra-tam",
"Mlym, Taml",
translit = {
Taml = "ta-translit",
},
-- Mlym translit in [[Module:scripts/data]]
}
m["mjw"] = {
"Karbi",
56591,
"tbq-kuk",
"Latn",
}
m["mjx"] = {
"Mahali",
12953686,
"mun",
}
m["mjy"] = {
"Mahican",
3182562,
"alg-eas",
"Latn",
}
m["mjz"] = {
"Majhi",
6737786,
"inc-bih",
}
m["mka"] = {
"Mbre",
3450154,
"nic", --unclassified within niger-congo tho
}
m["mkb"] = {
"Mal Paharia",
6583595,
"inc-eas",
"Deva",
}
m["mkc"] = {
"Siliput",
7515090,
"paa-mam",
"Latn",
}
m["mke"] = {
"Mawchi",
21403317,
}
m["mkf"] = {
"Miya",
43328,
"cdc-wst",
"Latn",
}
m["mkg"] = {
"Mak (China)",
3280623,
"qfa-kms",
}
m["mki"] = {
"Dhatki",
32480,
"raj",
"Deva, Mahj, Arab",
}
m["mkj"] = {
"Mokilese",
2335528,
"poz-mic",
"Latn",
}
m["mkk"] = {
"Byep",
35052,
"bnt-mka",
}
m["mkl"] = {
"Mokole",
36047,
"alv-yor",
"Latn",
}
m["mkm"] = {
"Moklen",
3319380,
}
m["mkn"] = {
"Kupang Malay",
18458203,
"crp",
"Latn",
}
m["mko"] = {
"Mingang Doso",
3915382,
"alv-bwj",
}
m["mkp"] = {
"Moikodi",
6894594,
"ngf-yar",
"Latn",
}
m["mkq"] = {
"Bay Miwok",
3460957,
"nai-utn",
"Latn",
}
m["mkr"] = {
"Malas",
11732402,
"ngf-nad",
"Latn",
}
m["mks"] = {
"Silacayoapan Mixtec",
7514027,
"omq-mxt",
"Latn",
}
m["mkt"] = {
"Vamale",
14916907,
"poz-cln",
"Latn",
}
m["mku"] = {
"Konyanka Maninka",
11163298,
"dmn-mnk",
}
m["mkv"] = {
"Mav̋ea",
3073532,
"poz-vnn",
"Latn",
}
m["mkx"] = {
"Kinamiging", --"Cinamiguin Manobo",
12953697,
"mno",
"Latn",
is_official_kwf_name = "https://kwfwikaatkultura.ph/kinamiging/", --TLCHANGE
english_name = "Cinamiguin Manobo", --TLCHANGE
}
m["mky"] = {
"Taba",
3512690,
"poz-hce",
"Latn",
}
m["mkz"] = {
"Makasae",
35782,
"paa-eti",
"Latn",
}
m["mla"] = {
"Tamambo",
1153276,
"poz-vnn",
"Latn",
}
m["mlb"] = {
"Mbule",
35843,
"nic-ymb",
"Latn",
}
m["mlc"] = {
"Caolan",
3446682,
"tai-cho",
"Latn, Hani",
sort_key = {Hani = "Hani-sortkey"},
}
m["mle"] = {
"Manambu",
11732406,
"paa-nnd",
"Latn",
}
m["mlf"] = {
"Mal",
3281057,
"mkh-khm",
}
m["mlh"] = {
"Mape",
6753787,
"ngf-kma",
"Latn",
}
m["mli"] = {
"Malimpung",
12473435,
}
m["mlj"] = {
"Miltu",
3441310,
}
m["mlk"] = {
"Ilwana",
6001357,
"bnt-sab",
}
m["mll"] = {
"Malua Bay",
6744946,
"poz-vnc",
"Latn",
}
m["mlm"] = {
"Mulam",
3092284,
"qfa-kms",
"Latn",
}
m["mln"] = {
"Malango",
3281522,
"poz-sls",
"Latn",
}
m["mlo"] = {
"Mlomp",
36009,
"alv-bak",
}
m["mlp"] = {
"Bargam",
4860543,
"ngf-mad",
"Latn",
}
m["mlq"] = {
"Western Maninkakan",
11028033,
"dmn-wmn",
}
m["mlr"] = {
"Vame",
3515088,
"cdc-cbm",
"Latn",
}
m["mls"] = {
"Masalit",
56557,
"ssa",
}
m["mlu"] = {
"To'abaita",
36645,
"poz-sls",
"Latn",
}
m["mlv"] = {
"Mwotlap",
2475538,
"poz-vnn",
"Latn",
}
m["mlw"] = {
"Moloko",
1965222,
"cdc-cbm",
"Latn",
}
m["mlx"] = {
"Malfaxal",
2157421,
"poz-vnc",
"Latn",
}
m["mlz"] = {
"Malaynon",
18755512,
"phi",
is_official_kwf_name = "https://kwfwikaatkultura.ph/malaynon/", --TLCHANGE
english_name = "Malaynon", --TLCHANGE
}
m["mma"] = {
"Mama",
3913963,
"nic-jrn",
}
m["mmb"] = {
"Momina",
6897297,
}
m["mmc"] = {
"Michoacán Mazahua",
12953705,
"oto",
"Latn",
}
m["mmd"] = {
"Maonan",
3092293,
"qfa-kms",
"Latn",
}
m["mme"] = {
"Tirax",
3276286,
"poz-vnc",
"Latn",
}
m["mmf"] = {
"Mundat",
56263,
"cdc-wst",
"Latn",
}
m["mmg"] = {
"North Ambrym",
2842468,
"poz-vnc",
"Latn",
}
m["mmh"] = {
"Mehináku",
3501838,
"awd",
"Latn",
}
m["mmi"] = {
"Hember Avu",
6940113,
"ngf-tib",
"Latn",
}
m["mmj"] = {
"Majhwar",
6737795,
}
m["mmk"] = {
"Mukha-Dora",
6933447,
}
m["mml"] = {
"Man Met",
3194984,
"mkh-pal",
}
m["mmm"] = {
"Maii",
6735599,
"poz-vnc",
"Latn",
}
m["mmn"] = {
"Mamanwa", --"Mamanwa",
3206623,
"phi",
"Latn",
is_official_kwf_name = "https://kwfwikaatkultura.ph/mamanwa/", --TLCHANGE
english_name = "Mamanwa", --TLCHANGE
}
m["mmo"] = {
"Mangga Buang",
12952294,
"poz-ocw",
"Latn",
}
m["mmp"] = {
"Musan",
2605703,
"paa-amu",
"Latn",
}
m["mmq"] = {
"Aisi",
6940074,
"ngf-ais",
"Latn",
}
m["mmr"] = {
"Western Xiangxi Miao",
3307901,
"hmn",
"Latn",
}
m["mmt"] = {
"Malalamai",
3281496,
"poz-ocw",
"Latn",
}
m["mmu"] = {
"Mmaala",
13123461,
"nic-ymb",
"Latn",
}
m["mmv"] = {
"Miriti",
6873567,
"sai-tuc",
"Latn",
}
m["mmw"] = {
"Emae",
3051961,
"poz-pnp",
"Latn",
}
m["mmx"] = {
"Madak",
3275205,
"poz-ocw",
"Latn",
}
m["mmy"] = {
"Migaama",
56259,
"cdc-est",
"Latn",
}
m["mmz"] = {
"Mabaale",
11003249,
"bnt-ngn",
}
m["mna"] = {
"Mbula",
3303572,
"poz-ocw",
"Latn",
}
m["mnb"] = {
"Muna",
6935584,
"poz-mun",
"Latn",
}
m["mnc"] = {
"Manchu",
33638,
"tuw-jrc",
"mnc-Mong, Latn",
ancestors = "juc",
-- mnc-Mong translit in [[Module:scripts/data]]
}
m["mnd"] = {
"Mondé",
6898840,
"tup",
"Latn",
}
m["mne"] = {
"Naba",
760732,
"csu-bgr",
}
m["mnf"] = {
"Mundani",
35839,
"nic-mom",
"Latn",
}
m["mng"] = {
"Eastern Mnong",
12953747,
"mkh-ban",
"Latn, Khmr",
}
m["mnh"] = {
"Mono (Congo)",
33501,
"bad-cnt",
"Latn",
}
m["mni"] = {
"Manipuri",
33868,
"sit",
"Mtei, Beng",
ancestors = "omp",
translit = {Mtei = "Mtei-translit"},
}
m["mnj"] = {
"Munji",
33639,
"ira-mny",
"Arab",
}
m["mnk"] = {
"Mandinka",
33678,
"dmn-wmn",
"Latn, Arab, Nkoo",
}
m["mnl"] = {
"Tiale",
6744350,
"poz-vnn",
"Latn",
}
m["mnm"] = {
"Mapena",
11732415,
"ngf-dag",
"Latn",
}
m["mnn"] = {
"Southern Mnong",
23857582,
"mkh-ban",
}
m["mnp"] = {
"Northern Min",
36457,
"zhx-inm",
"Hants",
generate_forms = "zh-generateforms",
translit = "zh-translit",
sort_key = "Hani-sortkey",
}
m["mnq"] = {
"Minriq",
2742268,
"mkh-asl",
"Latn",
}
m["mnr"] = {
"Mono (California)",
33591,
"azc-num",
"Latn",
}
m["mnt"] = {
"Maykulan",
3915696,
"aus-pam",
"Latn",
}
m["mnu"] = {
"Mer",
6817854,
"paa-mai",
"Latn",
}
m["mnv"] = {
"Rennellese",
3397346,
"poz-pnp",
"Latn",
}
m["mnw"] = {
"Mon",
13349,
"mkh-mnc",
"Mymr",
ancestors = "mkh-mmn",
sort_key = {
from = {"ျ", "ြ", "ွ", "ှ", "ၞ", "ၟ", "ၠ", "ၚ", "ဿ"},
to = {"္ယ", "္ရ", "္ဝ", "္ဟ", "္န", "္မ", "္လ", "င", "သ္သ"}
},
}
m["mnx"] = {
"Sougb",
3507964,
"paa-ebh",
"Latn",
}
m["mny"] = {
"Manyawa",
11002622,
"bnt-mak",
ancestors = "vmw",
}
m["mnz"] = {
"Moni",
6899857,
"ngf-pan",
"Latn",
}
m["moa"] = {
"Mwan",
3320111,
"dmn-nbe",
"Latn",
}
m["moc"] = {
"Mocoví",
3027906,
"sai-guc",
"Latn",
}
m["mod"] = {
"Mobilian",
13333,
"crp",
"Latn",
ancestors = "cho, cic",
}
m["moe"] = {
"Montagnais",
13351,
"alg",
"Latn",
ancestors = "cr",
strip_diacritics = {remove_diacritics = c.macron},
}
m["mog"] = {
"Mongondow",
3058458,
"phi",
"Latn",
}
m["moh"] = {
"Mohawk",
13339,
"iro-nor",
"Latn",
ancestors = "iro-omo",
}
m["moi"] = {
"Mboi",
3914417,
"alv-yun",
}
m["moj"] = {
"Monzombo",
11154772,
"nic-nkk",
"Latn",
}
m["mok"] = {
"Morori",
6913275,
}
m["mom"] = {
"Monimbo",
56542,
}
m["moo"] = {
"Monom",
6901726,
"mkh-ban",
}
m["mop"] = {
"Mopan Maya",
36183,
"myn",
"Latn",
}
m["moq"] = {
"Mor (Papuan)",
11732468,
"qfa-dis", -- Papuan; isolate in Glottolog and Palmer (2018); top-level TNG in Ross (2005), in Berau Gulf (under
-- TNG) in Usher (2020)
}
m["mor"] = {
"Moro",
36172,
"alv-hei",
"Latn",
}
m["mos"] = {
"Moore",
36096,
"nic-mre",
"Latn",
}
m["mot"] = {
"Barí",
2886281,
"cba",
"Latn",
}
m["mou"] = {
"Mogum",
3440473,
"cdc-est",
"Latn",
}
m["mov"] = {
"Mojave",
56510,
"nai-yuc",
"Latn",
}
m["mow"] = {
"Moi (Congo)",
11124792,
"bnt-bmo",
"Latn",
}
m["mox"] = {
"Molima",
3319495,
"poz-ocw",
"Latn",
}
m["moy"] = {
"Shekkacho",
56827,
"omv-gon",
}
m["moz"] = {
"Mukulu",
3440403,
"cdc-est",
}
m["mpa"] = {
"Mpoto",
6928303,
"bnt-mbi",
"Latn",
}
m["mpb"] = {
"Mullukmulluk",
6741120,
}
m["mpc"] = {
"Mangarayi",
6748829,
}
m["mpd"] = {
"Machinere",
12953681,
"awd",
"Latn",
}
m["mpe"] = {
"Majang",
56724,
"sdv",
}
m["mpg"] = {
"Marba",
56614,
"cdc-mas",
}
m["mph"] = {
"Maung",
6792550,
"aus-wdj",
"Latn",
}
m["mpi"] = {
"Mpade",
3280670,
"cdc-cbm",
"Latn",
}
m["mpj"] = {
"Martu Wangka",
3295916,
"aus-pam",
"Latn",
}
m["mpk"] = {
"Mbara (Chad)",
3912770,
"cdc-cbm",
}
m["mpl"] = {
"Middle Watut",
15887910,
"poz-ocw",
"Latn",
}
m["mpm"] = {
"Yosondúa Mixtec",
12953741,
"omq-mxt",
"Latn",
}
m["mpn"] = {
"Mindiri",
6863842,
"poz-ocw",
"Latn",
}
m["mpo"] = {
"Miu",
6883668,
"poz-ocw",
"Latn",
}
m["mpp"] = {
"Migabac",
11732448,
"ngf-sop",
"Latn",
}
m["mpq"] = {
"Matís",
3299145,
"sai-pan",
"Latn",
}
m["mpr"] = {
"Vangunu",
3554582,
"poz-ocw",
"Latn",
}
m["mps"] = {
"Dadibi",
5208077,
"paa-teb",
"Latn",
}
m["mpt"] = {
"Mian",
12952846,
"ngf-msu",
"Latn",
}
m["mpu"] = {
"Makuráp",
3281037,
"tup",
"Latn",
}
m["mpv"] = {
"Mungkip",
11732485,
"ngf-boa",
"Latn",
}
m["mpw"] = {
"Mapidian",
6753812,
"awd",
"Latn",
}
m["mpx"] = {
"Misima-Paneati",
6875666,
"poz-ocw",
"Latn",
}
m["mpy"] = {
"Mapia",
3287224,
"poz-mic",
"Latn",
}
m["mpz"] = {
"Mpi",
6928276,
"tbq-bka",
}
m["mqa"] = {
"Maba",
3273750,
}
m["mqb"] = {
"Mbuko",
3502213,
"cdc-cbm",
"Latn",
}
m["mqc"] = {
"Mangole",
6749097,
"poz-cma",
"Latn",
}
m["mqe"] = {
"Matepi",
11732426,
"ngf-han",
"Latn",
}
m["mqf"] = {
"Momuna",
6897518,
}
m["mqg"] = {
"Kota Bangun Kutai Malay",
12952778,
}
m["mqh"] = {
"Tlazoyaltepec Mixtec",
12953740,
"omq-mxt",
"Latn",
}
m["mqi"] = {
"Mariri",
6765544,
}
m["mqj"] = {
"Mamasa",
6745452,
"poz-ssw",
"Latn",
}
m["mqk"] = {
"Rajah Kabunsuwan Manobo",
12953700,
"mno",
}
m["mql"] = {
"Mbelime",
4286473,
"nic-eov",
"Latn",
}
m["mqm"] = {
"South Marquesan",
19694214,
"poz-pep",
"Latn",
}
m["mqn"] = {
"Moronene",
642581,
"poz-btk",
"Latn",
}
m["mqo"] = {
"Modole",
11732457,
"paa-gto",
"Latn",
}
m["mqp"] = {
"Manipa",
6749799,
"poz-cma",
"Latn",
}
m["mqq"] = {
"Minokok",
18642293,
"poz-san",
"Latn",
}
m["mqr"] = {
"Mander",
6747979,
"paa-tor",
"Latn",
}
m["mqs"] = {
"West Makian",
3033575,
"paa-nha",
"Latn",
}
m["mqt"] = {
"Mok",
13018559,
"mkh-pal",
}
m["mqu"] = {
"Mandari",
3285426,
"sdv-bri",
}
m["mqv"] = {
"Mosimo",
11732478,
"ngf-nwh",
"Latn",
}
m["mqw"] = {
"Murupi",
11732486,
"ngf-nwh",
"Latn",
}
m["mqx"] = {
"Mamuju",
6746004,
"poz-ssw",
"Latn",
}
m["mqy"] = {
"Manggarai",
3285748,
"poz-cet",
"Latn",
}
m["mqz"] = {
"Malasanga",
14916889,
"poz-ocw",
"Latn",
}
m["mra"] = {
"Mlabri",
3073465,
"mkh",
}
m["mrb"] = {
"Sungwadia",
3293299,
"poz-vnn",
"Latn",
}
m["mrc"] = {
"Maricopa",
56386,
"nai-yuc",
"Latn",
}
m["mrd"] = {
"Western Magar",
22303263,
"sit-gma",
"Deva",
}
m["mre"] = {
"Martha's Vineyard Sign Language",
33494,
"sgn",
"Latn, Sgnw",
}
m["mrf"] = {
"Elseng",
3915667,
"qfa-unc", -- "Border or language isolate"; unclassifiable due to paucity of data
"Latn",
}
m["mrg"] = {
"Mising",
3316328,
"sit-tan",
"Latn, Beng, Deva",
ancestors = "adi",
}
m["mrh"] = {
"Mara Chin",
4175893,
"tbq-kuk",
"Latn",
}
m["mrj"] = {
"Western Mari",
1776032,
"chm",
"Cyrl",
translit = "chm-translit",
sort_key = "mrj-sortkey",
}
m["mrk"] = {
"Hmwaveke",
5873712,
"poz-cln",
"Latn",
}
m["mrl"] = {
"Mortlockese",
3324598,
"poz-mic",
"Latn",
}
m["mrm"] = {
"Mwerlap",
3331115,
"poz-vnn",
"Latn",
}
m["mrn"] = {
"Cheke Holo",
2962165,
"poz-ocw",
"Latn",
}
m["mro"] = {
"Mru",
1951521,
"sit-mru",
"Latn, Mroo",
}
m["mrp"] = {
"Morouas",
6913299,
"poz-vnn",
"Latn",
}
m["mrq"] = {
"North Marquesan",
2603808,
"poz-pep",
"Latn",
}
m["mrr"] = {
"Hill Maria",
27602,
"dra-mdy",
"Deva",
}
m["mrs"] = {
"Maragus",
6754640,
"poz-vnc",
"Latn",
}
m["mrt"] = {
"Margi",
56241,
"cdc-cbm",
"Latn",
}
m["mru"] = {
"Mono (Cameroon)",
11031964,
"alv-mbm",
"Latn",
}
m["mrv"] = {
"Mangarevan",
36237,
"poz-pep",
"Latn",
}
m["mrw"] = {
"Mëranaw", --"Maranao",
33800,
"phi",
"Latn, Arab",
is_official_kwf_name = "https://kwfwikaatkultura.ph/meranaw/", --TLCHANGE
english_name = "Maranao", --TLCHANGE
}
m["mrx"] = {
"Dineor",
5278044,
"paa-tor",
"Latn",
}
m["mry"] = {
"Mandaya", --"Karaga Mandaya",
6747925,
"phi",
is_official_kwf_name = "https://kwfwikaatkultura.ph/mandaya/", --TLCHANGE
english_name = "Karaga Mandaya", --TLCHANGE
}
m["mrz"] = {
"Marind",
6763970,
"paa-mri",
"Latn",
}
m["msb"] = {
"Masbatenyo", --"Masbatenyo"
33948,
"phi",
"Latn",
is_official_kwf_name = "https://kwfwikaatkultura.ph/masbatenyo/", --TLCHANGE
english_name = "Masbatenyo", --TLCHANGE
}
m["msc"] = {
"Sankaran Maninka",
11155812,
"dmn-mnk",
}
m["msd"] = {
"Yucatec Maya Sign Language",
34281,
"sgn",
"Latn", -- when documented
}
m["mse"] = {
"Musey",
56328,
"cdc-mas",
}
m["msf"] = {
"Mekwei",
4544752,
"paa-nim",
"Latn",
}
m["msg"] = {
"Moraid",
6909020,
"paa-wbh",
"Latn",
}
m["msi"] = {
"Sabah Malay",
10867404,
"crp",
"Latn, Arab",
}
m["msj"] = {
"Ma",
6720909,
"nic-mbc",
"Latn",
}
m["msk"] = {
"Mansaka", --"Mansaka"
12952800,
"phi",
"Latn",
is_official_kwf_name = "https://kwfwikaatkultura.ph/mansaka/", --TLCHANGE
english_name = "Mansaka", --TLCHANGE
}
m["msl"] = {
"Molof",
4300950,
}
m["msm"] = {
"Manobo Agusan", --"Agusan Manobo",
12953696,
"mno",
"Latn",
is_official_kwf_name = "https://kwfwikaatkultura.ph/manobo-agusan/", --TLCHANGE
english_name = "Agusan Manobo", --TLCHANGE
}
m["msn"] = {
"Vurës",
3563857,
"poz-vnn",
"Latn",
}
m["mso"] = {
"Mombum",
6897079,
"ngf-mom",
"Latn",
}
m["msp"] = {
"Maritsauá",
6765915,
"tup",
"Latn",
}
m["msq"] = {
"Caac",
2932212,
"poz-cln",
"Latn",
}
m["msr"] = {
"Mongolian Sign Language",
3915499,
"sgn",
}
m["mss"] = {
"West Masela",
12952816,
"poz-tim",
}
m["msu"] = {
"Musom",
6943041,
"poz-ocw",
"Latn",
}
m["msv"] = {
"Maslam",
3502273,
}
m["msw"] = {
"Mansoanka",
35814,
}
m["msx"] = {
"Moresada",
11732475,
"ngf-pom",
"Latn",
}
m["msy"] = {
"Aruamu",
3501809,
"paa-rub",
"Latn",
}
m["msz"] = {
"Momare",
6897030,
"ngf-sop",
"Latn",
}
m["mta"] = {
"Manobo Dulangan", --"Cotabato Manobo",
12953698,
"mno",
"Latn",
is_official_kwf_name = "https://kwfwikaatkultura.ph/manobo-dulangan/", --TLCHANGE
english_name = "Cotabato Manobo", --TLCHANGE
}
m["mtb"] = {
"Anyin Morofo",
3502338,
"alv-ctn",
"Latn",
ancestors = "any",
}
m["mtc"] = {
"Munit",
11732482,
"ngf-kok",
"Latn",
}
m["mtd"] = {
"Mualang",
3073458,
"poz-mly",
"Latn",
}
m["mte"] = {
"Alu",
33503,
"poz-ocw",
"Latn",
}
m["mtf"] = {
"Murik (New Guinea)",
7050035,
"paa-lse",
"Latn",
}
m["mtg"] = {
"Una",
5580728,
"ngf-mek",
"Latn",
}
m["mth"] = {
"Munggui",
6936018,
"poz-hce",
"Latn",
}
m["mti"] = {
"Maiwa (New Guinea)",
6737223,
"ngf-dag",
"Latn",
}
m["mtj"] = {
"Moskona",
11288953,
"paa-ebh",
"Latn",
}
m["mtk"] = {
"Mbe'",
10964025,
"nic-nka",
"Latn",
}
m["mtl"] = {
"Montol",
3440457,
"cdc-wst",
"Latn",
}
m["mtm"] = {
"Mator",
20669419,
"syd",
"Cyrl",
}
m["mtn"] = {
"Matagalpa",
3490756,
"nai-min",
}
m["mto"] = {
"Totontepec Mixe",
7828400,
"nai-miz",
"Latn",
}
m["mtp"] = {
"Wichí Lhamtés Nocten",
5908756,
"sai-wic",
"Latn",
}
m["mtq"] = {
"Muong",
3236789,
"mkh-vie",
"Latn",
sort_key = "vi-sortkey",
}
m["mtr"] = {
"Mewari",
2992857,
"raj",
"Deva",
translit = "hi-translit", -- for now
}
m["mts"] = {
"Yora",
3572572,
"sai-pan",
"Latn",
}
m["mtt"] = {
"Mota",
3325052,
"poz-vnn",
"Latn",
}
m["mtu"] = {
"Tututepec Mixtec",
7857069,
"omq-mxt",
"Latn",
}
m["mtv"] = {
"Asaro'o",
3503684,
"ngf-war", -- ngf-moa if we split out Molet from Asaro'o
"Latn",
}
m["mtw"] = {
"Bukidnon Magahat", --"Magahat",
6729600,
"phi",
is_official_kwf_name = "https://kwfwikaatkultura.ph/bukidnon-magahat/", --TLCHANGE
english_name = "Magahat", --TLCHANGE
}
m["mtx"] = {
"Tidaá Mixtec",
7800805,
"omq-mxt",
"Latn",
}
m["mty"] = {
"Nabi",
6956858,
"paa-tor",
"Latn",
}
m["mua"] = {
"Mundang",
36032,
"alv-mbm",
}
m["mub"] = {
"Mubi",
3440518,
"cdc-est",
"Latn",
}
m["muc"] = {
"Mbu'",
35868,
"nic-beb",
"Latn",
}
m["mud"] = {
"Mednyj Aleut",
1977419,
"qfa-mix",
ancestors = "ale, ru"
}
m["mue"] = {
"Media Lengua",
36066,
"qfa-mix",
"Latn",
ancestors = "es, qu",
}
m["mug"] = {
"Musgu",
3123545,
"cdc-cbm",
"Latn",
}
m["muh"] = {
"Mündü",
35981,
"nic-nke",
"Latn",
}
m["mui"] = {
"Musi",
615660,
"poz-mly",
"Latn",
}
m["muj"] = {
"Mabire",
3440437,
}
m["mul"] = {
"Maramihang wika", -- "Translingual",
7834564,
"qfa-not",
"All",
-- NOTE: The following sort keys are used in process_page() in [[Module:headword/page]], which generates
-- the default sort key for the page (corresponding to {{DEFAULTSORT:...}}) by generating a sort key for
-- the pagename using `makeSortKey()` called on language object "mul". Currently this just handles
-- Japanese sort keys.
--
-- FIXME: This should be smarter and use the language of the page if there's only one.
sort_key = {
Hani = "Hani-sortkey",
Jpan = "Jpan-sortkey",
Hrkt = "Hira-sortkey", -- Sort all kana as Hira.
Hira = "Hira-sortkey",
Kana = "Hira-sortkey",
},
standard_chars = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz" .. c.punc,
}
m["mum"] = {
"Maiwala",
12952764,
"poz-ocw",
"Latn",
}
m["muo"] = {
"Nyong",
36373,
"alv-lek",
}
m["mup"] = {
"Malvi",
33413,
"raj",
"Deva",
translit = "hi-translit"
}
m["muq"] = {
"Eastern Xiangxi Miao",
27431376,
"hmn",
}
m["mur"] = {
"Murle",
56727,
"sdv",
}
m["mus"] = {
"Creek",
523014,
"nai-mus",
"Latn",
}
m["mut"] = {
"Western Muria",
12952886,
"dra-mur",
}
m["muu"] = {
"Yaaku",
34222,
"cus-eas",
}
m["muv"] = {
"Muthuvan",
3327420,
"dra-tam",
}
m["mux"] = {
"Bo-Ung",
15831607,
"ngf-hag",
"Latn",
}
m["muy"] = {
"Muyang",
3502301,
"cdc-cbm",
"Latn",
}
m["muz"] = {
"Mursi",
36013,
"sdv",
}
m["mva"] = {
"Manam",
6746851,
"poz-ocw",
"Latn",
}
m["mvb"] = {
"Mattole",
20824,
"ath-pco",
"Latn",
}
m["mvd"] = {
"Mamboru",
578815,
"poz",
"Latn",
}
m["mvg"] = {
"Yucuañe Mixtec",
25562736,
"omq-mxt",
"Latn",
}
m["mvh"] = {
"Mire",
3441359,
}
m["mvi"] = {
"Miyako",
36218,
"jpx-sry",
"Jpan",
translit = s["jpx-translit"],
display_text = s["jpx-displaytext"],
strip_diacritics = s["jpx-stripdiacritics"],
sort_key = s["jpx-sortkey"],
}
m["mvk"] = {
"Mekmek",
6810592,
"paa-yua",
"Latn",
}
m["mvl"] = {
"Mbara (Australia)",
6799620,
"aus-pam",
"Latn",
}
m["mvm"] = {
"Muya",
2422759,
"sit-qia",
}
m["mvn"] = {
"Minaveha",
6863278,
"poz-ocw",
"Latn",
}
m["mvo"] = {
"Marovo",
3294683,
"poz-ocw",
"Latn",
}
m["mvp"] = {
"Duri",
3915414,
"poz-ssw",
"Latn",
}
m["mvq"] = {
"Moere",
11732458,
"ngf-kum",
"Latn",
}
m["mvr"] = {
"Marau",
6755069,
"poz-hce",
"Latn",
}
m["mvs"] = {
"Massep",
3502895,
"qfa-dis", -- poorly documented (but in vigorous use); isolate per Ethnologue, Glottolog, and Foley (2018); Greater
-- Kwerba per Usher
"Latn",
}
m["mvt"] = {
"Mpotovoro",
6928305,
"poz-vnc",
"Latn",
}
m["mvu"] = {
"Marfa",
713633,
}
m["mvv"] = {
"Tagal Murut",
7675300,
"poz-san",
"Latn",
}
m["mvw"] = {
"Machinga",
12952754,
"bnt-rvm",
}
m["mvx"] = {
"Meoswar",
6817777,
"poz-hce",
"Latn",
}
m["mvy"] = {
"Indus Kohistani",
33399,
"inc-koh",
"Arab",
}
m["mvz"] = {
"Mesqan",
6821677,
"sem-eth",
}
m["mwa"] = {
"Mwatebu",
14916896,
"poz-ocw",
"Latn",
}
m["mwb"] = {
"Juwal",
6319103,
"paa-mmu",
"Latn",
}
m["mwc"] = {
"Are",
29277,
"poz-ocw",
"Latn",
}
m["mwe"] = {
"Mwera",
6944725,
"bnt-rvm",
"Latn",
}
m["mwf"] = {
"Murrinh-Patha",
2980398,
"aus-dal",
"Latn",
}
m["mwg"] = {
"Aiklep",
3399652,
"poz-ocw",
"Latn",
}
m["mwh"] = {
"Mouk-Aria",
3325498,
"poz-ocw",
"Latn",
}
m["mwi"] = {
"Labo",
2157452,
"poz-vnc",
"Latn",
}
m["mwk"] = {
"Kita Maninkakan",
3015523,
"dmn-wmn",
}
m["mwl"] = {
"Mirandes",
13330,
"roa-asl",
"Latn",
}
m["mwm"] = {
"Sar",
56850,
"csu-sar",
"Latn",
}
m["mwn"] = {
"Nyamwanga",
6944666,
"bnt-mwi",
"Latn",
}
m["mwo"] = {
"Sungwadaga",
3276435,
"poz-vnn",
"Latn",
}
m["mwp"] = {
"Kala Lagaw Ya",
2591262,
"aus-pam",
"Latn",
}
m["mwq"] = {
"Mün Chin",
331340,
"tbq-kuk",
}
m["mwr"] = {
"Marwari",
56312,
"raj",
"Deva, Mahj",
translit = {
Deva = "hi-translit", -- for now
Mahj = "Mahj-translit",
},
}
m["mws"] = {
"Mwimbi-Muthambi",
15632357,
"bnt-kka",
"Latn",
}
m["mwt"] = {
"Moken",
18648701,
"poz",
}
m["mwu"] = {
"Mittu",
6883573,
"csu-bbk",
"Latn",
}
m["mwv"] = {
"Mentawai",
13365,
"poz-nws",
"Latn",
}
m["mww"] = {
"White Hmong",
3138829,
"hmn",
"Latn, Hmng, Hmnp",
}
m["mwz"] = {
"Moingi",
11011905,
}
m["mxa"] = {
"Northwest Oaxaca Mixtec",
12953739,
"omq-mxt",
"Latn",
}
m["mxb"] = {
"Tezoatlán Mixtec",
3317286,
"omq-mxt",
"Latn",
}
m["mxd"] = {
"Modang",
6888037,
"poz",
"Latn",
}
m["mxe"] = {
"Mele-Fila",
3305008,
"poz-pnp",
"Latn",
}
m["mxf"] = {
"Malgbe",
3502224,
}
m["mxg"] = {
"Mbangala",
6799612,
"bnt-yak",
}
m["mxh"] = {
"Mvuba",
6944591,
"csu-mle",
"Latn",
}
m["mxi"] = {
"Mozarabic",
317044,
"roa-ibe",
"Arab, Hebr, Latn",
translit = "mxi-translit",
strip_diacritics = {
Arab = "ar-stripdiacritics",
},
-- Hebr display_text, strip_diacritics, sort_key in [[Module:scripts/data]]
}
m["mxj"] = {
"Miju",
56332,
"sit-mdz",
"Latn, Deva",
}
m["mxk"] = {
"Monumbo",
6906792,
"paa-mon",
"Latn",
}
m["mxl"] = {
"Maxi Gbe",
35770,
"alv-gbe",
}
m["mxm"] = {
"Meramera",
6817936,
"poz-ocw",
"Latn",
}
m["mxn"] = {
"Moi (Indonesia)",
11732459,
"paa-wbh",
"Latn",
}
m["mxo"] = {
"Mbowe",
10962309,
"bnt-kav",
}
m["mxp"] = {
"Tlahuitoltepec Mixe",
7810697,
}
m["mxq"] = {
"Juquila Mixe",
25559721,
}
m["mxr"] = {
"Murik (Malaysia)",
3328150,
nil,
"Latn",
}
m["mxs"] = {
"Huitepec Mixtec",
12953729,
"omq-mxt",
"Latn",
}
m["mxt"] = {
"Jamiltepec Mixtec",
12953730,
"omq-mxt",
"Latn",
}
m["mxu"] = {
"Mada (Cameroon)",
3441206,
"cdc-cbm",
"Latn",
}
m["mxv"] = {
"Metlatónoc Mixtec",
36363,
"omq-mxt",
"Latn",
}
m["mxw"] = {
"Namo",
12952923,
"paa-nam",
"Latn",
}
m["mxx"] = {
"Mahou",
11004334,
"dmn-mnk",
"Latn, Nkoo",
}
m["mxy"] = {
"Southeastern Nochixtlán Mixtec",
7070684,
"omq-mxt",
"Latn",
}
m["mxz"] = {
"Central Masela",
42575433,
"poz-tim",
"Latn",
}
m["myb"] = {
"Mbay",
3033565,
"csu-sar",
"Latn",
}
m["myc"] = {
"Mayeka",
11129517,
"bnt-boa",
}
m["mye"] = {
"Myene",
35832,
"bnt-tso",
"Latn",
}
m["myf"] = {
"Bambassi",
56540,
"omv-mao",
"Latn",
}
m["myg"] = {
"Manta",
35799,
"nic-mom",
"Latn",
}
m["myh"] = {
"Makah",
3280640,
"wak",
"Latn",
}
m["myj"] = {
"Mangayat",
35988,
"nic-ser",
}
m["myk"] = {
"Mamara",
36187,
"alv-sma",
"Latn",
}
m["myl"] = {
"Moma",
6897018,
"poz",
"Latn",
}
m["mym"] = {
"Me'en",
3408516,
"sdv",
}
m["myo"] = {
"Anfillo",
34928,
"omv-gon",
}
m["myp"] = {
"Pirahã",
33825,
"sai-mur",
"Latn",
}
m["myr"] = {
"Muniche",
3915654,
}
m["mys"] = {
"Mesmes",
3508617,
"sem-eth",
}
m["myu"] = {
"Mundurukú",
746723,
"tup",
"Latn",
}
m["myv"] = {
"Erzya",
29952,
"urj-mdv",
"Cyrl",
translit = "myv-translit",
override_translit = true,
}
m["myw"] = {
"Muyuw",
3502878,
"poz-ocw",
"Latn",
}
m["myx"] = {
"Masaba",
12952814,
"bnt-msl",
"Latn",
}
m["myy"] = {
"Macuna",
3275059,
"sai-tuc",
"Latn",
}
m["myz"] = {
"Classical Mandaic",
25559314,
"sem-ase",
"Mand",
translit = {
Mand = "Mand-translit",
},
strip_diacritics = {
Mand = "Mand-stripdiacritics",
}
}
m["mza"] = {
"Santa María Zacatepec Mixtec",
8063756,
"omq-mxt",
"Latn",
}
m["mzb"] = {
"Northern Saharan Berber",
11156769,
"ber",
"Arab, Latn, Tfng",
}
m["mzc"] = {
"Madagascar Sign Language",
12715020,
"sgn",
}
m["mzd"] = {
"Malimba",
35806,
"bnt-saw",
}
m["mze"] = {
"Morawa",
6909384,
"paa-mal",
"Latn",
}
m["mzg"] = {
"Monastic Sign Language",
3217333,
"sgn",
}
m["mzh"] = {
"Wichí Lhamtés Güisnay",
7998197,
"sai-wic",
"Latn",
}
m["mzi"] = {
"Ixcatlán Mazatec",
6101049,
"omq-maz",
"Latn",
}
m["mzj"] = {
"Manya",
11006832,
"dmn-mnk",
}
m["mzk"] = {
"Nigeria Mambila",
11004163,
"nic-mmb",
"Latn",
}
m["mzl"] = {
"Mazatlán Mixe",
25559728,
}
m["mzm"] = {
"Mumuye",
36021,
"alv-mum",
"Latn",
}
m["mzn"] = {
"Mazanderani",
13356,
"ira-msh",
"mzn-Arab",
}
m["mzo"] = {
"Matipuhy",
6787588,
"sai-kui",
"Latn",
}
m["mzp"] = {
"Movima",
1659701,
"qfa-iso",
"Latn",
}
m["mzq"] = {
"Mori Atas",
3324070,
"poz-btk",
"Latn",
}
m["mzr"] = {
"Marúbo",
3296011,
"sai-pan",
"Latn",
}
m["mzs"] = {
"Makanes",
35785,
"crp",
"Latn",
ancestors = "pt",
sort_key = {Latn = {remove_diacritics = c.grave .. c.acute .. c.circ .. c.tilde .. c.diaer .. c.cedilla}},
}
m["mzt"] = {
"Mintil",
6869641,
"mkh-asl",
}
m["mzu"] = {
"Inapang",
6013569,
"paa-tam",
"Latn",
}
m["mzv"] = {
"Manza",
36038,
"gba-eas",
}
m["mzw"] = {
"Deg",
35183,
"nic-gnw",
"Latn",
}
m["mzx"] = {
"Mawayana",
6794377,
"awd",
}
m["mzy"] = {
"Mozambican Sign Language",
6927809,
"sgn",
}
m["mzz"] = {
"Maiadomu",
6735234,
"poz-ocw",
"Latn",
}
return require("Module:languages").finalizeData(m, "language")
7ek7777cr33xrpqg2s1tpbsh0uyub7m
Module:pt-headword
828
34968
178622
173592
2026-11-09T06:00:46Z
GinawaSaHapon
3989
178622
Scribunto
text/plain
-- This module contains code for Portuguese headword templates.
-- Templates covered are:
-- * {{pt-noun}}, {{pt-proper noun}};
-- * {{pt-verb}};
-- * {{pt-adj}}, {{pt-adj-comp}}, {{pt-adj-sup}};
-- * {{pt-det}};
-- * {{pt-pron-adj}};
-- * {{pt-contr-adj}};
-- * {{pt-pp}};
-- * {{pt-cardinal}};
-- * {{pt-adv}}.
-- See [[Module:pt-verb]] for Portuguese conjugation templates.
local export = {}
local pos_functions = {}
local force_cat = false -- for testing; if true, categories appear in non-mainspace pages
local m_strutils = require("Module:string utilities")
local require_when_needed = require("Module:utilities/require when needed")
local m_table = require("Module:table")
local com = require("Module:pt-common")
local en_utilities_module = "Module:en-utilities"
local headword_utilities_module = "Module:headword utilities"
local inflection_utilities_module = "Module:inflection utilities"
local romut_module = "Module:romance utilities"
local pt_verb_module = "Module:pt-verb"
local lang = require("Module:languages").getByCode("pt")
local langname = lang:getCanonicalName()
local m_en_utilities = require_when_needed(en_utilities_module)
local m_headword_utilities = require_when_needed(headword_utilities_module)
local glossary_link = require_when_needed(headword_utilities_module, "glossary_link")
local insert = table.insert
local rfind = m_strutils.find
local rmatch = m_strutils.match
local ulower = m_strutils.lower
local usub = m_strutils.sub
local uupper = m_strutils.upper
-- When followed by a hyphen in a hyphenated compound, the hyphen will be included with the prefix when linked.
local include_hyphen_prefixes = m_table.listToSet {
"ab",
"afro",
"anarco",
"anglo",
"ântero",
"anti",
"auto",
"contra",
"ex",
"franco",
"hiper",
"infra",
"inter",
"intra",
"macro",
"micro",
"neo",
"pan",
"pós",
"pré",
"pró",
"proto",
"sobre",
"sub",
"super",
"vice",
}
-----------------------------------------------------------------------------------------
-- Utility functions --
-----------------------------------------------------------------------------------------
local function track(page)
require("Module:debug/track")("pt-headword/" .. page)
return true
end
local list_param = {list = true, disallow_holes = true}
local boolean_param = {type = "boolean"}
local metaphonic_label = "metaponiko"
local function make_plural(sg, special)
local pl = com.make_plural(sg, special)
if not pl then
error(("Internal error: Unable to generate default plural of '%s'"):format(sg))
end
return pl
end
-- Parse and insert an inflection not requiring additional processing into `data.inflections`. The raw arguments come
-- from `args[field]`, which is parsed for inline modifiers. `label` is the label that the inflections are given;
-- `plpos` is the plural part of speech, used in [[Category:LANGNAME PLPOS with red links in their headword lines]].
-- `accel` is the accelerator form, or nil.
local function parse_and_insert_inflection(data, args, field, label, accel, frob)
m_headword_utilities.parse_and_insert_inflection {
headdata = data,
forms = args[field],
paramname = field,
splitchar = ",",
include_mods = {"g"},
label = label,
accel = accel and {form = accel} or nil,
frob = frob,
}
end
-- The main entry point.
-- This is the only function that can be invoked from a template.
function export.show(frame)
local poscat = frame.args[1] or error("Part of speech has not been specified. Please pass parameter 1 to the module invocation.")
local parargs = frame:getParent().args
local params = {
["head"] = list_param,
["id"] = true,
["json"] = boolean_param,
["splithyph"] = boolean_param,
["nolinkhead"] = boolean_param,
["nolink"] = {type = "boolean", alias_of = "nolinkhead"},
["abbr"] = list_param,
["pagename"] = true, -- for testing
}
if pos_functions[poscat] then
for key, val in pairs(pos_functions[poscat].params) do
params[key] = val
end
end
local args = require("Module:parameters").process(parargs, params)
local pagename = args.pagename or mw.loadData("Module:headword/data").pagename
local user_specified_heads = args.head
local heads = user_specified_heads
if args.nolinkhead then
if #heads == 0 then
heads = {pagename}
end
else
local romut = require(romut_module)
local auto_linked_head = romut.add_links_to_multiword_term(pagename, args.splithyph, nil,
include_hyphen_prefixes)
if #heads == 0 then
heads = {auto_linked_head}
else
for i, head in ipairs(heads) do
if head:find("^~") then
head = romut.apply_link_modifiers(auto_linked_head, usub(head, 2))
heads[i] = head
end
end
end
end
local data = {
lang = lang,
pos_category = pos_functions[poscat] and pos_functions[poscat].pos_category or poscat,
categories = {},
heads = heads,
user_specified_heads = user_specified_heads,
no_redundant_head_cat = #user_specified_heads == 0,
genders = {},
inflections = {},
pagename = pagename,
id = args.id,
force_cat_output = force_cat,
checkredlinks = pos_functions[poscat] and pos_functions[poscat].redlink_pos or true,
}
if pagename:find("^%-") and poscat ~= "suffix forms" then
data.is_suffix = true
data.pos_category = "suffixes"
data.checkredlinks = true
local singular_poscat = poscat
insert(data.categories, langname .. " na hulaping gumagawa ng " .. singular_poscat)
insert(data.inflections, {label = "hulaping gumagawa ng " .. singular_poscat})
end
local pagename_lower = ulower(pagename)
for _, ch in ipairs{"gü", "jh", "kh", "ph", "qü", "sh", "th", "tl", "ts", "tz", "wh", "zh"} do
if pagename_lower:find(ch) then
insert(data.categories, langname .. " na kataga na binaybay na may " .. uupper(ch))
end
end
if pos_functions[poscat] then
pos_functions[poscat].func(args, data)
end
parse_and_insert_inflection(data, args, "abbr", "daglat")
if args.json then
return require("Module:JSON").toJSON(data)
end
return require("Module:headword").full_headword(data)
end
local function replace_hash_with_lemma(term, lemma)
-- If there is a % sign in the lemma, we have to replace it with %% so it doesn't get interpreted as a capture replace
-- expression.
lemma = lemma:gsub("%%", "%%%%")
return (term:gsub("#", lemma))
end
local function is_metaphonic(args, lemma)
if args.nometa then
return false
end
if args.meta then
return true
end
-- Anything in -oso with a preceding vowel (e.g. [[gostoso]], [[curioso]]) is normally metaphonic.
return rfind(lemma, com.V .. ".*oso$")
end
-----------------------------------------------------------------------------------------
-- Nouns --
-----------------------------------------------------------------------------------------
local allowed_genders = m_table.listToSet(
{"m", "f", "mf", "mfbysense", "mfequiv", "gneut", "n", "m-p", "f-p", "mf-p", "mfbysense-p", "mfequiv-p", "gneut-p", "n-p", "?", "?-p"}
)
local function validate_genders(genders)
for _, g in ipairs(genders) do
if type(g) == "table" then
g = g.spec
end
if not allowed_genders[g] then
error("Unrecognized gender: " .. g)
end
end
end
local function do_noun(args, data, is_proper)
local is_plurale_tantum = false
local has_singular = false
local category_plpos = data.checkredlinks
if category_plpos == true then
category_plpos = data.pos_category
end
local category_pos = m_en_utilities.singularize(category_plpos)
validate_genders(args[1])
data.genders = args[1]
local saw_m = false
local saw_f = false
local saw_gneut = false
local gender_for_irreg_ending
-- Check for specific genders and pluralia tantum.
for _, g in ipairs(args[1]) do
if type(g) == "table" then
g = g.spec
end
if g:find("-p$") then
is_plurale_tantum = true
else
has_singular = true
if g == "m" or g == "mf" or g == "mfbysense" then
saw_m = true
end
if g == "f" or g == "mf" or g == "mfbysense" then
saw_f = true
end
if g == "gneut" then
saw_gneut = true
end
end
end
if saw_m and saw_f then
gender_for_irreg_ending = "mf"
elseif saw_f then
gender_for_irreg_ending = "f"
else
gender_for_irreg_ending = "m"
end
local lemma = data.pagename
local plurals = {}
local function insert_noun_inflection(terms, label, accel)
m_headword_utilities.insert_inflection {
headdata = data,
terms = terms,
label = label,
accel = accel and {form = accel} or nil,
}
end
if is_plurale_tantum and not has_singular then
if args[2][1] then
error("Can't specify plurals of plurale tantum " .. category_pos)
end
insert(data.inflections, {label = glossary_link("maramihan lamang")})
else
plurals = m_headword_utilities.parse_term_list_with_modifiers {
paramname = {2, "pl"},
forms = args[2],
splitchar = ",",
}
-- Check for special plural signals
local mode = nil
local pl1 = plurals[1]
if pl1 and #pl1.term == 1 then
mode = pl1.term
if mode == "?" or mode == "!" or mode == "-" or mode == "~" then
pl1.term = nil
if m_headword_utilities.termobj_has_decorations(pl1) then
error(("Can't specify inline modifiers with plural code '%s'"):format(mode))
end
table.remove(plurals, 1) -- Remove the mode parameter
elseif mode ~= "+" and mode ~= "#" then
error(("Unexpected plural code '%s'"):format(mode))
end
end
if is_plurale_tantum then
-- both singular and plural
insert(data.inflections, {label = "minsan " .. glossary_link("maramihan lamang") .. ", paiba-iba"})
end
if mode == "?" then
-- Plural is unknown
insert(data.categories, langname .. " na " .. category_plpos .. " na may di-alam o di-tukoy na anyong maramihan")
elseif mode == "!" then
-- Plural is not attested
insert(data.inflections, {label = "di-kumpirmado ang maramihan"})
insert(data.categories, langname .. " na " .. category_plpos .. " na may di-kumpirmadong anyong maramihan")
if plurals[1] then
error("Can't specify any plurals along with unattested plural code '!'")
end
elseif mode == "-" then
-- Uncountable noun; may occasionally have a plural
insert(data.categories, langname .. " na di-mabibilang na " .. category_plpos)
-- If plural forms were given explicitly, then show "usually"
if plurals[1] then
insert(data.inflections, {label = "madalas " .. glossary_link("di-mabibilang")})
insert(data.categories, langname .. " na mabibilang na " .. category_plpos)
else
insert(data.inflections, {label = glossary_link("di-mabibilang")})
end
else
-- Countable or mixed countable/uncountable
if not plurals[1] and not is_proper then
plurals[1] = {term = "+"}
end
if mode == "~" then
-- Mixed countable/uncountable noun, always has a plural
insert(data.inflections, {label = glossary_link("mabibilang") .. " at " .. glossary_link("di-mabibilang")})
insert(data.categories, langname .. " na di-mabibilang na " .. category_plpos)
insert(data.categories, langname .. " na mabibilang na " .. category_plpos)
elseif plurals[1] then
-- Countable nouns
insert(data.categories, langname .. " na mabibilang na " .. category_plpos)
else
-- Uncountable nouns
insert(data.categories, langname .. " uncountable " .. category_plpos)
end
end
-- Gather plurals, handling requests for default plurals.
local has_default_or_hash = false
for _, pl in ipairs(plurals) do
if pl.term:find("^%+") or pl.term:find("#") then
has_default_or_hash = true
break
end
end
if has_default_or_hash then
for _, pl in ipairs(plurals) do
if pl.term == "+" then
pl.term = make_plural(lemma)
elseif pl.term:find("^%+") then
pl.term = require(romut_module).get_special_indicator(pl.term)
pl.term = make_plural(lemma, pl.term)
else
pl.term = replace_hash_with_lemma(pl.term, lemma)
end
end
end
local pl1 = plurals[1]
if pl1 and not plurals[2] and pl1.term == lemma then
insert(data.inflections, {label = glossary_link("di-nababago"),
q = pl1.q, qq = pl1.qq, l = pl1.l, ll = pl1.ll, refs = pl1.refs
})
insert(data.categories, langname .. " na di-mababanghay na " .. category_plpos)
else
insert_noun_inflection(plurals, "maramihan", "p")
end
if plurals[2] then
insert(data.categories, langname .. " na " .. category_plpos .. " na may maraming anyong maramihan")
end
end
-- Gather masculines/feminines. For each one, generate the corresponding plural. `field` is the name of the field
-- containing the masculine or feminine forms (normally "m" or "f"); `inflect` is a function of one or two arguments
-- to generate the default masculine or feminine from the lemma (the arguments are the lemma and optionally a
-- "special" flag to indicate how to handle multiword lemmas, and the function is normally make_feminine or
-- make_masculine from [[Module:pt-common]]); and `default_plurals` is a list into which the corresponding default
-- plurals of the gathered or generated masculine or feminine forms are stored.
local function handle_mf(field, inflect, default_plurals)
local specials = {} -- Tracks the specific plural indicators for each entry in a multi-item list
local term_index = 0
local mfs = m_headword_utilities.parse_term_list_with_modifiers {
paramname = field,
forms = args[field],
splitchar = ",",
frob = function(term)
term_index = term_index + 1
if term == "+" then
-- Generate default masculine/feminine.
term = inflect(lemma)
else
term = replace_hash_with_lemma(term, lemma)
end
local special = require(romut_module).get_special_indicator(term)
if special then
specials[term_index] = special -- Store indicator unique to this specific item's index
term = inflect(lemma, special)
end
return term
end
}
for i, mf in ipairs(mfs) do
local plobj = m_table.shallowCopy(mf)
local term_special = specials[i] -- Retrieve indicator matching this exact singular's index
plobj.term = make_plural(mf.term, term_special)
-- Add an accelerator for each masculine/feminine plural whose lemma is the corresponding singular, so that
-- the accelerated entry that is generated has a definition that looks like
-- # {{plural of|pt|MFSING}}
plobj.accel = {form = "p", lemma = mf.term}
insert(default_plurals, plobj)
end
return mfs
end
local feminine_plurals = {}
local feminines = handle_mf("f", com.make_feminine, feminine_plurals)
local masculine_plurals = {}
local masculines = handle_mf("m", com.make_masculine, masculine_plurals)
local function handle_mf_plural(mfplfield, default_plurals, singulars)
local mfpl = m_headword_utilities.parse_term_list_with_modifiers {
paramname = mfplfield,
forms = args[mfplfield],
splitchar = ",",
}
if is_plurale_tantum then
return mfpl
end
local new_mfpls = {}
local saw_plus
for i, mfpl in ipairs(mfpl) do
local accel
if #mfpl == #singulars then
-- If same number of overriding masculine/feminine plurals as singulars, assume each plural goes with
-- the corresponding singular and use each corresponding singular as the lemma in the accelerator. The
-- generated entry will have
-- # {{plural of|pt|SINGULAR}}
-- as the definition.
accel = {form = "p", lemma = singulars[i].term}
else
accel = nil
end
if mfpl.term == "+" then
-- We should never see + twice. If we do, it will lead to problems since we overwrite the values of
-- default_plurals the first time around.
if saw_plus then
error(("Saw + twice when handling %s="):format(mfplfield))
end
saw_plus = true
if not default_plurals[1] then
-- FIXME: Can this happen? Not in corresponding Spanish code and the old Portuguese code tried to
-- handle this condition by generating the default plural from the lemma.
error("Internal error: Something wrong, no generated default m/f plurals at this stage")
end
for _, defpl in ipairs(default_plurals) do
-- defpl is already a table and has an accel field
m_headword_utilities.combine_termobj_decorations(defpl, mfpl)
insert(new_mfpls, defpl)
end
elseif mfpl.term:find("^%+") then
mfpl.term = require(romut_module).get_special_indicator(mfpl.term)
for _, mf in ipairs(singulars) do
local mfplobj = m_table.shallowCopy(mfpl)
mfplobj.term = make_plural(mf.term, mfpl.term)
mfplobj.accel = accel
m_headword_utilities.combine_termobj_decorations(mfplobj, mf)
insert(new_mfpls, mfplobj)
end
else
mfpl.accel = accel
mfpl.term = replace_hash_with_lemma(mfpl.term, lemma)
insert(new_mfpls, mfpl)
end
end
return new_mfpls
end
if args.fpl[1] then
-- Override any existing feminine plurals.
feminine_plurals = handle_mf_plural("fpl", feminine_plurals, feminines)
end
if args.mpl[1] then
-- Override any existing masculine plurals.
masculine_plurals = handle_mf_plural("mpl", masculine_plurals, masculines)
end
local function parse_and_insert_noun_inflection(field, label, accel)
parse_and_insert_inflection(data, args, field, label, accel)
end
insert_noun_inflection(feminines, "pambabae", "f")
insert_noun_inflection(feminine_plurals, "pambabae maramihan")
insert_noun_inflection(masculines, "panlalaki")
insert_noun_inflection(masculine_plurals, "panlalaki maramihan")
if is_metaphonic(args, lemma) then
insert(data.inflections, {label = metaphonic_label})
insert(data.categories, langname .. " na " .. category_plpos .. " na may metaponya")
end
parse_and_insert_noun_inflection("dim", "pampaliit")
parse_and_insert_noun_inflection("aug", "pampalaki")
parse_and_insert_noun_inflection("pej", "pampanira")
parse_and_insert_noun_inflection("dem", "ngalang-lipi")
parse_and_insert_noun_inflection("fdem", "pambabae ngalang-lipi")
-- Maybe add category 'Portuguese nouns with irregular gender' (or similar)
local irreg_gender_lemma = com.rsub(lemma, " .*", "") -- only look at first word
if (rfind(irreg_gender_lemma, "[^ã]o$") and (gender_for_irreg_ending == "f" or gender_for_irreg_ending == "mf")) or
(irreg_gender_lemma:find("a$") and (gender_for_irreg_ending == "m" or gender_for_irreg_ending == "mf")) then
insert(data.categories, langname .. " na " .. category_plpos .. " na may di-regular na kasarian")
end
end
local function get_noun_params(is_proper)
return {
[1] = {list = "g", disallow_holes = true, required = not is_proper, default = "?", type = "genders",
flatten = true},
[2] = {list = "pl", disallow_holes = true},
["m"] = list_param,
["f"] = list_param,
["mpl"] = list_param,
["fpl"] = list_param,
["dim"] = list_param, --diminutive(s)
["aug"] = list_param, --diminutive(s)
["pej"] = list_param, --pejorative(s)
["dem"] = list_param, --demonym(s)
["fdem"] = list_param, --female demonym(s)
["meta"] = boolean_param, -- metaphonic
["nometa"] = boolean_param, -- explicitly not metaphonic
}
end
pos_functions["nouns"] = {
params = get_noun_params(),
func = do_noun,
}
pos_functions["proper nouns"] = {
params = get_noun_params("is proper"),
func = function(args, data)
do_noun(args, data, "is proper noun")
end,
}
-----------------------------------------------------------------------------------------
-- Pronouns --
-----------------------------------------------------------------------------------------
local function do_pronoun(args, data)
local lemma = data.pagename
validate_genders(args[1])
data.genders = args[1]
local function do_inflection(field, label, accel)
parse_and_insert_inflection(data, args, field, label, accel)
end
do_inflection("m", "panlalaki")
do_inflection("f", "pambabae")
do_inflection("sg", "isahan")
do_inflection("pl", "maramihan")
do_inflection("mpl", "panlalaki maramihan")
do_inflection("fpl", "pambabae maramihan")
do_inflection("n", "di-tiyak")
end
local function get_pronoun_params()
local params = {
[1] = {list = "g", disallow_holes = true, type = "genders", flatten = true}, --gender(s)
["m"] = list_param, --masculine form(s)
["f"] = list_param, --feminine form(s)
["sg"] = list_param, --singular form(s)
["pl"] = list_param, --plural form(s)
["mpl"] = list_param, --masculine plural form(s)
["fpl"] = list_param, --feminine plural form(s)
["n"] = list_param, --neuter form(s)
}
return params
end
pos_functions["pronouns"] = {
params = get_pronoun_params(),
func = do_pronoun,
}
-----------------------------------------------------------------------------------------
-- Adjectives --
-----------------------------------------------------------------------------------------
-- Handle comparatives and superlatives for adjectives and adverbs, including user-specified comparatives and
-- superlatives, default-requested comparatives/superlatives using '+', autogenerated comparatives/superlatives,
-- and hascomp=. Code is the same for adjectives and adverbs.
local function handle_adj_adv_comp(args, data, plpos, is_adv)
local lemma = data.pagename
local stem
if is_adv then
stem = com.rsub(lemma, "mente$", "")
else
stem = lemma
end
local function insert_inflection(terms, label, accel)
m_headword_utilities.insert_inflection {
headdata = data,
terms = terms,
label = label,
accel = accel and {form = accel} or nil,
}
end
local function make_absolute_superlative(special)
if is_adv then
return com.make_adverbial_absolute_superlative(stem, special)
else
return com.make_absolute_superlative(stem, special)
end
end
-- Maybe autogenerate default comparative/superlative.
local comp, sup
if args.comp and args.sup then-- comp= and sup= were given as options to the user
-- If no comp, but a non-default sup given, then add the default comparative/superlative.
-- This is useful when an absolute superlative is given.
comp = m_headword_utilities.parse_term_list_with_modifiers {
paramname = "comp",
forms = args.comp,
splitchar = ",",
}
sup = m_headword_utilities.parse_term_list_with_modifiers {
paramname = "sup",
forms = args.sup,
splitchar = ",",
}
local saw_sup_plus = false
if not comp[1] and sup[1] then
for _, supval in ipairs(sup) do
if supval.term == "+" then
saw_sup_plus = true
end
end
if not saw_sup_plus then
comp = {{term = "+"}}
insert(sup, 1, {term = "+"})
end
end
-- If comp=+, use default comparative 'mais ...', and set a default superlative if unspecified.
local saw_comp_plus = false
for _, compval in ipairs(comp) do
if compval.term == "+" then
saw_comp_plus = true
compval.term = "[[mais]] [[" .. lemma .. "]]"
end
end
if saw_comp_plus and not sup[1] then
sup = {{term = "+"}}
end
-- If sup=+ (possibly from comp=+), use default superlative 'o mais ...'. Also handle absolute superlatives.
for _, supval in ipairs(sup) do
if supval.term == "+" then
supval.term = "[[o]] [[mais]] [[" .. lemma .. "]]"
elseif supval.term == "+abs" then
supval.term = make_absolute_superlative()
elseif rfind(supval.term, "^%+abs:") then
local sp = rmatch(supval.term, "^%+abs:(.*)$")
supval.term = make_absolute_superlative(sp)
end
end
end
if args.hascomp then
if args.hascomp == "both" then
insert(data.inflections, {label = "minsan " .. glossary_link("mahahambing")})
insert(data.categories, langname .. " na mahahambing na " .. plpos)
insert(data.categories, langname .. " di mahahambing na " .. plpos)
else
local hascomp = require("Module:yesno")(args.hascomp)
if hascomp == true then
insert(data.inflections, {label = glossary_link("mahahambing")})
insert(data.categories, langname .. " na mahahambing na " .. plpos)
elseif hascomp == false then
insert(data.inflections, {label = "hindi " .. glossary_link("mahahambing")})
insert(data.categories, langname .. " na di-mahahambing na " .. plpos)
else
error("Unrecognized value for hascomp=: " .. args.hascomp)
end
end
elseif comp and comp[1] or sup and sup[1] then
insert(data.inflections, {label = glossary_link("mahahambing")})
insert(data.categories, langname .. " na mahahambing na " .. plpos)
end
insert_inflection(comp, "pahambing")
insert_inflection(sup, "pasukdol")
end
local function do_adjective(args, data, is_superlative)
local feminines = {}
local masculine_plurals = {}
local feminine_plurals = {}
-- Use "participle" not "past participle" for categories such as 'invariable participles'
local category_plpos = data.checkredlinks
if category_plpos == true then
category_plpos = data.pos_category
end
local category_pos = category_pos
if args.sp then
local romut = require(romut_module)
if not romut.allowed_special_indicators[args.sp] then
local indicators = {}
for indic, _ in pairs(romut.allowed_special_indicators) do
insert(indicators, "'" .. indic .. "'")
end
table.sort(indicators)
error("Special inflection indicator beginning can only be " ..
mw.text.listToText(indicators) .. ": " .. args.sp)
end
end
local lemma = data.pagename
local function fetch_inflections(field)
local retval = m_headword_utilities.parse_term_list_with_modifiers {
paramname = field,
forms = args[field],
splitchar = ",",
}
if not retval[1] then
return {{term = "+"}}
end
return retval
end
local function insert_inflection(terms, label, accel)
m_headword_utilities.insert_inflection {
headdata = data,
terms = terms,
label = label,
accel = accel and {form = accel} or nil,
}
end
if args.short then
insert(data.inflections, {label = "[[Apendise:Pandiwa ng Portuges|maikling pandiwari]]"})
end
if args.inv then
-- invariable adjective
insert(data.inflections, {label = glossary_link("di-nababago")})
insert(data.categories, langname .. " na di-mababanghay na " .. category_plpos)
if args.sp or args.f[1] or args.pl[1] or args.mpl[1] or args.fpl[1] then
error("Can't specify inflections with an invariable " .. category_pos)
end
elseif args.fonly then
-- feminine-only
if args.f[1] then
error("Can't specify explicit feminines with feminine-only " .. category_pos)
end
if args.pl[1] then
error("Can't specify explicit plurals with feminine-only " .. category_pos .. ", use fpl=")
end
if args.mpl[1] then
error("Can't specify explicit masculine plurals with feminine-only " .. category_pos)
end
local argsfpl = fetch_inflections("fpl")
for _, fpl in ipairs(argsfpl) do
if fpl.term == "+" then
fpl.term = make_plural(lemma, args.sp)
else
fpl.term = replace_hash_with_lemma(fpl.term, lemma)
end
insert(feminine_plurals, fpl)
end
insert(data.inflections, {label = "pambabae lamang"})
insert_inflection(feminine_plurals, "pambabae maramihan", "f|p")
else
-- Gather feminines.
for _, f in ipairs(fetch_inflections("f")) do
if f.term == "+" then
-- Generate default feminine.
f.term = com.make_feminine(lemma, args.sp)
else
f.term = replace_hash_with_lemma(f.term, lemma)
end
insert(feminines, f)
end
local fem_like_lemma = #feminines == 1 and feminines[1].term == lemma and
not m_headword_utilities.termobj_has_decorations(feminines[1])
if fem_like_lemma then
insert(data.categories, langname .. " na di-tiyak na " .. category_plpos)
end
local mpl_field = "mpl"
local fpl_field = "fpl"
if args.pl[1] then
if args.mpl[1] or args.fpl[1] then
error("Can't specify both pl= and mpl=/fpl=")
end
mpl_field = "pl"
fpl_field = "pl"
end
local argsmpl = fetch_inflections(mpl_field)
local argsfpl = fetch_inflections(fpl_field)
for _, mpl in ipairs(argsmpl) do
if mpl.term == "+" then
-- Generate default masculine plural.
mpl.term = make_plural(lemma, args.sp)
else
mpl.term = replace_hash_with_lemma(mpl.term, lemma)
end
insert(masculine_plurals, mpl)
end
for _, fpl in ipairs(argsfpl) do
if fpl.term == "+" then
for _, f in ipairs(feminines) do
-- Generate default feminine plural; f is a table.
local fplobj = m_table.shallowCopy(fpl)
fplobj.term = make_plural(f.term, args.sp)
m_headword_utilities.combine_termobj_decorations(fplobj, f)
insert(feminine_plurals, fplobj)
end
else
fpl.term = replace_hash_with_lemma(fpl.term, lemma)
insert(feminine_plurals, fpl)
end
end
local fem_pl_like_masc_pl = masculine_plurals[1] and feminine_plurals[1] and
m_table.deepEquals(masculine_plurals, feminine_plurals)
local masc_pl_like_lemma = #masculine_plurals == 1 and masculine_plurals[1].term == lemma and
not m_headword_utilities.termobj_has_decorations(masculine_plurals[1])
if fem_like_lemma and fem_pl_like_masc_pl and masc_pl_like_lemma then
-- actually invariable
insert(data.inflections, {label = glossary_link("di-nababago")})
insert(data.categories, langname .. " na di-mababanghay na " .. category_plpos)
else
-- Make sure there are feminines given and not same as lemma.
if not fem_like_lemma then
insert_inflection(feminines, "pambabae", "f|s")
elseif args.gneut then
data.genders = {"gneut"}
else
data.genders = {"mf"}
end
if fem_pl_like_masc_pl then
if args.gneut then
insert_inflection(masculine_plurals, "maramihan", "p")
else
-- This is how the Spanish module works.
-- insert_inflection(masculine_plurals, "masculine and feminine plural", "p")
insert_inflection(masculine_plurals, "maramihan", "p")
end
else
insert_inflection(masculine_plurals, "panlalaki maramihan", "m|p")
insert_inflection(feminine_plurals, "pambabae maramihan", "f|p")
end
end
end
local function parse_and_insert_adj_inflection(field, label, accel, frob)
parse_and_insert_inflection(data, args, field, label, accel, frob)
end
parse_and_insert_adj_inflection("n", "di-tiyak")
handle_adj_adv_comp(args, data, category_plpos, false)
parse_and_insert_adj_inflection("dim", "pampaliit", nil, function(term)
if term == "+" then
term = com.make_diminutive(lemma)
elseif term:find("^%+") then
term = require(romut_module).get_special_indicator(term)
term = com.make_diminutive(lemma, term)
end
return term
end)
parse_and_insert_adj_inflection("aug", "pampalaki", nil, function(term)
if term == "+" then
term = com.make_augmentative(lemma)
elseif term:find("^%+") then
term = require(romut_module).get_special_indicator(term)
term = com.make_augmentative(lemma, term)
end
return term
end)
if is_metaphonic(args, lemma) then
insert(data.inflections, {label = metaphonic_label})
insert(data.categories, langname .. " na " .. category_plpos .. " na may metaponya")
end
if args.irreg and is_superlative then
insert(data.categories, langname .. " na di-regular na pasukdol na " .. category_plpos)
end
end
local function get_adjective_params(adjtype)
local params = {
["inv"] = boolean_param, --invariable
["sp"] = true, -- special indicator: "first", "first-last", etc.
["f"] = list_param, --feminine form(s)
["pl"] = list_param, --plural override(s)
["mpl"] = list_param, --masculine plural override(s)
["fpl"] = list_param, --feminine plural override(s)
["meta"] = boolean_param, -- metaphonic
["nometa"] = boolean_param, -- explicitly not metaphonic
}
if adjtype == "base" then
params["comp"] = list_param --comparative(s)
params["sup"] = list_param --superlative(s)
params["dim"] = list_param --diminutive(s)
params["aug"] = list_param --augmentative(s)
params["fonly"] = boolean_param -- feminine only
params["gneut"] = boolean_param -- gender-neutral adjective e.g. [[latine]] (FIXME: do these exist in Portuguese?)
params["hascomp"] = true -- has comparative
end
if adjtype == "part" then
params["short"] = boolean_param -- short participle
end
if adjtype == "sup" then
params["irreg"] = boolean_param
end
if adjtype == "pron" or adjtype == "contr" then
params["n"] = list_param --neuter form(s)
end
return params
end
pos_functions["adjectives"] = {
params = get_adjective_params("base"),
func = do_adjective,
}
pos_functions["comparative adjectives"] = {
params = get_adjective_params("comp"),
func = do_adjective,
pos_category = "adjectives",
}
pos_functions["superlative adjectives"] = {
params = get_adjective_params("sup"),
func = function(args, data)
do_adjective(args, data, "is superlative")
end,
pos_category = "adjectives",
}
pos_functions["past participles"] = {
params = get_adjective_params("part"),
func = do_adjective,
redlink_pos = "participles",
}
pos_functions["determiners"] = {
params = get_adjective_params("det"),
func = do_adjective,
}
pos_functions["adjective-like pronouns"] = {
params = get_adjective_params("pron"),
func = do_adjective,
pos_category = "pronouns",
}
pos_functions["adjective-like contractions"] = {
params = get_adjective_params("contr"),
func = do_adjective,
pos_category = "contractions",
}
-----------------------------------------------------------------------------------------
-- Adverbs --
-----------------------------------------------------------------------------------------
local function do_adverb(args, data)
handle_adj_adv_comp(args, data, "adverbs", "is adv")
end
local function get_adverb_params(advtype)
local params = {}
if advtype == "base" then
params["comp"] = list_param --comparative(s)
params["sup"] = list_param --superlative(s)
params["hascomp"] = true -- has comparative
end
return params
end
pos_functions["adverbs"] = {
params = get_adverb_params("base"),
func = do_adverb,
}
pos_functions["comparative adverbs"] = {
params = get_adverb_params("comp"),
func = do_adverb,
pos_category = "adverbs",
}
pos_functions["superlative adverbs"] = {
params = get_adverb_params("sup"),
func = do_adverb,
pos_category = "adverbs",
}
-----------------------------------------------------------------------------------------
-- Verbs --
-----------------------------------------------------------------------------------------
pos_functions["verbs"] = {
params = {
[1] = true,
["pres"] = list_param, --present
["pres_qual"] = {list = "pres\1_qual", allow_holes = true},
["pres3s"] = list_param, --third-singular present
["pres3s_qual"] = {list = "pres3s\1_qual", allow_holes = true},
["pret"] = list_param, --preterite
["pret_qual"] = {list = "pret\1_qual", allow_holes = true},
["part"] = list_param, --participle
["part_qual"] = {list = "part\1_qual", allow_holes = true},
["short_part"] = list_param, --short participle
["short_part_qual"] = {list = "short_part\1_qual", allow_holes = true},
["noautolinktext"] = boolean_param,
["noautolinkverb"] = boolean_param,
["attn"] = boolean_param,
},
func = function(args, data, frame)
local preses, preses_3s, prets, parts, short_parts
if args.attn then
insert(data.categories, "Requests for attention concerning " .. langname)
return
end
local pt_verb = require(pt_verb_module)
local alternant_multiword_spec = pt_verb.do_generate_forms(args, "pt-verb", data.heads[1])
local specforms = alternant_multiword_spec.forms
local function slot_exists(slot)
return specforms[slot] and #specforms[slot] > 0
end
local function do_finite(slot_tense, label_tense)
-- Use pres_3s if it exists and pres_1s doesn't exist (e.g. impersonal verbs); similarly for pres_3p
-- (only3p verbs); but fall back to pres_1s if neither pres_1s nor pres_3s nor pres_3p exist (e.g.
-- [[empedernir]]).
local has_1s = slot_exists(slot_tense .. "_1s")
local has_3s = slot_exists(slot_tense .. "_3s")
local has_3p = slot_exists(slot_tense .. "_3p")
if has_1s or (not has_3s and not has_3p) then
return {
slot = slot_tense .. "_1s",
label = ("unang panauhan isahan %s"):format(label_tense),
}, true
elseif has_3s then
return {
slot = slot_tense .. "_3s",
label = ("ikatlong panauhan isahan %s"):format(label_tense),
}, false
else
return {
slot = slot_tense .. "_3p",
label = ("ikatlong panauhan maramihan %s"):format(label_tense),
}, false
end
end
local did_pres_1s
preses, did_pres_1s = do_finite("pres", "pangkasalukuyan")
preses_3s = {
slot = "pres_3s",
label = "ikatlong panauhan isahan pangkasalukuyan",
}
prets = do_finite("pret", "preterite")
parts = {
slot = "pp_ms",
label = "pandiwaring pangnakaraan",
}
short_parts = {
slot = "short_pp_ms",
label = "maikling pandiwaring pangnakaraan",
}
if args.pres[1] or args.pres3s[1] or args.pret[1] or args.part[1] or args.short_part[1] then
track("verb-old-multiarg")
end
local function strip_brackets(qualifiers)
if not qualifiers then
return nil
end
local stripped_qualifiers = {}
for _, qualifier in ipairs(qualifiers) do
local stripped_qualifier = qualifier:match("^%[(.*)%]$")
if not stripped_qualifier then
error("Internal error: Qualifier should be surrounded by brackets at this stage: " .. qualifier)
end
insert(stripped_qualifiers, stripped_qualifier)
end
return stripped_qualifiers
end
local function do_verb_form(args, qualifiers, slot_desc, skip_if_empty)
local forms
local to_insert
if #args == 0 then
forms = specforms[slot_desc.slot]
if not forms or #forms == 0 then
if skip_if_empty then
return
end
forms = {{form = "-"}}
end
elseif #args == 1 and args[1] == "-" then
forms = {{form = "-"}}
else
forms = {}
for i, arg in ipairs(args) do
local qual = qualifiers[i]
if qual then
-- FIXME: It's annoying we have to add brackets and strip them out later. The inflection
-- code adds all footnotes with brackets around them; we should change this.
qual = {"[" .. qual .. "]"}
end
local form = arg
if not args.noautolinkverb then
-- [[Module:inflection utilities]] already loaded by [[Module:pt-verb]]
form = require(inflection_utilities_module).add_links(form)
end
insert(forms, {form = form, footnotes = qual})
end
end
if forms[1].form == "-" then
to_insert = {label = "walang " .. slot_desc.label}
else
local into_table = {label = slot_desc.label}
for _, form in ipairs(forms) do
local qualifiers = strip_brackets(form.footnotes)
-- Strip redundant brackets surrounding entire form. These may get generated e.g.
-- if we use the angle bracket notation with a single word.
local stripped_form = rmatch(form.form, "^%[%[([^%[%]]*)%]%]$") or form.form
stripped_form = pt_verb.remove_variant_codes(stripped_form)
-- Don't include accelerators if brackets remain in form, as the result will be wrong.
-- FIXME: For now, don't include accelerators. We should use the new {{pt-verb form of}} once
-- implemented.
-- local this_accel = not stripped_form:find("%[%[") and accel or nil
local this_accel = nil
insert(into_table, {term = stripped_form, q = qualifiers, accel = this_accel})
end
to_insert = into_table
end
insert(data.inflections, to_insert)
end
local skip_pres_if_empty
if alternant_multiword_spec.no_pres1_and_sub then
insert(data.inflections, {label = "walang unang panauhan isahan pangkasalukuyan"})
insert(data.inflections, {label = "walang pasakali pangkasalukuyan"})
end
if alternant_multiword_spec.no_pres_stressed then
insert(data.inflections, {label = "walang nakadiin na paturol o pasakali pangkasalukuyan"})
skip_pres_if_empty = true
end
if alternant_multiword_spec.only3s then
insert(data.inflections, {label = glossary_link("di-panao")})
elseif alternant_multiword_spec.only3sp then
insert(data.inflections, {label = "ikatlong panauhan lamang"})
elseif alternant_multiword_spec.only3p then
insert(data.inflections, {label = "ikatlong panauhan maramihan lamang"})
end
local has_vowel_alt
if alternant_multiword_spec.vowel_alt then
for _, vowel_alt in ipairs(alternant_multiword_spec.vowel_alt) do
if vowel_alt ~= "+" and vowel_alt ~= "í" and vowel_alt ~= "ú" then
has_vowel_alt = true
break
end
end
end
do_verb_form(args.pres, args.pres_qual, preses, skip_pres_if_empty)
-- We want to include both the pres_1s and pres_3s if there is a vowel alternation in the present singular. But
-- we don't want to redundantly include the pres_3s if we already included it.
if did_pres_1s and has_vowel_alt then
do_verb_form(args.pres3s, args.pres3s_qual, preses_3s, skip_pres_if_empty)
end
do_verb_form(args.pret, args.pret_qual, prets)
do_verb_form(args.part, args.part_qual, parts)
do_verb_form(args.short_part, args.short_part_qual, short_parts, "skip if empty")
-- Add categories.
for _, cat in ipairs(alternant_multiword_spec.categories) do
insert(data.categories, cat)
end
-- If the user didn't explicitly specify head=, or specified exactly one head (not 2+) and we were able to
-- incorporate any links in that head into the 1= specification, use the infinitive generated by
-- [[Module:pt-verb]] in place of the user-specified or auto-generated head. This was copied from
-- [[Module:it-headword]], where doing this gets accents marked on the verb(s). We don't have accents marked on
-- the verb but by doing this we do get any footnotes on the infinitive propagated here. Don't do this if the
-- user gave multiple heads or gave a head with a multiword-linked verbal expression such as Italian
-- '[[dare esca]] [[al]] [[fuoco]]' (FIXME: give Portuguese equivalent).
if #data.user_specified_heads == 0 or (
#data.user_specified_heads == 1 and alternant_multiword_spec.incorporated_headword_head_into_lemma
) then
data.heads = {}
for _, lemma_obj in ipairs(alternant_multiword_spec.forms.infinitive_linked) do
local quals, refs = require(inflection_utilities_module).
convert_footnotes_to_qualifiers_and_references(lemma_obj.footnotes)
insert(data.heads, {term = lemma_obj.form, q = quals, refs = refs})
end
end
end
}
-----------------------------------------------------------------------------------------
-- Suffix forms --
-----------------------------------------------------------------------------------------
pos_functions["suffix forms"] = {
params = {
[1] = {required = true, list = true, disallow_holes = true},
["g"] = {list = true, disallow_holes = true, type = "genders", flatten = true},
},
func = function(args, data)
validate_genders(args.g)
data.genders = args.g
local suffix_type = {}
for _, typ in ipairs(args[1]) do
insert(suffix_type, "hulaping gumagawa ng " .. typ)
end
insert(data.inflections, {label = "anyong di-lema ng " .. m_table.serialCommaJoin(suffix_type, {conj = "o"})})
end,
}
return export
5sxw9gi12gmwq4z0xh2mx1wkk06lau0
178623
178622
2026-11-09T06:04:22Z
GinawaSaHapon
3989
178623
Scribunto
text/plain
-- This module contains code for Portuguese headword templates.
-- Templates covered are:
-- * {{pt-noun}}, {{pt-proper noun}};
-- * {{pt-verb}};
-- * {{pt-adj}}, {{pt-adj-comp}}, {{pt-adj-sup}};
-- * {{pt-det}};
-- * {{pt-pron-adj}};
-- * {{pt-contr-adj}};
-- * {{pt-pp}};
-- * {{pt-cardinal}};
-- * {{pt-adv}}.
-- See [[Module:pt-verb]] for Portuguese conjugation templates.
local export = {}
local pos_functions = {}
local force_cat = false -- for testing; if true, categories appear in non-mainspace pages
local m_strutils = require("Module:string utilities")
local require_when_needed = require("Module:utilities/require when needed")
local m_table = require("Module:table")
local com = require("Module:pt-common")
local en_utilities_module = "Module:en-utilities"
local headword_utilities_module = "Module:headword utilities"
local inflection_utilities_module = "Module:inflection utilities"
local romut_module = "Module:romance utilities"
local pt_verb_module = "Module:pt-verb"
local lang = require("Module:languages").getByCode("pt")
local langname = lang:getCanonicalName()
local m_en_utilities = require_when_needed(en_utilities_module)
local m_headword_utilities = require_when_needed(headword_utilities_module)
local glossary_link = require_when_needed(headword_utilities_module, "glossary_link")
local insert = table.insert
local rfind = m_strutils.find
local rmatch = m_strutils.match
local ulower = m_strutils.lower
local usub = m_strutils.sub
local uupper = m_strutils.upper
-- When followed by a hyphen in a hyphenated compound, the hyphen will be included with the prefix when linked.
local include_hyphen_prefixes = m_table.listToSet {
"ab",
"afro",
"anarco",
"anglo",
"ântero",
"anti",
"auto",
"contra",
"ex",
"franco",
"hiper",
"infra",
"inter",
"intra",
"macro",
"micro",
"neo",
"pan",
"pós",
"pré",
"pró",
"proto",
"sobre",
"sub",
"super",
"vice",
}
-----------------------------------------------------------------------------------------
-- Utility functions --
-----------------------------------------------------------------------------------------
local function track(page)
require("Module:debug/track")("pt-headword/" .. page)
return true
end
local list_param = {list = true, disallow_holes = true}
local boolean_param = {type = "boolean"}
local metaphonic_label = "metaponiko"
local function make_plural(sg, special)
local pl = com.make_plural(sg, special)
if not pl then
error(("Internal error: Unable to generate default plural of '%s'"):format(sg))
end
return pl
end
-- Parse and insert an inflection not requiring additional processing into `data.inflections`. The raw arguments come
-- from `args[field]`, which is parsed for inline modifiers. `label` is the label that the inflections are given;
-- `plpos` is the plural part of speech, used in [[Category:LANGNAME PLPOS with red links in their headword lines]].
-- `accel` is the accelerator form, or nil.
local function parse_and_insert_inflection(data, args, field, label, accel, frob)
m_headword_utilities.parse_and_insert_inflection {
headdata = data,
forms = args[field],
paramname = field,
splitchar = ",",
include_mods = {"g"},
label = label,
accel = accel and {form = accel} or nil,
frob = frob,
}
end
-- The main entry point.
-- This is the only function that can be invoked from a template.
function export.show(frame)
local poscat = frame.args[1] or error("Part of speech has not been specified. Please pass parameter 1 to the module invocation.")
local parargs = frame:getParent().args
local params = {
["head"] = list_param,
["id"] = true,
["json"] = boolean_param,
["splithyph"] = boolean_param,
["nolinkhead"] = boolean_param,
["nolink"] = {type = "boolean", alias_of = "nolinkhead"},
["abbr"] = list_param,
["pagename"] = true, -- for testing
}
if pos_functions[poscat] then
for key, val in pairs(pos_functions[poscat].params) do
params[key] = val
end
end
local args = require("Module:parameters").process(parargs, params)
local pagename = args.pagename or mw.loadData("Module:headword/data").pagename
local user_specified_heads = args.head
local heads = user_specified_heads
if args.nolinkhead then
if #heads == 0 then
heads = {pagename}
end
else
local romut = require(romut_module)
local auto_linked_head = romut.add_links_to_multiword_term(pagename, args.splithyph, nil,
include_hyphen_prefixes)
if #heads == 0 then
heads = {auto_linked_head}
else
for i, head in ipairs(heads) do
if head:find("^~") then
head = romut.apply_link_modifiers(auto_linked_head, usub(head, 2))
heads[i] = head
end
end
end
end
local data = {
lang = lang,
pos_category = pos_functions[poscat] and pos_functions[poscat].pos_category or poscat,
categories = {},
heads = heads,
user_specified_heads = user_specified_heads,
no_redundant_head_cat = #user_specified_heads == 0,
genders = {},
inflections = {},
pagename = pagename,
id = args.id,
force_cat_output = force_cat,
checkredlinks = pos_functions[poscat] and pos_functions[poscat].redlink_pos or true,
}
if pagename:find("^%-") and poscat ~= "suffix forms" then
data.is_suffix = true
data.pos_category = "suffixes"
data.checkredlinks = true
local singular_poscat = poscat
insert(data.categories, langname .. " na hulaping gumagawa ng " .. singular_poscat)
insert(data.inflections, {label = "hulaping gumagawa ng " .. singular_poscat})
end
local pagename_lower = ulower(pagename)
for _, ch in ipairs{"gü", "jh", "kh", "ph", "qü", "sh", "th", "tl", "ts", "tz", "wh", "zh"} do
if pagename_lower:find(ch) then
insert(data.categories, langname .. " na kataga na binaybay na may " .. uupper(ch))
end
end
if pos_functions[poscat] then
pos_functions[poscat].func(args, data)
end
parse_and_insert_inflection(data, args, "abbr", "daglat")
if args.json then
return require("Module:JSON").toJSON(data)
end
return require("Module:headword").full_headword(data)
end
local function replace_hash_with_lemma(term, lemma)
-- If there is a % sign in the lemma, we have to replace it with %% so it doesn't get interpreted as a capture replace
-- expression.
lemma = lemma:gsub("%%", "%%%%")
return (term:gsub("#", lemma))
end
local function is_metaphonic(args, lemma)
if args.nometa then
return false
end
if args.meta then
return true
end
-- Anything in -oso with a preceding vowel (e.g. [[gostoso]], [[curioso]]) is normally metaphonic.
return rfind(lemma, com.V .. ".*oso$")
end
-----------------------------------------------------------------------------------------
-- Nouns --
-----------------------------------------------------------------------------------------
local allowed_genders = m_table.listToSet(
{"m", "f", "mf", "mfbysense", "mfequiv", "gneut", "n", "m-p", "f-p", "mf-p", "mfbysense-p", "mfequiv-p", "gneut-p", "n-p", "?", "?-p"}
)
local function validate_genders(genders)
for _, g in ipairs(genders) do
if type(g) == "table" then
g = g.spec
end
if not allowed_genders[g] then
error("Unrecognized gender: " .. g)
end
end
end
local function do_noun(args, data, is_proper)
local is_plurale_tantum = false
local has_singular = false
local category_plpos = data.checkredlinks
if category_plpos == true then
category_plpos = data.pos_category
end
local category_pos = m_en_utilities.singularize(category_plpos)
validate_genders(args[1])
data.genders = args[1]
local saw_m = false
local saw_f = false
local saw_gneut = false
local gender_for_irreg_ending
-- Check for specific genders and pluralia tantum.
for _, g in ipairs(args[1]) do
if type(g) == "table" then
g = g.spec
end
if g:find("-p$") then
is_plurale_tantum = true
else
has_singular = true
if g == "m" or g == "mf" or g == "mfbysense" then
saw_m = true
end
if g == "f" or g == "mf" or g == "mfbysense" then
saw_f = true
end
if g == "gneut" then
saw_gneut = true
end
end
end
if saw_m and saw_f then
gender_for_irreg_ending = "mf"
elseif saw_f then
gender_for_irreg_ending = "f"
else
gender_for_irreg_ending = "m"
end
local lemma = data.pagename
local plurals = {}
local function insert_noun_inflection(terms, label, accel)
m_headword_utilities.insert_inflection {
headdata = data,
terms = terms,
label = label,
accel = accel and {form = accel} or nil,
}
end
if is_plurale_tantum and not has_singular then
if args[2][1] then
error("Can't specify plurals of plurale tantum " .. category_pos)
end
insert(data.inflections, {label = glossary_link("maramihan lamang")})
else
plurals = m_headword_utilities.parse_term_list_with_modifiers {
paramname = {2, "pl"},
forms = args[2],
splitchar = ",",
}
-- Check for special plural signals
local mode = nil
local pl1 = plurals[1]
if pl1 and #pl1.term == 1 then
mode = pl1.term
if mode == "?" or mode == "!" or mode == "-" or mode == "~" then
pl1.term = nil
if m_headword_utilities.termobj_has_decorations(pl1) then
error(("Can't specify inline modifiers with plural code '%s'"):format(mode))
end
table.remove(plurals, 1) -- Remove the mode parameter
elseif mode ~= "+" and mode ~= "#" then
error(("Unexpected plural code '%s'"):format(mode))
end
end
if is_plurale_tantum then
-- both singular and plural
insert(data.inflections, {label = "minsan " .. glossary_link("maramihan lamang") .. ", paiba-iba"})
end
if mode == "?" then
-- Plural is unknown
insert(data.categories, langname .. " na " .. category_plpos .. " na may di-alam o di-tukoy na anyong maramihan")
elseif mode == "!" then
-- Plural is not attested
insert(data.inflections, {label = "di-kumpirmado ang maramihan"})
insert(data.categories, langname .. " na " .. category_plpos .. " na may di-kumpirmadong anyong maramihan")
if plurals[1] then
error("Can't specify any plurals along with unattested plural code '!'")
end
elseif mode == "-" then
-- Uncountable noun; may occasionally have a plural
insert(data.categories, langname .. " na di-mabibilang na " .. category_plpos)
-- If plural forms were given explicitly, then show "usually"
if plurals[1] then
insert(data.inflections, {label = "madalas " .. glossary_link("di-mabibilang")})
insert(data.categories, langname .. " na mabibilang na " .. category_plpos)
else
insert(data.inflections, {label = glossary_link("di-mabibilang")})
end
else
-- Countable or mixed countable/uncountable
if not plurals[1] and not is_proper then
plurals[1] = {term = "+"}
end
if mode == "~" then
-- Mixed countable/uncountable noun, always has a plural
insert(data.inflections, {label = glossary_link("mabibilang") .. " at " .. glossary_link("di-mabibilang")})
insert(data.categories, langname .. " na di-mabibilang na " .. category_plpos)
insert(data.categories, langname .. " na mabibilang na " .. category_plpos)
elseif plurals[1] then
-- Countable nouns
insert(data.categories, langname .. " na mabibilang na " .. category_plpos)
else
-- Uncountable nouns
insert(data.categories, langname .. " uncountable " .. category_plpos)
end
end
-- Gather plurals, handling requests for default plurals.
local has_default_or_hash = false
for _, pl in ipairs(plurals) do
if pl.term:find("^%+") or pl.term:find("#") then
has_default_or_hash = true
break
end
end
if has_default_or_hash then
for _, pl in ipairs(plurals) do
if pl.term == "+" then
pl.term = make_plural(lemma)
elseif pl.term:find("^%+") then
pl.term = require(romut_module).get_special_indicator(pl.term)
pl.term = make_plural(lemma, pl.term)
else
pl.term = replace_hash_with_lemma(pl.term, lemma)
end
end
end
local pl1 = plurals[1]
if pl1 and not plurals[2] and pl1.term == lemma then
insert(data.inflections, {label = glossary_link("di-nababago"),
q = pl1.q, qq = pl1.qq, l = pl1.l, ll = pl1.ll, refs = pl1.refs
})
insert(data.categories, langname .. " na di-mababanghay na " .. category_plpos)
else
insert_noun_inflection(plurals, "maramihan", "p")
end
if plurals[2] then
insert(data.categories, langname .. " na " .. category_plpos .. " na may maraming anyong maramihan")
end
end
-- Gather masculines/feminines. For each one, generate the corresponding plural. `field` is the name of the field
-- containing the masculine or feminine forms (normally "m" or "f"); `inflect` is a function of one or two arguments
-- to generate the default masculine or feminine from the lemma (the arguments are the lemma and optionally a
-- "special" flag to indicate how to handle multiword lemmas, and the function is normally make_feminine or
-- make_masculine from [[Module:pt-common]]); and `default_plurals` is a list into which the corresponding default
-- plurals of the gathered or generated masculine or feminine forms are stored.
local function handle_mf(field, inflect, default_plurals)
local specials = {} -- Tracks the specific plural indicators for each entry in a multi-item list
local term_index = 0
local mfs = m_headword_utilities.parse_term_list_with_modifiers {
paramname = field,
forms = args[field],
splitchar = ",",
frob = function(term)
term_index = term_index + 1
if term == "+" then
-- Generate default masculine/feminine.
term = inflect(lemma)
else
term = replace_hash_with_lemma(term, lemma)
end
local special = require(romut_module).get_special_indicator(term)
if special then
specials[term_index] = special -- Store indicator unique to this specific item's index
term = inflect(lemma, special)
end
return term
end
}
for i, mf in ipairs(mfs) do
local plobj = m_table.shallowCopy(mf)
local term_special = specials[i] -- Retrieve indicator matching this exact singular's index
plobj.term = make_plural(mf.term, term_special)
-- Add an accelerator for each masculine/feminine plural whose lemma is the corresponding singular, so that
-- the accelerated entry that is generated has a definition that looks like
-- # {{plural of|pt|MFSING}}
plobj.accel = {form = "p", lemma = mf.term}
insert(default_plurals, plobj)
end
return mfs
end
local feminine_plurals = {}
local feminines = handle_mf("f", com.make_feminine, feminine_plurals)
local masculine_plurals = {}
local masculines = handle_mf("m", com.make_masculine, masculine_plurals)
local function handle_mf_plural(mfplfield, default_plurals, singulars)
local mfpl = m_headword_utilities.parse_term_list_with_modifiers {
paramname = mfplfield,
forms = args[mfplfield],
splitchar = ",",
}
if is_plurale_tantum then
return mfpl
end
local new_mfpls = {}
local saw_plus
for i, mfpl in ipairs(mfpl) do
local accel
if #mfpl == #singulars then
-- If same number of overriding masculine/feminine plurals as singulars, assume each plural goes with
-- the corresponding singular and use each corresponding singular as the lemma in the accelerator. The
-- generated entry will have
-- # {{plural of|pt|SINGULAR}}
-- as the definition.
accel = {form = "p", lemma = singulars[i].term}
else
accel = nil
end
if mfpl.term == "+" then
-- We should never see + twice. If we do, it will lead to problems since we overwrite the values of
-- default_plurals the first time around.
if saw_plus then
error(("Saw + twice when handling %s="):format(mfplfield))
end
saw_plus = true
if not default_plurals[1] then
-- FIXME: Can this happen? Not in corresponding Spanish code and the old Portuguese code tried to
-- handle this condition by generating the default plural from the lemma.
error("Internal error: Something wrong, no generated default m/f plurals at this stage")
end
for _, defpl in ipairs(default_plurals) do
-- defpl is already a table and has an accel field
m_headword_utilities.combine_termobj_decorations(defpl, mfpl)
insert(new_mfpls, defpl)
end
elseif mfpl.term:find("^%+") then
mfpl.term = require(romut_module).get_special_indicator(mfpl.term)
for _, mf in ipairs(singulars) do
local mfplobj = m_table.shallowCopy(mfpl)
mfplobj.term = make_plural(mf.term, mfpl.term)
mfplobj.accel = accel
m_headword_utilities.combine_termobj_decorations(mfplobj, mf)
insert(new_mfpls, mfplobj)
end
else
mfpl.accel = accel
mfpl.term = replace_hash_with_lemma(mfpl.term, lemma)
insert(new_mfpls, mfpl)
end
end
return new_mfpls
end
if args.fpl[1] then
-- Override any existing feminine plurals.
feminine_plurals = handle_mf_plural("fpl", feminine_plurals, feminines)
end
if args.mpl[1] then
-- Override any existing masculine plurals.
masculine_plurals = handle_mf_plural("mpl", masculine_plurals, masculines)
end
local function parse_and_insert_noun_inflection(field, label, accel)
parse_and_insert_inflection(data, args, field, label, accel)
end
insert_noun_inflection(feminines, "pambabae", "f")
insert_noun_inflection(feminine_plurals, "pambabae maramihan")
insert_noun_inflection(masculines, "panlalaki")
insert_noun_inflection(masculine_plurals, "panlalaki maramihan")
if is_metaphonic(args, lemma) then
insert(data.inflections, {label = metaphonic_label})
insert(data.categories, langname .. " na " .. category_plpos .. " na may metaponya")
end
parse_and_insert_noun_inflection("dim", "pampaliit")
parse_and_insert_noun_inflection("aug", "pampalaki")
parse_and_insert_noun_inflection("pej", "pampanira")
parse_and_insert_noun_inflection("dem", "ngalang-lipi")
parse_and_insert_noun_inflection("fdem", "pambabae ngalang-lipi")
-- Maybe add category 'Portuguese nouns with irregular gender' (or similar)
local irreg_gender_lemma = com.rsub(lemma, " .*", "") -- only look at first word
if (rfind(irreg_gender_lemma, "[^ã]o$") and (gender_for_irreg_ending == "f" or gender_for_irreg_ending == "mf")) or
(irreg_gender_lemma:find("a$") and (gender_for_irreg_ending == "m" or gender_for_irreg_ending == "mf")) then
insert(data.categories, langname .. " na " .. category_plpos .. " na may di-regular na kasarian")
end
end
local function get_noun_params(is_proper)
return {
[1] = {list = "g", disallow_holes = true, required = not is_proper, default = "?", type = "genders",
flatten = true},
[2] = {list = "pl", disallow_holes = true},
["m"] = list_param,
["f"] = list_param,
["mpl"] = list_param,
["fpl"] = list_param,
["dim"] = list_param, --diminutive(s)
["aug"] = list_param, --diminutive(s)
["pej"] = list_param, --pejorative(s)
["dem"] = list_param, --demonym(s)
["fdem"] = list_param, --female demonym(s)
["meta"] = boolean_param, -- metaphonic
["nometa"] = boolean_param, -- explicitly not metaphonic
}
end
pos_functions["pangngalan"] = {
params = get_noun_params(),
func = do_noun,
}
pos_functions["pangngalang pantangi"] = {
params = get_noun_params("is proper"),
func = function(args, data)
do_noun(args, data, "is proper noun")
end,
}
-----------------------------------------------------------------------------------------
-- Pronouns --
-----------------------------------------------------------------------------------------
local function do_pronoun(args, data)
local lemma = data.pagename
validate_genders(args[1])
data.genders = args[1]
local function do_inflection(field, label, accel)
parse_and_insert_inflection(data, args, field, label, accel)
end
do_inflection("m", "panlalaki")
do_inflection("f", "pambabae")
do_inflection("sg", "isahan")
do_inflection("pl", "maramihan")
do_inflection("mpl", "panlalaki maramihan")
do_inflection("fpl", "pambabae maramihan")
do_inflection("n", "di-tiyak")
end
local function get_pronoun_params()
local params = {
[1] = {list = "g", disallow_holes = true, type = "genders", flatten = true}, --gender(s)
["m"] = list_param, --masculine form(s)
["f"] = list_param, --feminine form(s)
["sg"] = list_param, --singular form(s)
["pl"] = list_param, --plural form(s)
["mpl"] = list_param, --masculine plural form(s)
["fpl"] = list_param, --feminine plural form(s)
["n"] = list_param, --neuter form(s)
}
return params
end
pos_functions["panghalip"] = {
params = get_pronoun_params(),
func = do_pronoun,
}
-----------------------------------------------------------------------------------------
-- Adjectives --
-----------------------------------------------------------------------------------------
-- Handle comparatives and superlatives for adjectives and adverbs, including user-specified comparatives and
-- superlatives, default-requested comparatives/superlatives using '+', autogenerated comparatives/superlatives,
-- and hascomp=. Code is the same for adjectives and adverbs.
local function handle_adj_adv_comp(args, data, plpos, is_adv)
local lemma = data.pagename
local stem
if is_adv then
stem = com.rsub(lemma, "mente$", "")
else
stem = lemma
end
local function insert_inflection(terms, label, accel)
m_headword_utilities.insert_inflection {
headdata = data,
terms = terms,
label = label,
accel = accel and {form = accel} or nil,
}
end
local function make_absolute_superlative(special)
if is_adv then
return com.make_adverbial_absolute_superlative(stem, special)
else
return com.make_absolute_superlative(stem, special)
end
end
-- Maybe autogenerate default comparative/superlative.
local comp, sup
if args.comp and args.sup then-- comp= and sup= were given as options to the user
-- If no comp, but a non-default sup given, then add the default comparative/superlative.
-- This is useful when an absolute superlative is given.
comp = m_headword_utilities.parse_term_list_with_modifiers {
paramname = "comp",
forms = args.comp,
splitchar = ",",
}
sup = m_headword_utilities.parse_term_list_with_modifiers {
paramname = "sup",
forms = args.sup,
splitchar = ",",
}
local saw_sup_plus = false
if not comp[1] and sup[1] then
for _, supval in ipairs(sup) do
if supval.term == "+" then
saw_sup_plus = true
end
end
if not saw_sup_plus then
comp = {{term = "+"}}
insert(sup, 1, {term = "+"})
end
end
-- If comp=+, use default comparative 'mais ...', and set a default superlative if unspecified.
local saw_comp_plus = false
for _, compval in ipairs(comp) do
if compval.term == "+" then
saw_comp_plus = true
compval.term = "[[mais]] [[" .. lemma .. "]]"
end
end
if saw_comp_plus and not sup[1] then
sup = {{term = "+"}}
end
-- If sup=+ (possibly from comp=+), use default superlative 'o mais ...'. Also handle absolute superlatives.
for _, supval in ipairs(sup) do
if supval.term == "+" then
supval.term = "[[o]] [[mais]] [[" .. lemma .. "]]"
elseif supval.term == "+abs" then
supval.term = make_absolute_superlative()
elseif rfind(supval.term, "^%+abs:") then
local sp = rmatch(supval.term, "^%+abs:(.*)$")
supval.term = make_absolute_superlative(sp)
end
end
end
if args.hascomp then
if args.hascomp == "both" then
insert(data.inflections, {label = "minsan " .. glossary_link("mahahambing")})
insert(data.categories, langname .. " na mahahambing na " .. plpos)
insert(data.categories, langname .. " di mahahambing na " .. plpos)
else
local hascomp = require("Module:yesno")(args.hascomp)
if hascomp == true then
insert(data.inflections, {label = glossary_link("mahahambing")})
insert(data.categories, langname .. " na mahahambing na " .. plpos)
elseif hascomp == false then
insert(data.inflections, {label = "hindi " .. glossary_link("mahahambing")})
insert(data.categories, langname .. " na di-mahahambing na " .. plpos)
else
error("Unrecognized value for hascomp=: " .. args.hascomp)
end
end
elseif comp and comp[1] or sup and sup[1] then
insert(data.inflections, {label = glossary_link("mahahambing")})
insert(data.categories, langname .. " na mahahambing na " .. plpos)
end
insert_inflection(comp, "pahambing")
insert_inflection(sup, "pasukdol")
end
local function do_adjective(args, data, is_superlative)
local feminines = {}
local masculine_plurals = {}
local feminine_plurals = {}
-- Use "participle" not "past participle" for categories such as 'invariable participles'
local category_plpos = data.checkredlinks
if category_plpos == true then
category_plpos = data.pos_category
end
local category_pos = category_pos
if args.sp then
local romut = require(romut_module)
if not romut.allowed_special_indicators[args.sp] then
local indicators = {}
for indic, _ in pairs(romut.allowed_special_indicators) do
insert(indicators, "'" .. indic .. "'")
end
table.sort(indicators)
error("Special inflection indicator beginning can only be " ..
mw.text.listToText(indicators) .. ": " .. args.sp)
end
end
local lemma = data.pagename
local function fetch_inflections(field)
local retval = m_headword_utilities.parse_term_list_with_modifiers {
paramname = field,
forms = args[field],
splitchar = ",",
}
if not retval[1] then
return {{term = "+"}}
end
return retval
end
local function insert_inflection(terms, label, accel)
m_headword_utilities.insert_inflection {
headdata = data,
terms = terms,
label = label,
accel = accel and {form = accel} or nil,
}
end
if args.short then
insert(data.inflections, {label = "[[Apendise:Pandiwa ng Portuges|maikling pandiwari]]"})
end
if args.inv then
-- invariable adjective
insert(data.inflections, {label = glossary_link("di-nababago")})
insert(data.categories, langname .. " na di-mababanghay na " .. category_plpos)
if args.sp or args.f[1] or args.pl[1] or args.mpl[1] or args.fpl[1] then
error("Can't specify inflections with an invariable " .. category_pos)
end
elseif args.fonly then
-- feminine-only
if args.f[1] then
error("Can't specify explicit feminines with feminine-only " .. category_pos)
end
if args.pl[1] then
error("Can't specify explicit plurals with feminine-only " .. category_pos .. ", use fpl=")
end
if args.mpl[1] then
error("Can't specify explicit masculine plurals with feminine-only " .. category_pos)
end
local argsfpl = fetch_inflections("fpl")
for _, fpl in ipairs(argsfpl) do
if fpl.term == "+" then
fpl.term = make_plural(lemma, args.sp)
else
fpl.term = replace_hash_with_lemma(fpl.term, lemma)
end
insert(feminine_plurals, fpl)
end
insert(data.inflections, {label = "pambabae lamang"})
insert_inflection(feminine_plurals, "pambabae maramihan", "f|p")
else
-- Gather feminines.
for _, f in ipairs(fetch_inflections("f")) do
if f.term == "+" then
-- Generate default feminine.
f.term = com.make_feminine(lemma, args.sp)
else
f.term = replace_hash_with_lemma(f.term, lemma)
end
insert(feminines, f)
end
local fem_like_lemma = #feminines == 1 and feminines[1].term == lemma and
not m_headword_utilities.termobj_has_decorations(feminines[1])
if fem_like_lemma then
insert(data.categories, langname .. " na di-tiyak na " .. category_plpos)
end
local mpl_field = "mpl"
local fpl_field = "fpl"
if args.pl[1] then
if args.mpl[1] or args.fpl[1] then
error("Can't specify both pl= and mpl=/fpl=")
end
mpl_field = "pl"
fpl_field = "pl"
end
local argsmpl = fetch_inflections(mpl_field)
local argsfpl = fetch_inflections(fpl_field)
for _, mpl in ipairs(argsmpl) do
if mpl.term == "+" then
-- Generate default masculine plural.
mpl.term = make_plural(lemma, args.sp)
else
mpl.term = replace_hash_with_lemma(mpl.term, lemma)
end
insert(masculine_plurals, mpl)
end
for _, fpl in ipairs(argsfpl) do
if fpl.term == "+" then
for _, f in ipairs(feminines) do
-- Generate default feminine plural; f is a table.
local fplobj = m_table.shallowCopy(fpl)
fplobj.term = make_plural(f.term, args.sp)
m_headword_utilities.combine_termobj_decorations(fplobj, f)
insert(feminine_plurals, fplobj)
end
else
fpl.term = replace_hash_with_lemma(fpl.term, lemma)
insert(feminine_plurals, fpl)
end
end
local fem_pl_like_masc_pl = masculine_plurals[1] and feminine_plurals[1] and
m_table.deepEquals(masculine_plurals, feminine_plurals)
local masc_pl_like_lemma = #masculine_plurals == 1 and masculine_plurals[1].term == lemma and
not m_headword_utilities.termobj_has_decorations(masculine_plurals[1])
if fem_like_lemma and fem_pl_like_masc_pl and masc_pl_like_lemma then
-- actually invariable
insert(data.inflections, {label = glossary_link("di-nababago")})
insert(data.categories, langname .. " na di-mababanghay na " .. category_plpos)
else
-- Make sure there are feminines given and not same as lemma.
if not fem_like_lemma then
insert_inflection(feminines, "pambabae", "f|s")
elseif args.gneut then
data.genders = {"gneut"}
else
data.genders = {"mf"}
end
if fem_pl_like_masc_pl then
if args.gneut then
insert_inflection(masculine_plurals, "maramihan", "p")
else
-- This is how the Spanish module works.
-- insert_inflection(masculine_plurals, "masculine and feminine plural", "p")
insert_inflection(masculine_plurals, "maramihan", "p")
end
else
insert_inflection(masculine_plurals, "panlalaki maramihan", "m|p")
insert_inflection(feminine_plurals, "pambabae maramihan", "f|p")
end
end
end
local function parse_and_insert_adj_inflection(field, label, accel, frob)
parse_and_insert_inflection(data, args, field, label, accel, frob)
end
parse_and_insert_adj_inflection("n", "di-tiyak")
handle_adj_adv_comp(args, data, category_plpos, false)
parse_and_insert_adj_inflection("dim", "pampaliit", nil, function(term)
if term == "+" then
term = com.make_diminutive(lemma)
elseif term:find("^%+") then
term = require(romut_module).get_special_indicator(term)
term = com.make_diminutive(lemma, term)
end
return term
end)
parse_and_insert_adj_inflection("aug", "pampalaki", nil, function(term)
if term == "+" then
term = com.make_augmentative(lemma)
elseif term:find("^%+") then
term = require(romut_module).get_special_indicator(term)
term = com.make_augmentative(lemma, term)
end
return term
end)
if is_metaphonic(args, lemma) then
insert(data.inflections, {label = metaphonic_label})
insert(data.categories, langname .. " na " .. category_plpos .. " na may metaponya")
end
if args.irreg and is_superlative then
insert(data.categories, langname .. " na di-regular na pasukdol na " .. category_plpos)
end
end
local function get_adjective_params(adjtype)
local params = {
["inv"] = boolean_param, --invariable
["sp"] = true, -- special indicator: "first", "first-last", etc.
["f"] = list_param, --feminine form(s)
["pl"] = list_param, --plural override(s)
["mpl"] = list_param, --masculine plural override(s)
["fpl"] = list_param, --feminine plural override(s)
["meta"] = boolean_param, -- metaphonic
["nometa"] = boolean_param, -- explicitly not metaphonic
}
if adjtype == "base" then
params["comp"] = list_param --comparative(s)
params["sup"] = list_param --superlative(s)
params["dim"] = list_param --diminutive(s)
params["aug"] = list_param --augmentative(s)
params["fonly"] = boolean_param -- feminine only
params["gneut"] = boolean_param -- gender-neutral adjective e.g. [[latine]] (FIXME: do these exist in Portuguese?)
params["hascomp"] = true -- has comparative
end
if adjtype == "part" then
params["short"] = boolean_param -- short participle
end
if adjtype == "sup" then
params["irreg"] = boolean_param
end
if adjtype == "pron" or adjtype == "contr" then
params["n"] = list_param --neuter form(s)
end
return params
end
pos_functions["pang-uri"] = {
params = get_adjective_params("base"),
func = do_adjective,
}
pos_functions["comparative adjectives"] = {
params = get_adjective_params("comp"),
func = do_adjective,
pos_category = "adjectives",
}
pos_functions["superlative adjectives"] = {
params = get_adjective_params("sup"),
func = function(args, data)
do_adjective(args, data, "is superlative")
end,
pos_category = "adjectives",
}
pos_functions["past participles"] = {
params = get_adjective_params("part"),
func = do_adjective,
redlink_pos = "participles",
}
pos_functions["determiners"] = {
params = get_adjective_params("det"),
func = do_adjective,
}
pos_functions["adjective-like pronouns"] = {
params = get_adjective_params("pron"),
func = do_adjective,
pos_category = "pronouns",
}
pos_functions["adjective-like contractions"] = {
params = get_adjective_params("contr"),
func = do_adjective,
pos_category = "contractions",
}
-----------------------------------------------------------------------------------------
-- Adverbs --
-----------------------------------------------------------------------------------------
local function do_adverb(args, data)
handle_adj_adv_comp(args, data, "adverbs", "is adv")
end
local function get_adverb_params(advtype)
local params = {}
if advtype == "base" then
params["comp"] = list_param --comparative(s)
params["sup"] = list_param --superlative(s)
params["hascomp"] = true -- has comparative
end
return params
end
pos_functions["pang-abay"] = {
params = get_adverb_params("base"),
func = do_adverb,
}
pos_functions["comparative adverbs"] = {
params = get_adverb_params("comp"),
func = do_adverb,
pos_category = "adverbs",
}
pos_functions["superlative adverbs"] = {
params = get_adverb_params("sup"),
func = do_adverb,
pos_category = "adverbs",
}
-----------------------------------------------------------------------------------------
-- Verbs --
-----------------------------------------------------------------------------------------
pos_functions["pandiwa"] = {
params = {
[1] = true,
["pres"] = list_param, --present
["pres_qual"] = {list = "pres\1_qual", allow_holes = true},
["pres3s"] = list_param, --third-singular present
["pres3s_qual"] = {list = "pres3s\1_qual", allow_holes = true},
["pret"] = list_param, --preterite
["pret_qual"] = {list = "pret\1_qual", allow_holes = true},
["part"] = list_param, --participle
["part_qual"] = {list = "part\1_qual", allow_holes = true},
["short_part"] = list_param, --short participle
["short_part_qual"] = {list = "short_part\1_qual", allow_holes = true},
["noautolinktext"] = boolean_param,
["noautolinkverb"] = boolean_param,
["attn"] = boolean_param,
},
func = function(args, data, frame)
local preses, preses_3s, prets, parts, short_parts
if args.attn then
insert(data.categories, "Requests for attention concerning " .. langname)
return
end
local pt_verb = require(pt_verb_module)
local alternant_multiword_spec = pt_verb.do_generate_forms(args, "pt-verb", data.heads[1])
local specforms = alternant_multiword_spec.forms
local function slot_exists(slot)
return specforms[slot] and #specforms[slot] > 0
end
local function do_finite(slot_tense, label_tense)
-- Use pres_3s if it exists and pres_1s doesn't exist (e.g. impersonal verbs); similarly for pres_3p
-- (only3p verbs); but fall back to pres_1s if neither pres_1s nor pres_3s nor pres_3p exist (e.g.
-- [[empedernir]]).
local has_1s = slot_exists(slot_tense .. "_1s")
local has_3s = slot_exists(slot_tense .. "_3s")
local has_3p = slot_exists(slot_tense .. "_3p")
if has_1s or (not has_3s and not has_3p) then
return {
slot = slot_tense .. "_1s",
label = ("unang panauhan isahan %s"):format(label_tense),
}, true
elseif has_3s then
return {
slot = slot_tense .. "_3s",
label = ("ikatlong panauhan isahan %s"):format(label_tense),
}, false
else
return {
slot = slot_tense .. "_3p",
label = ("ikatlong panauhan maramihan %s"):format(label_tense),
}, false
end
end
local did_pres_1s
preses, did_pres_1s = do_finite("pres", "pangkasalukuyan")
preses_3s = {
slot = "pres_3s",
label = "ikatlong panauhan isahan pangkasalukuyan",
}
prets = do_finite("pret", "preterite")
parts = {
slot = "pp_ms",
label = "pandiwaring pangnakaraan",
}
short_parts = {
slot = "short_pp_ms",
label = "maikling pandiwaring pangnakaraan",
}
if args.pres[1] or args.pres3s[1] or args.pret[1] or args.part[1] or args.short_part[1] then
track("verb-old-multiarg")
end
local function strip_brackets(qualifiers)
if not qualifiers then
return nil
end
local stripped_qualifiers = {}
for _, qualifier in ipairs(qualifiers) do
local stripped_qualifier = qualifier:match("^%[(.*)%]$")
if not stripped_qualifier then
error("Internal error: Qualifier should be surrounded by brackets at this stage: " .. qualifier)
end
insert(stripped_qualifiers, stripped_qualifier)
end
return stripped_qualifiers
end
local function do_verb_form(args, qualifiers, slot_desc, skip_if_empty)
local forms
local to_insert
if #args == 0 then
forms = specforms[slot_desc.slot]
if not forms or #forms == 0 then
if skip_if_empty then
return
end
forms = {{form = "-"}}
end
elseif #args == 1 and args[1] == "-" then
forms = {{form = "-"}}
else
forms = {}
for i, arg in ipairs(args) do
local qual = qualifiers[i]
if qual then
-- FIXME: It's annoying we have to add brackets and strip them out later. The inflection
-- code adds all footnotes with brackets around them; we should change this.
qual = {"[" .. qual .. "]"}
end
local form = arg
if not args.noautolinkverb then
-- [[Module:inflection utilities]] already loaded by [[Module:pt-verb]]
form = require(inflection_utilities_module).add_links(form)
end
insert(forms, {form = form, footnotes = qual})
end
end
if forms[1].form == "-" then
to_insert = {label = "walang " .. slot_desc.label}
else
local into_table = {label = slot_desc.label}
for _, form in ipairs(forms) do
local qualifiers = strip_brackets(form.footnotes)
-- Strip redundant brackets surrounding entire form. These may get generated e.g.
-- if we use the angle bracket notation with a single word.
local stripped_form = rmatch(form.form, "^%[%[([^%[%]]*)%]%]$") or form.form
stripped_form = pt_verb.remove_variant_codes(stripped_form)
-- Don't include accelerators if brackets remain in form, as the result will be wrong.
-- FIXME: For now, don't include accelerators. We should use the new {{pt-verb form of}} once
-- implemented.
-- local this_accel = not stripped_form:find("%[%[") and accel or nil
local this_accel = nil
insert(into_table, {term = stripped_form, q = qualifiers, accel = this_accel})
end
to_insert = into_table
end
insert(data.inflections, to_insert)
end
local skip_pres_if_empty
if alternant_multiword_spec.no_pres1_and_sub then
insert(data.inflections, {label = "walang unang panauhan isahan pangkasalukuyan"})
insert(data.inflections, {label = "walang pasakali pangkasalukuyan"})
end
if alternant_multiword_spec.no_pres_stressed then
insert(data.inflections, {label = "walang nakadiin na paturol o pasakali pangkasalukuyan"})
skip_pres_if_empty = true
end
if alternant_multiword_spec.only3s then
insert(data.inflections, {label = glossary_link("di-panao")})
elseif alternant_multiword_spec.only3sp then
insert(data.inflections, {label = "ikatlong panauhan lamang"})
elseif alternant_multiword_spec.only3p then
insert(data.inflections, {label = "ikatlong panauhan maramihan lamang"})
end
local has_vowel_alt
if alternant_multiword_spec.vowel_alt then
for _, vowel_alt in ipairs(alternant_multiword_spec.vowel_alt) do
if vowel_alt ~= "+" and vowel_alt ~= "í" and vowel_alt ~= "ú" then
has_vowel_alt = true
break
end
end
end
do_verb_form(args.pres, args.pres_qual, preses, skip_pres_if_empty)
-- We want to include both the pres_1s and pres_3s if there is a vowel alternation in the present singular. But
-- we don't want to redundantly include the pres_3s if we already included it.
if did_pres_1s and has_vowel_alt then
do_verb_form(args.pres3s, args.pres3s_qual, preses_3s, skip_pres_if_empty)
end
do_verb_form(args.pret, args.pret_qual, prets)
do_verb_form(args.part, args.part_qual, parts)
do_verb_form(args.short_part, args.short_part_qual, short_parts, "skip if empty")
-- Add categories.
for _, cat in ipairs(alternant_multiword_spec.categories) do
insert(data.categories, cat)
end
-- If the user didn't explicitly specify head=, or specified exactly one head (not 2+) and we were able to
-- incorporate any links in that head into the 1= specification, use the infinitive generated by
-- [[Module:pt-verb]] in place of the user-specified or auto-generated head. This was copied from
-- [[Module:it-headword]], where doing this gets accents marked on the verb(s). We don't have accents marked on
-- the verb but by doing this we do get any footnotes on the infinitive propagated here. Don't do this if the
-- user gave multiple heads or gave a head with a multiword-linked verbal expression such as Italian
-- '[[dare esca]] [[al]] [[fuoco]]' (FIXME: give Portuguese equivalent).
if #data.user_specified_heads == 0 or (
#data.user_specified_heads == 1 and alternant_multiword_spec.incorporated_headword_head_into_lemma
) then
data.heads = {}
for _, lemma_obj in ipairs(alternant_multiword_spec.forms.infinitive_linked) do
local quals, refs = require(inflection_utilities_module).
convert_footnotes_to_qualifiers_and_references(lemma_obj.footnotes)
insert(data.heads, {term = lemma_obj.form, q = quals, refs = refs})
end
end
end
}
-----------------------------------------------------------------------------------------
-- Suffix forms --
-----------------------------------------------------------------------------------------
pos_functions["suffix forms"] = {
params = {
[1] = {required = true, list = true, disallow_holes = true},
["g"] = {list = true, disallow_holes = true, type = "genders", flatten = true},
},
func = function(args, data)
validate_genders(args.g)
data.genders = args.g
local suffix_type = {}
for _, typ in ipairs(args[1]) do
insert(suffix_type, "hulaping gumagawa ng " .. typ)
end
insert(data.inflections, {label = "anyong di-lema ng " .. m_table.serialCommaJoin(suffix_type, {conj = "o"})})
end,
}
return export
hlqpzic3x4tfaoy3nsv108lab0f7z54
Module:pt-pronunc
828
35652
178626
169832
2026-11-09T06:12:56Z
GinawaSaHapon
3989
178626
Scribunto
text/plain
--[=[
This module implements the template {{pt-IPA}}.
Author: Benwing
-- FIXME:
1. Implement i^ not before vowel = epenthetic i or deleted epenthetic i in Brazil (in that order), and i^^ not before
vowel = opposite order. Epenthetic i should not affect stress but should otherwise be treated like a normal vowel.
Deleted epenthetic i should trigger palatalization of t/d but have no other effects. [DONE]
2. Implement i^ before vowel = i.V or yV (in that order), and i^^ before vowel = opposite order. [DONE]
3. Implement i* = mandatory epenthetic i in Brazil. [DONE]
4. Implement u^ = u. or w (in that order), and u^^ = opposite order. [DONE]
5. Implement e^ = e or i in Brazil (in that order), and e^^ = opposite order. [DONE]
6. Implement o^ = o or u in Brazil (in that order), and o^^ = opposite order. [DONE]
7. Implement ê* = ê in Brazil, é in Portugal (useful especially before nasal consonants). [DONE]
8. Implement ô* = ô in Brazil, ó in Portugal (useful especially before nasal consonants). [DONE]
9. Implement é* = é in Brazil, ê in Portugal (useful especially in -éi-). [DONE]
10. Implement ó* = ó in Brazil, ô in Portugal (useful especially in -ói-). [DONE]
11. Implement des^ at beginning of word = 'dis++' or 'des++' in Brazil (in that order), and des^^ = opposite order. [DONE]
12. In Portugal, before [ɫ], unstressed 'a' should be /a/; unstressed 'e' should be /ɛ/; and unstressed 'o' should be
either /o/ or /ɔ/ (in that order). [DONE]
13. Support qualifiers using <q:...>. [DONE]
14. Support references using <ref:...>. Syntax is the same as for IPA ref=. [DONE]
15. In Portugal, unstressed o in hiatus should be /w/, and unstressed e in hiatus should be /j/. [DONE]
16. Support - (hyphen) = left and right parts should be treated as distinct phonological words but written joined
together, and non-final primary stresses turn into secondary stresses. Word-initial and word-final behavior should
happen, e.g. Brazil epenthesis of (j) before word-final /s/ followed a stressed vowel, Brazil raising of esC- and
Portugal rendering of o- as ò-. [DONE]
17. Support : (colon), similar to hyphen but in non-final parts, final vowels aren't rendered as closed. [DONE]
18. Support + (plus sign), similar to colon but non-final primary stresses aren't displayed and syllable-division
ignores the plus sign. [DONE]
19. In Brazil, word-initial enC-, emC- should display as (careful pronunciation) ẽ-, (natural pronunciation) ĩ-. [DONE]
20. In Portugal, -sç- and -sc(e/i)- should show as (careful pronunciation) /ʃs/, (natural pronunciation) /ʃ/. Same for
-sz- ([[as]] [[zonas]]). [DONE] [FIXME: Verify this reduction in [[as]] [[zonas]].]
21. In Portugal, grave accent indicates unstressed open a/e/o and macron indicates unstressed closed a/e/o; both are
ignored in Brazil. [DONE]
22. In Portugal, iCi where the first i is before the stress should (maybe) show as iCi, (traditional pronunciation) ɨCi.
In iCiCi, both of the first two i's show as ɨ in the traditional pronunciation (FIXME: verify this). C should be
only a single consonant, hence not in [[piscina]] or [[distrito]] (FIXME: verify this). Does not apply if the first
i is stressed (e.g. [[mínimo]], [[tília]], [[pírico]], [[tísica]]) or if the stressed i is word-final ([[Mimi]],
[[Lili]], [[chichizinho]], [[piripiri]]), or in certain other words ([[felicíssimo]], [[filhinho]], [[estilista]],
[[pirite]]). Possibly this means it doesn't apply when the stressed i is in a suffix (-íssimo, -inho, -ista). We
can always disable the eCi spelling by adding an h in 'ihCi' to make it look like a cluster between the i's. NOTE:
It appears that iCi -> eCi should apply in [[dicionário]], meaning if we apply it at the end, we have to distinguish
between glides from original i and glides from e or y.
23. In Portugal and Brazil, stressed o in hiatus should automatically be ô (voo, Samoa, Alagoas, perdoe, abençoe). [DONE]
24. In Northern Portugal, stressed closed ô in hiatus (whether written explicitly as e.g. vôo, Côa or generated automatically)
should show as e.g. /ˈbo.(w)ɐ/. [DONE] [FIXME: Verify syllable division.]
25. Recognize -zinha like -zinho, -mente. Just use hyphen (-) to handle these. We don't recognize -zão, -zona, -zito,
-zita because of too many false positives; you can just write the hyphen explicitly before the suffix as needed.
Cf. among our current vocabulary we have 10 -zão augmentatives (animalzão, aviãozão, cipozão, cuzão, homenzão,
leãozão, paizão, pãozão, pezão, tatuzão), 2 -ão augmentatives after a word ending in -z (codornizão, felizão), and
7 non-augmentatives (alazão, coalizão, razão, rezão, sazão, sezão, vazão). Similarly for -zona: we have 5 -zona
augmentatives (boazona, cuzona, maçãzona, mãezona, mãozona) against 8 non-augmentatives (amazona, aminofenazona,
arilidrazona, Arizona, cronozona, ecozona, Eurozona, fenazona) and no -ona augmentatives after words ending in -z.
For -zito, we have 1 -ito diminutive after a word ending in -z (Queluzito), one non-diminutive (quartzito), and no
-zito diminutives. For -zita we have 1 -zita diminutive (maçãzita) and 4 non-diminutives (andaluzita, monazita,
pedzita, stolzita). [DONE]
26. Final 'r' isn't optional before -zinho, -zinha, -mente. [DONE]
27. Consider making secondary stress optional in cases like traduçãozinha where the stress is directly before the
primary stress.
28. In Brazil, unstressed final-syllable /a/ should be reduced before -r, cf. [[açúcar]]. [DONE]
29. Support + = pagename, and pagename= argument. [DONE]
30. Deduplicate final pronunciations without distinct qualifiers. [DONE]
31. Implement support for dot-under without accompanying quality diacritic. When attached to a/e/o, it defaults to acute
= open pronun, except in the following circumstances, where it defaults to circumflex: (1) in the diphthongs
ei/eu/oi/ou; (2) in a nasal vowel. [DONE]
32. Portugal final -e should show as optional (ɨ) unless there is a vowel-initial word following, in which case it
should not be displayed at all. [NOT DONE]
33. Syllabification: "Improper" clusters of non-sibiliant-obstruent + obstruent (pt, bt, bd, dk, kt; ps, bs, bv, bʒ, tz,
dv, ks; ft), non-sibiliant-obstruent + nasal (pn, bn, tm, tn, dm, dn, gm, gn), nasal + nasal (mn) are syllabified in
Portugal as .pt, .bv, .mn, etc. Note ʃ.t, ʃ.p, ʃ.k, etc. But in Brazil, all of these divide between the consonants
(p.t, b.v, ʃ.t, s.p, etc.). Particular case: [[ab-rogação]] divides as a.brr in Portugal but ab.rr in Brazil. [DONE]
34. -ão, -ãe, -õe should be recognized as nasal diphthongs with a circumflex added to force stress. [DONE]
35. Recognize obsolete -aõ, -aẽ, -oẽ as equivalent to -ão, -ãe, -õe.
36. In CluV, CruV, CliV, CriV, the 'u' and 'i' are vowels not glides in both Portugal and Brazil. [DONE]
37. Epenthesis of (j) before final stressed s in Brazil should not happen after i. [DONE]
38. Dialect markers such as "Brazil", "Portugal" should go at the beginning. [DONE]
39. Portugal exC, êxC should be rendered like eiʃC (FIXME: Does this apply to "Central Portugal" as well?). exs- needs
handling like eiʃs-/(i)ʃs- not like eiss-. [DONE]
40. Unstressed word-initial exC- should maybe have two pronunciations, one with eiʃC- and the other with (i)ʃC-.
(FIXME: Verify.)
41. -sj- (e.g. [[transgénico]]) should reduce to a single /ʒ/. [DONE]
42. [[transgredir]] should have /z/ (Brazil), /ʒ/ (Portugal) instead of /s/, /ʃ/. [DONE]
43. Unstressed -ie- in hiatus should automatically be -iè- in Portugal or maybe -iè-/-ié-? [DONE] (FIXME: Verify.)
44. Initial esC- in Brazil should be either isC- or esC-. [DONE]
45. Initial sC- in Portugal and maybe Brazil should be /s/ not /ʃ/. [DONE]
46. Deleted epenthetic /i/ should block conversion of syllable-final m/n into nasalization, cf. [[amnésia]] respelled
'ami^nési^a'. [DONE]
47. Portugal 'o', 'os' should be unstressed with /u/, not have /ɔ/. [DONE]
48. /s/ after nasal vowel before glide should not become voiced. [DONE]
49. [[arrozinho]] (which uses the + component divider) should have the IPA stress mark before the 'z' not after. [DONE]
50. Portugal final -ɨ should be suppressed before a vowel (with a tie sign), and made optional word-finally. [DONE]
51. Final -dor/-tor/-sor/-ssor + feminine and plural should have closed /o/. [DONE]
52. Final -oso should have closed /o/, but feminine and plural should have open /ɔ/. [DONE]
53. Hiatuses in Brazil involving 'i' should have two possibilities (full vowel or glide); likewise for 'u'. [DONE]
54. In Brazil phonetic representation, hiatuses involving 'i' should be [ɪ], and those involving 'u' should be [ʊ]. [DONE]
55. ui^ should convert to 'ui' in Portugal = /wi/, but to 'u.i' or 'uy' in Brazil. [DONE]
56. des^ should be 'des++' or 'dis++' (++ in both cases). [DONE]
57. Word-boundary special handling (e.g. des^, x-, -x, etc.) should also respect component boundaries e.g. in
[[aerodeslizador]], [[criptoxantina]]. [DONE]
58. Convert apostrophe to tie, and make tie transparent to syllabification. [DONE]
59. 'x' in -nx- should default to /ʃ/ ([[enxame]], [[enxugar]]). [DONE]
60. Final i^ should not be stressed. [DONE]
61. There should not be a comma between phonemic and phonetic representations. [DONE]
62. Final stressed -io in Brazil should be either i.u or iw. [DONE]
63. Unstressed final '-ax' has open /a/ including in Portugal. [DONE]
64. Clean up handling of qualifiers and fix bugs. [DONE]
65. Support i#, u# for i./y or u./w in both Brazil and Portugal. [DONE]
66. In Portugal, final unstressed -ar/-or should be pronounced open, but in Brazil, closed. [DONE]
67. Suppress initial e- in esC- in Portugal after /i/. [DONE]
68. In C[lr][iu], [iu] should be either full vowel or glide in Portugal. [DONE]
69. Support substitution notation. [DONE]
70. [[em]] by itself should be /ẽj̃/ or /ĩ/. [DONE]
71. Suffixes beginning with a vowel should act as if a pseudo-consonant precedes. [DONE]
72. Prefixes should change primary stress to secondary. [DONE]
73. -ing should be like -im in Brazil but -ingh in Portugal. [DONE]
74. Single 's' after colon should be /z/ not /s/, in keeping with normal spelling practices;
likewise for single 'r'. [NOT YET, FIRST NEED TO PUSH APPROPRIATE RESPELLING CHANGES]
75. Intertonic 'o' after stressed i/e/ɛ ([[período]], [[rubéola]]) in Brazil should be .u or w.
76. Don't display phonetic IPA if identical to phonemic IPA. [DONE]
77. Add "South Brazil" pronunciation. [DONE; SEE ISSUES BELOW]
78. -êm should be like -ém in Brazil but -ẽiem in Portugal. [DONE]
79. -õem should be like -õe in Brazil but -õeem in Portugal. [DONE]
80. -le in Portugal should split into two pronuns, one with vowel for -e, one without. [DONE]
81. Initial 'e' in Portugal after des^ should be /i/. [DONE]
]=]
--[=[
Issues concerning South Brazil pronunciation:
(Rather than an unified South Brazil pronunciation, it's better to transform it in Rio Grande do Sul/Gaúcho pronunciation)
1. Should all 'ẽ' (not just word-final) be rendered as [ẽj̃]? We have several existing examples, e.g.
/de.zẽ.ba.ˈla.do/|/de.zẽj̃.ba.ˈla.do/ for [[desembalado]], /ˌde.zẽj̃.has.ˈkɐ̃.so/ for [[desenrascanço]],
/ẽj̃.baw.sa.ˈma(ɻ)/ for [[embalsamar]], [ẽj̃.pũˈj̃aɾ] for [[empunhar]], /ẽ.ʁus.tiɾ/|[ẽj̃.ʁuʃˈ(t)͡ʃiɾ] for
[[enrustir]].
2. Coda 'r': Mostly /ɻ/ but sometimes given as both /ɾ/ and /ɻ/ or occasionally /r/.
3. Raising of unstressed 'e' and 'o', and 'des-': Mostly not present but sometimes yes, e.g. [[clarear]]
/kla.ɾe.ˈa(ɾ)/|/kla.ɾe.ˈa(ɻ)/|/kla.ɾi.ˈa(ɾ)/|/kla.ɾi.ˈa(ɻ)/|/kla.ˈɾja(ɾ)/|/kla.ˈɾja(ɻ)/, [[consentâneo]]
[kõ(w̃).senˈtə̃.ni̯u], [[cozido]] /ko.ˈzi.dʊ/|/ku.ˈzi.dʊ/, [[desbloquear]] /dez.blo.ke.ˈa(ɾ)/|/dez.blo.ˈkja(ɾ)/|/d͡ʒiz-/|/-(ɻ)/,
[[desferir]] /des.fe.ˈɾi(ɾ)/|/des.fe.ˈɾi(ɻ)/|/des.fi.ˈɾi(ɾ)/|/des.fi.ˈɾi(ɻ)/, [[desrespeitar]] /dez.hes.pej.ˈta(ɾ)/|/d͡ʒiz-/|/-(ɻ)/,
[[destruído]] /des.tɾu.ˈi.do/|/d͡ʒis.tɾu.ˈi.do/.
4. Epenthetic 'i': Less common? E.g. [[dignidade]] given as just /ˌd͡ʒiɡ.ni.ˈda.de/, but [[digno]] as /ˈd͡ʒiɡ.no/|/ˈd͡ʒi.ɡi.no/.
5. Secondary stress: Often given two syllables before the stress. I have left it out unless it seems stable and in a prefix, e.g.
[[eleitorado]] /e.ˌlej.to.ˈɾa.do/, [[eletrizante]] /e.ˌle.tɾi.ˈzɐ̃.te/, [[elevado]] /ˌe.le.ˈva.do/, [[elevador]] /e.ˌle.va.ˈdoɻ/,
[[engarrafamento]] /ˌẽ.ɡa.ˌha.fa.ˈmẽ.to/.
6. Initial em-: Mostly given as /ẽ-/ or /ẽj̃-/, but sometimes /ĩ-/ as in [[emparedar]] /ĩ.pa.ɾe.ˈda(ɾ)/|/ĩ.pa.ɾe.ˈda(ɻ)/
or both as in [[encaixar]] /ẽ.kaj.ˈʃa(ɾ)/|/ĩ.kaj.ˈʃa(ɻ)/|/ẽ.kaj.ˈʃa(ɾ)/|/ĩ.kaj.ˈʃa(ɻ)/.
7. -nh-: [[empunhar]] given as [ẽj̃.pũˈj̃aɾ], but is /j̃/ actually characteristic of this accent or is it /ɲ/?
8. Other inconsistencies: e.g. [[enrustir]] /ẽ.ʁus.tiɾ/|[ẽj̃.ʁuʃˈ(t)͡ʃiɾ], with coda /ʃ/ and strong [ʁ] (usually [h]).
[[ab-rogar]] /ab.ʁoˈɡa(ɻ)/|/ab.hoˈɡa(ɻ)/|/ab.χoˈɡa(ɻ)/|/ab.ɦoˈɡa(ɻ)/ with all possible strong r's.
9. Nasal vowels: Almost always as elsewhere, but occasionally e.g. [[entender]] /ĩn.tẽnˈde(r)/, [[a gente]] /a.ˈʒen.te/|/a.ˈʒẽ.te/.
10. Nasal diphthongs: -ão sometimes claims to be /ɐ̃õ/, sometimes /ɐ̃w̃/. [[-ção]] listed both.
]=]
local export = {}
local m_IPA = require("Module:IPA")
local m_table = require("Module:table")
local m_strutils = require("Module:string utilities")
local m_qual = require("Module:qualifier")
local lang = require("Module:languages").getByCode("pt")
local u = require("Module:string/char")
local rfind = mw.ustring.find
local rsubn = mw.ustring.gsub
local rmatch = mw.ustring.match
local rsplit = mw.text.split
local ulower = mw.ustring.lower
local uupper = mw.ustring.upper
local usub = mw.ustring.sub
local ulen = mw.ustring.len
local unpack = unpack or table.unpack -- Lua 5.2 compatibility
local AC = u(0x0301) -- acute = ́
local GR = u(0x0300) -- grave = ̀ = open vowel quality without stress in Portugal only
local MACRON = u(0x0304) -- macron = ̄ = closed vowel quality without stress in Portugal only
local CFLEX = u(0x0302) -- circumflex = ̂
local TILDE = u(0x0303) -- tilde = ̃
local DIA = u(0x0308) -- diaeresis = ̈
local CEDILLA = u(0x0327) -- cedilla = ̧
local DOTOVER = u(0x0307) -- dot over = ̇
-- DOTUNDER indicates an explicitly unstressed syllable; useful when accompanied by a quality marker (acute or
-- circumflex), or by itself with a/e/o, where it defaults to acute (except in the following circumstances, where it
-- defaults to circumflex: (1) in the diphthongs ei/eu/oi/ou; (2) in a nasal vowel).
local DOTUNDER = u(0x0323) -- dot under = ̣
-- LINEUNDER indicates an explicit secondary stress; normally not necessary as primary stress is converted to secondary
-- stress if another primary stress follows, but can be used e.g. after a primary stress; can be accompanied by a
-- quality marker (acute or circumflex) with a/e/o; if not, defaults to acute (except in the same circumstances where
-- dot under defaults to circumflex).
local LINEUNDER = u(0x0331) -- line under = ̱
-- Serves to temorarily mark where a syllable division should not happen; temporarily substitutes for comma+space;
-- temporarily substitutes for #.
local TEMP1 = u(0xFFF0)
local SYLDIV = u(0xFFF1) -- used to represent a user-specific syllable divider (.) so we won't change it
local PSEUDOCONS = u(0xFFF2) -- pseudo-consonant at the edge of prefixes ending in a vowel and suffixes beginning with a vowel
local PREFIX_MARKER = u(0xFFF3) -- marker indicating a prefix so we can convert primary to secondary accents
-- Since we convert all symbols at the beginning and decompose accented characters (except for ç and ü), we can later
-- use capital and/or accented letters to represent additional distinctions, typically in cases where we want to
-- remember the source of a given phoneme. By convention we use capital letters, optionally with accents.
-- Specifically:
-- * A/E/O represent written a/e/o where we don't yet know the vowel quality. Towards the beginning, we convert all
-- written a/e/o to A/E/O and later convert them to their final qualities (which might include /a/ /e/ /o/, so we
-- can't use those symbols directly for this purpose).
-- * Ẽ stands for a word-initial Brazilian sound that can be pronounced either /ẽ/ (in careful speech) or /ĩ/ (in
-- natural speech) and originates from en- or em- before a consonant. We distinguish this from written in-/im-,
-- which can be only /ĩ/, and written ehn-/ehm- (or similar), which can be only /ẽ/.
-- * I is used to represent epenthetic i in Brazilian variants (which should not affect stress assignment but is
-- otherwise treated as a normal sound), and Ɨ represents deleted epenthetic i (which still palatalizes /t/ and /d/).
-- I is also used to represent Brazil e or i from initial esC-, and Portugal (i) from initial esC-.
-- * Ì is used to represent either i. in hiatus or /j/ in Brazil; likewise for Ù representing u. in hiatus or /w/.
-- * Ɔ (capital version of ɔ) stands for a Portugal sound that can be pronounced either /o/ or /ɔ/ (depending on the
-- speaker), before syllable-final /l/.
-- * Ú is used word-finally after i to represent either .u in hiatus or /w/ in Brazil.
-- * L is used word-finally in Portugal to split words ending in -le into two pronuns due to the differing pronunciation
-- of /l/ in the two cases (coda or not).
local vowel = "aɐeɛiɨoɔuüAEẼIƗÌOƆÙÚ"
local V = "[" .. vowel .. "]"
local NV_NOT_SPACING_CFLEX = "[^" .. vowel .. "%^]"
local high_front_vocalic = "iIƗÌy"
local front_vocalic = "eɛɨẼ" .. high_front_vocalic
local FRONTV = "[" .. front_vocalic .. "]"
local glide = "yw"
local W = "[" .. glide .. "]" -- glide
local ipa_stress = "ˈˌ"
local ipa_stress_c = "[" .. ipa_stress .. "]"
local primary_quality = AC .. CFLEX
local primary_quality_c = "[" .. primary_quality .. "]"
local quality = AC .. CFLEX .. GR .. MACRON
local quality_c = "[" .. quality .. "]"
local stress = LINEUNDER .. DOTOVER .. DOTUNDER .. ipa_stress
local stress_c = "[" .. stress .. "]"
local non_primary_stress = LINEUNDER .. DOTUNDER .. "ˌ"
local non_primary_stress_c = "[" .. non_primary_stress .. "]"
local accent = quality .. stress .. TILDE
local accent_c = "[" .. accent .. "]"
-- Any component separator that should be "transparent" (i.e. ignored) during syllabification processes. This should
-- include a subset of the component_sep characters, currently + and * (which ++ is converted into).
local syl_transp_component_sep = "+*"
local syl_transp_component_sep_c = "[" .. syl_transp_component_sep .. "]"
-- Any character that should be "transparent" (i.e. ignored) during syllabification processes. This includes the
-- syllable-transparent component separators + and ++ (converted into *) as well as the tie character, which originates
-- from an apostrophe (e.g. [[barriga d'água]]).
local syl_transp = syl_transp_component_sep .. "‿"
local syl_transp_c = "[" .. syl_transp .. "]"
-- Zero or more syllable-transparent characters; used during syllabification.
local STC = syl_transp_c .. "*"
-- Component separators that are not transparent to syllabification. Includes colon (:), hyphen (-) and double hyphen
-- (--), which is converted internally to @.
local non_syl_transp_component_sep = ":@%-"
local non_syl_transp_component_sep_c = "[" .. non_syl_transp_component_sep .. "]"
-- "component_sep" means any symbol that may separate word components (not including #, which is added at a certain
-- point next to certain word components so that the adjacent characters are treated as if they are at word bounaries).
local component_sep = syl_transp_component_sep .. non_syl_transp_component_sep
local component_sep_c = "[" .. component_sep .. "]"
local word_or_component_sep_c = "[#" .. component_sep .. "]"
-- Syllable divider (auto-inserted or user-specified).
local syldiv = "." .. SYLDIV
local syldiv_c = "[" .. syldiv .. "]"
-- "charsep" means any symbol that may separate the individual characters that make up a word, and which should be
-- ignored for e.g. consonant-consonant assimilation processes. This currently includes accents and syllable dividers.
local charsep = accent .. syldiv
local charsep_c = "[" .. charsep .. "]"
-- Characters that may divide words, other than the tie (‿), which is transparent to syllabification.
local non_syl_transp_word_divider = " #"
-- All characters that may divide words.
local word_divider = non_syl_transp_word_divider .. "‿"
-- "wordsep_not_syl_transp" means the same as "wordsep" below but excludes syllable-transparent characters. It is used
-- in other collections of symbols (particularly when negated, so as to include syllable-transparent characters but
-- otherwise exclude word separators) rather than by itself.
local wordsep_not_syl_transp = charsep .. non_syl_transp_word_divider .. non_syl_transp_component_sep
-- "wordsep" means any symbol that may separate the individual characters that make up a word or may separate words or
-- components, and which should be ignored for e.g. consonant-consonant assimilation processes that operate across
-- words. This currently includes everything in "charsep" and "component_sep" plus symbols that may divide words.
local wordsep = wordsep_not_syl_transp .. syl_transp
local wordsep_c = "[" .. wordsep .. "]"
local C = "[^" .. vowel .. wordsep .. "_]" -- consonant
-- consonant or syllable-transparent component separator
local C_OR_SYL_TRANSP = "[^" .. vowel .. wordsep_not_syl_transp .. "_]"
local H_OR_SYL_TRANSP = "[h" .. syl_transp .. "]"
local H_GLIDE_OR_SYL_TRANSP = "[h" .. glide .. syl_transp .. "]"
local C_NOT_H_OR_GLIDE = "[^h" .. glide .. vowel .. wordsep .. "_]" -- consonant other than h, w or y
local C_OR_WORD_BOUNDARY = "[^" .. vowel .. charsep .. "_]" -- consonant or word boundary
local voiced_cons = "bdglʎmnɲŋrɾʁvzjʒʤ" -- voiced sound
-- Unstressed words with vowel reduction in Brazil and Portugal.
local unstressed_words = require("Module:table").listToSet({
"o", "os", -- definite articles
"me", "te", "se", "lhe", "lhes", "nos", "vos", -- unstressed object pronouns
-- See https://en.wikipedia.org/wiki/Personal_pronouns_in_Portuguese#Contractions_between_clitic_pronouns
"mo", "mos", "to", "tos", "lho", "lhos", -- object pronouns combined with articles
-- Allomorphs of articles after certain consonants
"lo", "los", "no", -- [[nos]] above as object pronoun
-- Allomorphs of object pronouns before other pronouns
"vo", -- [[no]] above as allomorph of article
"que", -- subordinating conjunctions
"e", -- coordinating conjunctions
"de", "do", "dos", "por", -- basic prepositions + combinations with articles; [[no]], [[nos]] above already
-- FIXME: Portugal pronun for pelos, pela, pelas given as if spelled pêlos, etc. with stress, but differently for pelo.
-- I am assuming the Portugal pronuns with stress are wrong.
"pelo", "pelos", "pela", "pelas" -- preposition + article combinations
})
-- Unstressed words with vowel reduction in Portugal only.
local unstressed_full_vowel_words_brazil = require("Module:table").listToSet({
"a", "as", -- definite articles
-- See https://en.wikipedia.org/wiki/Personal_pronouns_in_Portuguese#Contractions_between_clitic_pronouns
"ma", "mas", "ta", "tas", "lha", "lhas", -- object pronouns combined with articles
-- Allomorphs of articles after certain consonants
"la", "las", "na", "nas",
"da", "das", -- basic prepositions + combinations with articles; [[na]], [[nas]] above already
-- coordinating conjunctions; [[mas]] above already
"para", "pra", -- preposition; for the verb form use "pára" instead
})
-- Unstressed words without vowel reduction.
local unstressed_full_vowel_words = require("Module:table").listToSet({
"um", "uns", -- single-syllable indefinite articles
"meu", "teu", "seu", "meus", "teus", "seus", -- single-syllable possessives
"ou", -- coordinating conjunctions
-- Note that in order to match à and às we have to write them as below because at the point we are trying to
-- match them, all text has been converted to canonical decomposed Unicode form. Writing "à" and "às" directly
-- won't work even if you type in the text using decomposed Unicode characters because all page contents are
-- automatically converted to canonical composed form when saved.
"ao", "aos", "a" .. GR, "a" .. GR .. "s", -- basic prepositions + combinations with articles
"em", "com", -- other prepositions
})
-- Special-case pronunciations for certain unstressed words with irregular pronunciations. The left side is the
-- original spelling after DOTUNDER or DOTOVER has been added; which diacritic gets added depends on whether the word
-- has vowel reduction (DOTOVER) or no vowel reduction (DOTUNDER). The right side is the respelling. See comment just
-- above for why we write "a" .. GR instead of "à".
local unstressed_pronunciation_substitution = {
["a" .. DOTUNDER .. "o"] = "a" .. DOTUNDER .. "u",
["a" .. DOTUNDER .. "os"] = "a" .. DOTUNDER .. "us",
["a" .. GR .. DOTUNDER] = "a" .. DOTUNDER,
["a" .. GR .. DOTUNDER .. "s"] = "a" .. DOTUNDER .. "s",
["po" .. DOTOVER .. "r"] = "pu" .. DOTOVER .. "r",
}
-- Dialects and subdialects:
export.all_styles = {"gbr", "rio", "sp", "sbr", "gpt", "cpt", "spt", "npt"}
export.all_style_groups = {
all = export.all_styles,
br = {"gbr", "rio", "sp", "sbr"},
pt = {"gpt", "cpt", "spt", "npt"},
}
local style_to_style_group = {}
for group, styles in pairs(export.all_style_groups) do
if group ~= "all" then
for _, style in ipairs(styles) do
style_to_style_group[style] = group
end
end
end
export.all_style_descs = {
-- style groups
br = "[[w:Brazilian_Portuguese|Brasil]]",
pt = "[[w:European_Portuguese|Portugal]]",
-- styles
gbr = "[[w:Brazilian_Portuguese|Brasil]]", -- "general" Brazil
rio = "[[w:Carioca#Sociolect|Rio de Janeiro]]", -- Carioca accent
sp = "[[w:Paulistano_dialect|São Paulo]]", -- Paulistano accent
sbr = "Timog Brasil",
gpt = "[[w:European_Portuguese|Portugal]]", -- "general" Portugal
-- lisbon = "Lisbon", -- (not added yet)
cpt = "[[w:Estremenho dialect|Gitnang Portugal]]", -- Central Portugal outside of Lisbon
spt = "Timog Portugal", -- Dialects of Alentejo and Algarve (West Algarve is a distinct subdialect)
npt = "[[w:Northern Portuguese|Hilagang Portugal]]" -- Northern Portugal dialects (Porto/Minho and Transmontano are subdialects)
}
-- 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
-- version of rsubn() that returns a 2nd argument boolean indicating whether
-- a substitution was made.
local function rsubb(term, foo, bar)
local retval, nsubs = rsubn(term, foo, bar)
return retval, nsubs > 0
end
-- apply rsub() repeatedly until no change
local function rsub_repeatedly(term, foo, bar)
while true do
local new_term = rsub(term, foo, bar)
if new_term == term then
return term
end
term = new_term
end
end
-- Flat-map a function `fun` over `items`. This is like `map` over a sequence followed by `flatten`, i.e. the function
-- must itself return a sequence and all of the returned sequences are flattened into a single sequence.
local function flatmap(items, fun)
local new = {}
for _, item in ipairs(items) do
local results = fun(item)
for _, result in ipairs(results) do
table.insert(new, result)
end
end
return new
end
-- Combine two sets of qualifiers, either of which may be nil or a list of qualifiers. Remove duplicate qualifiers.
-- Return value is nil or a list of qualifiers.
local function combine_qualifiers(qual1, qual2)
if not qual1 then
return qual2
end
if not qual2 then
return qual1
end
local qualifiers = m_table.deepCopy(qual1)
for _, qual in ipairs(qual2) do
m_table.insertIfNot(qualifiers, qual)
end
return qualifiers
end
-- Reorder the diacritics (accent marks) in `text` according to a canonical order. Specifically, there can conceivably
-- be up to three accents on a vowel: a quality mark (acute/circumflex/grave/macron); a mark indicating secondary stress
-- (lineunder), tertiary stress (dotunder; i.e. no stress but no vowel reduction) or forced vowel reduction (dotover);
-- and a nasalization mark (tilde). Order them as follows: quality - stress - nasalization. `err` is a function of one
-- argument (an error string) and should throw an error if called.
local function reorder_accents(text, err)
local function reorder_accent_string(accentstr)
local accents = rsplit(accentstr, "")
local accent_order = {
[AC] = 1,
[CFLEX] = 1,
[GR] = 1,
[MACRON] = 1,
[LINEUNDER] = 2,
[DOTUNDER] = 2,
[DOTOVER] = 2,
[TILDE] = 3,
}
table.sort(accents, function(ac1, ac2)
return accent_order[ac1] < accent_order[ac2]
end)
return table.concat(accents)
end
text = rsub(text, "(" .. accent_c .. "+)", reorder_accent_string)
-- Remove duplicate accents.
text = rsub_repeatedly(text, "(" .. accent_c .. ")%1", "%1")
-- Make sure we don't have more than one of a given class.
if rfind(text, quality_c .. quality_c) then
err("Two different quality diacritics cannot occur together")
end
if rfind(text, stress_c .. stress_c) then
err("Two different stress diacritics cannot occur together")
end
-- Only a/e/o can receive a circumflex, grave or macron.
if rfind(text, "[^aeo][" .. CFLEX .. GR .. MACRON .. "]") then
err("Only a/e/o can be followed by circumflex, grave or macron")
end
return text
end
-- Generate partial IPA for a single preprocessed term respelling `text` in the specified `style` ('gbr', 'rio', etc.;
-- see all_style_descs above). If `phonetic` is given, generate phonetic output, otherwise phonemic output. `err` is a
-- function of one argument (an error string) and should throw an error if called. This function is a subfunction of
-- `IPA` and cannot really be used by itself, because it generates output containing special symbols that need to be
-- postprocessed into multiple outputs (and in addition some other final postprocessing needs to happen, e.g. to get
-- stress marks in the right place). The function `IPA` is available be called externally.
local function one_term_ipa(text, style, phonetic, err)
local brazil = m_table.contains(export.all_style_groups.br, style) and true or false
local portugal = m_table.contains(export.all_style_groups.pt, style) and true or false
-- Initial x -> /ʃ/: [[xérox]], [[xilofone]], [[xadrez]], etc.
text = rsub(text, "(" .. word_or_component_sep_c .. ")x", "%1ʃ")
-- Final x -> /ks/ ([[clímax]], [[xérox]], [[córtex]], [[hélix]], [[durex]], [[lux]], etc.), but for now we map to
-- X because later on we open unstressed vowels before final x.
text = rsub(text, "x(" .. word_or_component_sep_c .. ")", "X%1")
-- x after certain dipthongs (ai, ei, oi, ou) and after -en- should be /ʃ/. Other diphthongs before x are rare
-- and mostly learned and we need to force explicit respelling.
text = rsub(text, "(([aeo])" .. charsep_c .. "*([iun])" .. charsep_c .. "*)x",
function(all, a, b)
local ab = a .. b
-- [[baixo]], [[peixe]], [[troixa]], [[frouxo]], [[enxame]], etc.
if ab == "ai" or ab == "ei" or ab == "oi" or ab == "ou" or ab == "en" then
return all .. "ʃ"
else
return all .. "x"
end
end)
-- -exC- should be pronounced like -esC- in Brazil but -eisC- in Portugal. Cf. excelente, experiência, têxtil,
-- êxtase. Not with other vowels (cf. [[Felixlândia]], [[Laxmi]], [[Oxford]]).
-- FIXME: Maybe this applies only to Lisbon and environs?
text = rsub(text, "(e" .. accent_c .. "*)x(" .. C .. ")", function(v, c)
if brazil then
return v .. "s" .. c
elseif c == "s" then
return v .. "isç"
else
return v .. "is" .. c
end
end)
if rfind(text, "x") then
err("x must be respelled z, sh, cs, ss or similar")
end
-- combinations with h; needs to precede handling of c and s, and needs to precede syllabification so that
-- the consonant isn't divided from the following h.
if style == "npt" then
-- In Northern Portugal the affricate tch is kept
text = rsub(text, "ch", "tʃ")
end
-- Else
text = rsub(text, "([scln])h", {["s"]="ʃ", ["c"]="ʃ", ["n"]="ɲ", ["l"]="ʎ" })
-- remove initial <h>
text = rsub(text, "(" .. word_or_component_sep_c .. ")h([^" .. accent .. "])", "%1%2")
-- Betacism
if style == "npt" then
-- In Northern Portugal, <v> is realized as <b>
text = rsub(text, "v", "b")
end
-- c, g, q
-- This should precede syllabification especially so that the latter isn't confused by gu, qu, gü, qü
-- also, c -> ç before front vowel ensures that cc e.g. in [[cóccix]], [[occitano]] isn't reduced to single c.
text = rsub(text, "c(" .. FRONTV .. ")", "ç%1")
text = rsub(text, "g(" .. FRONTV .. ")", "j%1")
text = rsub(text, "gu(" .. FRONTV .. ")", "g%1")
-- [[camping]], [[doping]], [[jogging]], [[Bangkok]], [[angstrom]], [[tungstênio]]
text = rsub(text, "ng([^" .. vowel .. glide .. "hlr])", brazil and "n%1" or "ngh%1")
text = rsub(text, "qu(" .. FRONTV .. ")", "k%1")
text = rsub(text, "ü", "u") -- [[agüentar]], [[freqüentemente]], [[Bündchen]], [[hübnerita]], etc.
text = rsub(text, "([gq])u(" .. V .. ")", "%1w%2") -- [[quando]], [[guarda]], etc.
text = rsub(text, "[cq]", "k") -- [[Qatar]], [[burqa]], [[Iraq]], etc.
-- y -> i between non-vowels, cf. [[Itamaraty]] /i.ta.ma.ɾa.ˈt(ʃ)i/, [[Sydney]] respelled 'Sýdjney' or similar
-- /ˈsid͡ʒ.nej/ (Brazilian). Most words with y need respelling in any case, but this may help.
text = rsub(text, "(" .. C_OR_WORD_BOUNDARY .. ")y(" .. accent_c .. "*" .. C_OR_WORD_BOUNDARY .. ")", "%1i%2")
-- Reduce double letters to single, except for rr, mm, nn and ss, which map to special single sounds. Do this
-- before syllabification so double letters don't get divided across syllables. The case of cci, cce is handled
-- above. nn always maps to /n/ and mm to /m/ and can be used to force a coda /n/ or /m/. As a result,
-- [[connosco]] will need respelling 'comnôsco', 'cõnôsco' or 'con.nôsco', and [[comummente]] will similarly
-- need respelling e.g. as 'comum.mente' or 'comũmente'. Examples of words with double letters (Brazilian
-- pronunciation):
-- * [[Accra]] no respelling needed /ˈa.kɾɐ/;
-- * [[Aleppo]] respelled 'Aléppo' /aˈlɛ.pu/;
-- * [[buffer]] respelled 'bâfferh' /ˈbɐ.feʁ/;
-- * [[cheddar]] respelled 'chéddarh' /ˈʃɛ.daʁ/;
-- * [[Hanna]] respelled 'Ranna' /ˈʁɐ̃.nɐ/;
-- * [[jazz]] respelled 'djézz' /ˈd͡ʒɛs/;
-- * [[Minnesota]] respelled 'Minnessôta' /mi.neˈso.tɐ/;
-- * [[nutella]] respelled 'nutélla' /nuˈtɛ.lɐ/;
-- * [[shopping]] respeled 'shópping' /ˈʃɔ.pĩ/ or 'shóppem' /ˈʃɔ.pẽj̃/;
-- * [[Stonehenge]] respelled 'Sto̱wnn.rrendj' /ˌstownˈʁẽd͡ʒ/;
-- * [[Yunnan]] no respelling needed /juˈnɐ̃/.
--
-- Note that further processing of r and s happens after syllabification and stress assignment, because we need
-- e.g. to know the distinction between final -s and -z to assign the stress properly.
text = rsub(text, "rr", "ʁ")
text = rsub(text, "nn", "N")
text = rsub(text, "mm", "M")
-- Deleted epenthetic /i/ should prevent preceding /m/, /n/ from being converted into nasalization.
text = rsub(text, "mƗ", "MƗ")
text = rsub(text, "nƗ", "NƗ")
-- Will map later to /s/; need to special case to support spellings like 'nóss' (= nós, plural of nó).
text = rsub(text, "ss", "S")
text = rsub(text, "(" .. C .. ")%1", "%1")
-- muit- is special and contains nasalization. Do before palatalization of t/d so [[muitíssimo]] works.
text = rsub(text, "(" .. word_or_component_sep_c .. "mu" .. stress_c .. "*)(it)", "%1" .. TILDE .. "%2")
-- Palatalize t/d + Ɨ -> affricates in Brazil. Use special unitary symbols, which we later convert to regular affricate
-- symbols, so we can distinguish palatalized d from written dj. We only do Ɨ now so we can delete it; we do another
-- palatalization round towards the end after raising e -> i.
local palatalize_td = {["t"] = "ʧ", ["d"] = "ʤ"}
if brazil then
text = rsub(text, "([td])(" .. word_or_component_sep_c .. "*Ɨ)",
function(td, high_vocalic) return palatalize_td[td] .. high_vocalic end)
-- Now delete the symbol for deleted epenthetic /i/; it still triggers palatalization of t and d.
text = rsub(text, "Ɨ", "")
end
-- Divide words into syllables.
-- First, change user-specified . into a special character so we won't move it around. We need to keep this
-- going forward until after we place the stress, so we can correctly handle initial i- + vowel, as in [[ia]],
-- [[iate]] and [[Iaundé]]. We need to divide [[ia]] as 'i.a' but [[iate]] as 'ia.te' and [[Iaundé]] as 'Ia.un.dé'.
-- In the former case, the stress goes on i but in the latter cases not; so we always divide <ia> as 'i.a',
-- and then after stress assignment remove the syllable divider if the <i> isn't stressed. The tricky thing is
-- that we want to allow the user to override this by explicitly adding a . between the <i> and <a>. So we need
-- to keep the distinction between user-specified . and auto-determined . until after stress assignment.
text = rsub(text, "%.", SYLDIV)
-- We have various characters indicating divisions between word components where we want to treat the components
-- more or less like separate words (e.g. -mente, -zinho/-zinha). Some such "characters" are digraphs, which we
-- convert internally to single characters to simplify the code. Here, -- separates off -mente/-zinho/-zinha and
-- ++ separates off prefixes. We want to ignore at least + and ++ (converted to *) for syllabification purposes.
text = rsub(text, "%-%-", "@")
text = rsub(text, "%+%+", "*")
-- Respell [[homenzinho]] as 'homemzinho' so it is stressed correctly.
text = rsub(text, "n(" .. SYLDIV .. "?ziɲos?" .. word_or_component_sep_c .. ")", "m%1")
-- Divide before the last consonant (possibly followed by a glide). We then move the syllable division marker
-- leftwards over clusters that can form onsets. Note that syllable-transparent component separators will always
-- be (and will continue to be) to the left of syllable dividers rather than to the right, so we don't need to
-- check for the latter situation.
text = rsub_repeatedly(text, "(" .. V .. accent_c .. "*" .. C_OR_SYL_TRANSP .. "-)(" .. C .. H_GLIDE_OR_SYL_TRANSP .. "*" .. V .. ")", "%1.%2")
text = rsub(text, "([pbtdkgfv]" .. H_OR_SYL_TRANSP .. "*)%.([lr])", ".%1%2")
if portugal then
-- "Improper" clusters of non-sibiliant-obstruent + obstruent (pt, bt, bd, dk, kt; ps, bs, bv, bʒ, tz, dv, ks;
-- ft), non-sibiliant-obstruent + nasal (pn, bn, tm, tn, dm, dn, gm, gn), nasal + nasal (mn) are syllabified in
-- Portugal as .pt, .bv, .mn, etc. Note ʃ.t, ʃ.p, ʃ.k, etc. But in Brazil, all of these divide between the
-- consonants (p.t, b.v, ʃ.t, s.p, etc.). Particular case: [[ab-rogação]] divides as a.brr in Portugal but ab.rr
-- in Brazil.
text = rsub(text, "([pbtdkgfv]" .. H_OR_SYL_TRANSP .. "*)%.([pbtdkgfvsSçzʃʒjmMnNɲʎʁ])", ".%1%2")
text = rsub(text, "([mM]" .. H_OR_SYL_TRANSP .. "*)%.([nN])", ".%1%2")
else
-- /tʃ/, /dʒ/ are normally single sounds, but adj- in [[adjetivo]], [[adjunto]] etc. should be 'ad.j'
text = rsub(text, "(t" .. STC .. ")%.(ʃ)", ".%1%2")
text = rsub(text, "(d" .. STC .. ")%.(j)", ".%1%2")
text = rsub(text, "(" .. word_or_component_sep_c .. "a" .. STC .. ")%.(d" .. STC .. ")(j)", "%1%2.%3")
end
-- All vowels should be separated from adjacent vowels by a syllable division except
-- (1) aeo + unstressed i/u, ([[saiba]], [[peixe]], [[noite]], [[Paulo]], [[deusa]], [[ouro]]), except when
-- followed by nh or m/n/r/l + (non-vowel or word end), e.g. Bom.ba.im, ra.i.nha, Co.im.bra, sa.ir, but Jai.me,
-- a.mai.nar, bai.le, ai.ro.so, quei.mar, bei.ra;
-- (2) iu(s), ui(s) at end of word, e.g. fui, Rui, a.zuis, pa.riu, viu, sa.iu;
-- (3) ão, ãe, õe.
--
-- The easiest way to handle this is to put a special symbol between vowels that should not have a syllable
-- division between them.
--
-- First, put a syllable divider between [aeo].[iu][mnlr], as in [[Bombaim]], [[Coimbra]], [[saindo]], [[sair]],
-- [[Iaundé]], [[Raul]]. Note that in cases like [[Jaime]], [[queimar]], [[fauna]], [[baile]], [[Paulo]], [[beira]],
-- where a vowel follows the m/n/l/r, there will already be a syllable division between i.m, u.n, etc., which will
-- block the following substitution.
text = rsub(text, "([aeo]" .. accent_c .. "*" .. STC .. ")([iu]" .. STC .. "[mnlr])", "%1.%2")
-- Also put a syllable divider between [aeo].[iu].ɲ coming from 'nh' ([[rainha]], [[moinho]]).
text = rsub(text, "([aeo]" .. accent_c .. "*" .. STC .. ")([iu]" .. STC .. "%.ɲ)", "%1.%2")
-- Prevent syllable division between final -ui(s), -iu(s). This should precede the following rule that prevents
-- syllable division between ai etc., so that [[saiu]] "he left" gets divided as sa.iu.
-- It doesn't make sense to have STC in the middle of a diphthong here.
text = rsub(text, "(u" .. accent_c .. "*)(is?" .. word_or_component_sep_c .. ")", "%1" .. TEMP1 .. "%2")
text = rsub(text, "(i" .. accent_c .. "*)(us?" .. word_or_component_sep_c .. ")", "%1" .. TEMP1 .. "%2")
-- Prevent syllable division between ai, ou, etc. unless either the second vowel is accented [[saído]]) or there's
-- a TEMP1 marker already after the second vowel (which will occur e.g. in [[saiu]] divided as 'sa.iu').
text = rsub_repeatedly(text, "([aeo]" .. accent_c .. "*)([iu][^" .. accent .. TEMP1 .. "])", "%1" .. TEMP1 .. "%2")
text = rsub_repeatedly(text, "(u" .. accent_c .. "*)(i[^" .. accent .. TEMP1 .. "])", "%1" .. TEMP1 .. "%2")
text = rsub_repeatedly(text, "(i" .. accent_c .. "*)(u[^" .. accent .. TEMP1 .. "])", "%1" .. TEMP1 .. "%2")
-- Prevent syllable division between nasal diphthongs unless somehow the second vowel is accented.
text = rsub_repeatedly(text, "(a" .. accent_c .. "*" .. TILDE .. ")([eo][^" .. accent .. "])", "%1" .. TEMP1 .. "%2")
text = rsub_repeatedly(text, "(o" .. accent_c .. "*" .. TILDE .. ")(e[^" .. accent .. "])", "%1" .. TEMP1 .. "%2")
text = rsub_repeatedly(text, "(u" .. accent_c .. "*" .. TILDE .. ")(i[^" .. accent .. "])", "%1" .. TEMP1 .. "%2")
-- All other sequences of vowels get divided.
text = rsub_repeatedly(text, "(" .. V .. accent_c .. "*" .. STC .. ")(" .. V .. ")", "%1.%2")
-- Remove the marker preventing syllable division.
text = rsub(text, TEMP1, "")
-- An acute or circumflex not followed by a stress marker has primary stress, so indicate it.
text = rsub_repeatedly(text, "(" .. V .. quality_c .. ")([^" .. stress .. "])", "%1ˈ%2")
-- Line-under indicates secondary stress.
text = rsub(text, LINEUNDER, "ˌ")
-- Add primary stress to the word if not already present.
local function accent_word(word)
-- Check if stress already marked. We check first for primary stress before checking for tilde in case both
-- primary stress and tilde occur, e.g. [[bênção]], [[órgão]], [[hétmã]], [[connosco]] respelled 'cõnôsco'.
if rfind(word, "ˈ") then
return word
end
-- Preserve the syllable divider, which may be auto-added or user-specified.
local syllables = m_strutils.capturing_split(word, "([." .. SYLDIV .. "])")
-- Check for nasal vowel marked with tilde and without non-primary stress; assign stress to the last such
-- syllable in case there's more than one tilde, e.g. [[pãozão]]. Note, this can happen in the part before
-- -mente, cf. [[anticristãmente]], and before -zinho, cf. [[coraçãozinho]].
for i = #syllables, 1, -2 do -- -2 because of the syllable dividers; see above.
local changed
syllables[i], changed = rsubb(syllables[i], "(" .. V .. quality_c .. "*)" .. TILDE, "%1ˈ" .. TILDE)
if changed then
return table.concat(syllables)
end
end
-- Apply the default stress rule.
local sylno
-- Prefixes ending in a vowel such as pseudo- have a PSEUDOCONS after the final vowel, but we don't want that to
-- interfere in the stress-assignment algorithm.
if #syllables > 1 and (rfind(word, "[aeo][s" .. PSEUDOCONS .. "]?$") or rfind(word, "[ae]m$") or rfind(word, "[ae]ns$")) then
-- Stress the last syllable but one. The -2 is because of the syllable dividers; see above.
sylno = #syllables - 2
else
sylno = #syllables
end
-- Don't put stress on epenthetic i; instead, we stress the preceding syllable, as if epenthetic i weren't
-- there.
while sylno > 1 and rfind(syllables[sylno], "I") do
sylno = sylno - 2
end
-- It is (vaguely) possible that we have a one-syllable word beginning with a complex cluster such as gn-
-- followed by a normally unstressed ending such as -em. In this case, we want the ending to be stressed.
while sylno < #syllables and rfind(syllables[sylno], "I") do
sylno = syno + 2
end
-- If we are on a syllable without a vowel (can happen if it's the last syllable in a non-final component of a
-- word, when using a component separator that is transparent to stress, such as in [[rapazinho]] respelled
-- 'rapaz+inho'), stress the syllable to the left.
while sylno > 1 and not rfind(syllables[sylno], V) do
sylno = sylno - 2
end
if rfind(syllables[sylno], stress_c) then
-- Don't do anything if stress mark already present. (Since we check for primary stress above, this check
-- specifically affects non-primary stress.)
return word
end
-- Add stress mark after first vowel (and any quality mark).
syllables[sylno] = rsub(syllables[sylno], "^(.-" .. V .. quality_c .. "*)", "%1ˈ")
return table.concat(syllables)
end
-- Split the text into words and the words into components so we can correctly add stress to components without it.
local words = rsplit(text, " ")
for j, word_with_boundary_markers in ipairs(words) do
-- Prefixes have a PREFIX_MARKER after the # at the end of the prefix; split it off.
local begin_marker, word, end_marker = rmatch(word_with_boundary_markers, "^(#*)(.-)([#" .. PREFIX_MARKER .. "]*)$")
-- Words ends in -mente, -zinho(s) or -zinha(s); add primary stress to the preceding portion as if stressed
-- (e.g. [[agitadamente]] -> 'agitádamente') unless already stressed (e.g. [[rapidamente]] respelled
-- 'rápidamente'). The primary stress will be converted to secondary stress further below. Essentially, we
-- rip the word apart into two words ('mente'/'zinho' and the preceding portion) and
-- stress each one independently. Note that the effect of adding a primary stress will also be to cause
-- an error if stressed 'e' or 'o' is not properly marked as é/ê or ó/ô; cf. [[certamente]], which must
-- be respelled 'cértamente', and [[posteriormente]], which must be respelled 'posteriôrmente', just as
-- with [[certa]] and [[posterior]]. To prevent this happening, you can add an accent to -mente or
-- -zinho, e.g. [[dormente]] respelled 'dormênte', [[vizinho]] respelled 'vizínho'.
if rfind(word, syldiv_c .. "men%.te$") then
word = rsub(word, syldiv_c .. "(men%.te)$", "@%1")
else
word = rsub(word, syldiv_c .. "(zi%.ɲ[oa]s?)$", "@%1")
end
-- Split on components; preserve the component divider.
local components = m_strutils.capturing_split(word, "(" .. component_sep_c .. syldiv_c .. "*)")
for k = 1, #components, 2 do -- 2 because of the component dividers.
-- Don't add stress to components followed by ++ (converted to *).
if k == #components or not rfind(components[k + 1], "%*") then
components[k] = accent_word(components[k])
end
end
-- Reconstruct the word.
words[j] = begin_marker .. table.concat(components, "") .. end_marker
end
-- Reconstruct the text from the words.
text = table.concat(words, " ")
-- Add word boundaries around component separators. We add them on both sides of - and -- (converted to @), which
-- behave mostly like a true word separator, but only on the right side of other component separators (which
-- corresponds to the beginning of the word following the separator). Note that some component separators (+ and ++
-- [converted to *]) are transparent to syllable boundaries, meaning that there may be a syllable divider directly
-- to the right of the component separator. To simplify the code below, we put the word boundary marker on the outside
-- of the syllable boundary marker.
text = rsub(text, "([%-@]" .. syldiv_c .. "?)", "#%1#")
text = rsub(text, "([+:*]" .. syldiv_c .. "?)", "%1#")
-- I has served its purpose (not considered when accenting).
text = rsub(text, "I", "i")
-- Remove hiatus between initial <i> and following vowel ([[Iasmim]]) unless the <i> is stressed ([[ia]]) or the
-- user explicitly added a . (converted to SYLDIV above).
text = rsub(text, "#i%.(" .. V .. ")", "#y%1")
if brazil then
-- In Brazil, hiatuses involving i. or u. have two possibilities (full vowel or glide); represent using Ì. and Ù.,
-- which we later convert appropriately. Do this before eliminating SYLDIV so the user can force a hiatus using a
-- period.
local hiatus_to_optional_glide = {["i"] = "Ì", ["u"] = "Ù"}
text = rsub(text, "(" .. C_OR_WORD_BOUNDARY .. ")([iu])(%." .. V .. ")",
function(before, hiatus, after) return before .. hiatus_to_optional_glide[hiatus] .. after end)
-- In Brazil, hiatuses of the form í.o (e.g. [[rio]] "river", [[vazio]]; but not [[rio]] "I laugh") have two
-- possibilities (i.u or iw); represent using Ú, which we later convert appropriately. Do this before eliminating
-- SYLDIV so the user can force a hiatus using a period, as in [[rio]] "I laugh" respelled 'ri.o'.
text = rsub(text, "(i" .. ipa_stress_c .. "%.)o(s?#)", "%1Ú%2")
else
-- Outside of Brazil, e.i -> a.i, e.g. [[ateísta]], [[proteína]], [[proteinúrio]] respelled 'prote.inúrio'. But seems
-- not to happen in rei- ([[reincidente]], [[reiniciar]], [[reidratar]], etc.). Note, it does occur in [[reídeo]],
-- which needs respelling.
text = rsub(text, "(#re" .. syldiv_c .. ")(i)", "%1" .. TEMP1 .. "%2")
text = rsub(text, "e(" .. syldiv_c .. "i)", "a%1")
text = rsub(text, TEMP1, "")
-- Outside of Brazil, hiatuses involving 'e./i.' or 'o./u.' after obstruent + l/r preceding a vowel have two
-- possibilities (full vowel or glide), as in [[criança]], [[altruista]], etc. Represent using Ì. and Ù., which
-- we later convert appropriately. Do this before eliminating SYLDIV so the user can force a hiatus using a
-- period.
local hiatus_to_optional_glide = {["e"] = "Ì", ["i"] = "Ì", ["o"] = "Ù", ["u"] = "Ù"}
text = rsub(text, "([pbtdkgfv]" .. H_OR_SYL_TRANSP .. "*[lr])([eiou])(%." .. V .. ")",
function(before, hiatus, after) return before .. hiatus_to_optional_glide[hiatus] .. after end)
-- Outside of Brazil, remove hiatus more generally whenever 'e./i.' or 'o./u.' precedes a vowel. Do this before
-- eliminating SYLDIV so the user can force hiatus using a period.
local hiatus_to_glide = {["e."] = "y", ["i."] = "y", ["o."] = "w", ["u."] = "w"}
text = rsub(text, "(" .. C_OR_WORD_BOUNDARY .. ")([eiou]%.)(" .. V .. ")",
function(before, hiatus, after) return before .. hiatus_to_glide[hiatus] .. after end)
end
-- Convert user-specified syllable division back to period. See comment above when we add SYLDIV.
text = rsub(text, SYLDIV, ".")
-- Vowel quality handling. First convert all a -> A, e -> E, o -> O. We will then convert A -> a/ɐ, E -> e/ɛ/ɨ,
-- O -> o/ɔ/u depending on accent marks and context. Ultimately all vowels will be one of the nine qualities
-- aɐeɛiɨoɔu and following each vowel will either be nothing (no stress), an IPA primary stress mark (ˈ) or an
-- IPA secondary stress mark (ˌ), in turn possibly followed by a tilde (nasalization). After doing everything
-- that depends on the position of stress, we will move the IPA stress marks to the beginning of the syllable.
text = rsub(text, "[aeo]", {["a"] = "A", ["e"] = "E", ["o"] = "O"})
text = rsub(text, DOTOVER, "") -- eliminate DOTOVER; it served its purpose of preventing stress
-- Nasal vowel handling.
-- Final unstressed -am (in third-person plural verbs) pronounced like unstressed -ão.
text = rsub(text, "Am#", "A" .. TILDE .. "O#")
if portugal then
-- In Portugal, final -n is really /n/, and preceding unstressed e/o are open ([[cólon]], [[crípton]], [[éon]];
-- [[glúten]], [[hífen]], [[pólen]]).
text = rsub(text, "n#", "N#")
text = rsub(text, "([EO])(N#)", "%1" .. AC .. "%2")
end
if brazil then
-- In Brazil, [[põem]] is pronounced like 'põe'.
text = rsub(text, "(Oˈ" .. TILDE .. ")(Em#)", "%1E#")
else
-- In Portugal, circumflex accent on final -em ([[vêm]], [[mantêm]], etc.) indicates a special double nasal diphthong
-- pronunciation.
text = rsub(text, "E" .. CFLEX .. "ˈm#", "E" .. CFLEX .. "ˈ" .. TILDE .. "y" .. TILDE .. ".E" .. CFLEX .. "m#")
-- In Portugal, [[põem]] is pronounced like 'põeem'.
text = rsub(text, "(Oˈ" .. TILDE .. ")(Em#)", "%1E.%2")
end
-- Acute accent on final -em ([[além]], [[também]]) and final -ens ([[parabéns]]) does not indicate an open
-- pronunciation.
text = rsub(text, "E" .. AC .. "(ˈ[mn]s?#)", "E" .. CFLEX .. "%1")
-- Vowel + m/n within a syllable gets converted to tilde.
text = rsub(text, "(" .. V .. quality_c .. "*" .. stress_c .. "*)[mn]", "%1" .. TILDE)
-- Non-high vowel without quality mark + tilde needs to get the circumflex (possibly fed by the previous change).
text = rsub(text, "([AEO])(" .. stress_c .. "*)" .. TILDE, "%1" .. CFLEX .. "%2" .. TILDE)
-- Primary-stressed vowel without quality mark + m/n/nh across syllable boundary gets a circumflex, cf. [[cama]],
-- [[ano]], [[banho]] (excluding Northern Portugal)
if style == "npt" then
-- Northern Portugal keeps the open "a" vowels
text = rsub(text, "(A)(ˈ%.[mnɲMN])", "%1" .. AC .. "%2")
end
text = rsub(text, "(" .. V .. ")(ˈ%.[mnɲMN])", "%1" .. CFLEX .. "%2")
if brazil then
if style ~= "sbr" then -- Seems this happens less or not at all in South Brazil.
-- Primary-stressed vowel + m/n across syllable boundary gets nasalized in Brazil, cf. [[cama]], [[ano]].
text = rsub(text, "(" .. V .. quality_c .. "*)(ˈ%.[mnMN])", "%1" .. TILDE .. "%2")
end
-- All vowels before nh (always across syllable boundary) get circumflexed and nasalized in Brazil,
-- cf. [[ganhar]]. I *think* the circumflexing but not nasalizing happens in South Brazil.
text = rsub(text, "(" .. V .. stress_c .. "*)(%.ɲ)", "%1" .. CFLEX .. "%2")
if style ~= "sbr" then -- I *think* this doesn't apply to South Brazil; need to verify.
text = rsub(text, "(" .. V .. quality_c .. "*" .. stress_c .. "*)(%.ɲ)", "%1" .. TILDE .. "%2")
end
-- Convert initial unstressed em-/en- before consonant to special symbol /Ẽ/, which later on is converted
-- to /e/ (careful pronunciation) or /i/ (natural pronunciation).
text = rsub(text, "(#E" .. CFLEX .. TILDE ..")(%." .. C ..")", "#Ẽ" .. TILDE .. "%2")
-- Same in [[em]] standing alone (which will have a DOTUNDER in it), and in [[em-]].
text = rsub(text, "(#E" .. CFLEX .. DOTUNDER .. "?" .. TILDE ..")(#)", "#Ẽ" .. TILDE .. "%2")
end
-- Nasal diphthongs.
local nasal_termination_to_glide = {["E"] = "y", ["O"] = "w"}
-- In ãe, ão, the second letter represents a glide.
text = rsub(text, "(A" .. CFLEX .. stress_c .. "*" .. TILDE .. ")([EO])",
function(v1, v2) return v1 .. nasal_termination_to_glide[v2] .. TILDE end)
-- Likewise for õe.
text = rsub(text, "(O" .. CFLEX .. stress_c .. "*" .. TILDE .. ")E", "%1y" .. TILDE)
-- Likewise for ũi (generated above from muit-).
text = rsub(text, "(u" .. stress_c .. "*" .. TILDE .. ")i", "%1y" .. TILDE)
-- Final -em and -ens (stressed or not) pronounced /ẽj̃(s)/. (Later converted to /ɐ̃j̃(s)/ in Portugal.)
text = rsub(text, "(E" .. CFLEX .. stress_c .. "*" .. TILDE .. ")(s?#)", "%1y" .. TILDE .. "%2")
-- Oral diphthongs.
-- ei, eu, oi, ou -> êi, êu, ôi, ôu
text = rsub(text, "([EO])(" .. stress_c .. "*[iuywY])", "%1" .. CFLEX .. "%2")
-- ai, au -> ái, áu
text = rsub(text, "(A)(" .. stress_c .. "*[iuywY])", "%1" .. AC .. "%2")
-- Convert A/E/O as appropriate when followed by a secondary or tertiary stress marker. If a quality is given,
-- it takes precedence; otherwise, act as if an acute accent were given.
text = rsub(text, "([AEO])(" .. non_primary_stress_c .. ")", "%1" .. AC .. "%2")
-- Stressed o in -dor, -dor, -sor ([[ganhador]], [[autor]], [[invasor]], [[agressor]], etc.) and feminines and plurals
-- is closed /o/.
text = rsub(text, "([dtsS])O(ˈr#)", "%1o%2")
text = rsub(text, "([dtsS])O(ˈ%.r[EA]s?#)", "%1o%2")
-- Stressed o in -oso is closed /o/.
text = rsub(text, "O(ˈ%.sO#)", "o%1")
-- Stressed o in -osa, -osos, -osas is open /ɔ/.
text = rsub(text, "O(ˈ%.s[OA]s?#)", "ɔ%1")
-- Unstressed syllables.
-- Before final <x>, unstressed a/e/o are open, e.g. [[clímax]], [[córtex]], [[xérox]].
text = rsub(text, "([AEO])(X)", "%1" .. AC .. "%2")
-- Capital X has served its purpose, so replace it.
text = rsub(text, "X", "kç")
if brazil then
if style ~= "sbr" then
-- Final unstressed -e(s), -o(s) -> /i/ /u/ (including before -mente)
local brazil_final_vowel = {["E"] = "i", ["O"] = "u"}
text = rsub(text, "([EO])(s?#)", function(v, after) return brazil_final_vowel[v] .. after end)
-- Word-final unstressed -a(s) -> /ɐ/ (not before -mente)
text = rsub(text, "A(s?#[^@])", function(after) return "ɐ" .. after end)
-- Word-final unstressed -ar -> /ɐr/ (e.g. [[açúcar]])
text = rsub(text, "A(r#)", function(after) return "ɐ" .. after end)
end
-- Initial unmarked unstressed non-nasal e- + -sC- -> /i/ or /e/ ([[estar]], [[esmeralda]]). To defeat this,
-- explicitly mark the <e> e.g. as <ệ> or <eh>. We reuse the special symbol /I/ for this purpose, which later
-- on is converted to /i/ or /e/. In South Brazil, however, the raised /i/ variant doesn't seem to exist.
if not rfind(text, "#Es.ç") then
text = rsub(text, "#E(s" .. C .. "*%.)", style == "sbr" and "#e%1" or "#I%1")
end
-- Remaining unstressed a, e, o without quality mark -> /a/ /e/ /o/.
local brazil_unstressed_vowel = {["A"] = "a", ["E"] = "e", ["O"] = "o"}
text = rsub(text, "([AEO])([^" .. accent .. "])",
function(v, after) return brazil_unstressed_vowel[v] .. after end)
end
if portugal then
-- In Portugal, final unstressed -r opens preceding a/e/o ([[dólar]], [[líder]], [[júnior]], [[inter-]]
-- respelled 'ínter:...').
text = rsub(text, "([AEO])(r" .. word_or_component_sep_c .. ")", "%1" .. AC .. "%2")
-- In Portugal, unstressed a/e/o before coda l takes on an open quality. Note that any /l/ directly after a
-- vowel must be a coda /l/ because otherwise there would be a syllable boundary marker.
text = rsub(text, "([AEO])l", function(v)
-- The symbol Ɔ is later converted to /o/ or /ɔ/.
local vowel_to_before_l = {["A"] = "a", ["E"] = "ɛ", ["O"] = "Ɔ"}
return vowel_to_before_l[v] .. "l"
end)
-- Unstressed 'ie' -> /jɛ/
text = rsub(text, "yE([^" .. accent .. "])", "yɛ%1")
-- Initial unmarked unstressed non-nasal e- + -sC- (seemingly also after des-, see Infopédia [[desestagnar]]) ->
-- temporary symbol I (later changed to /(i)/, except after a vowel, in which case it is deleted). Note that /s/
-- directly after a vowel must be a coda /s/ because otherwise there would be a syllable boundary marker.
text = rsub(text, "#Es", "#Is")
-- Initial unmarked unstressed non-nasal e- -> /i/, including after des-.
text = rsub(text, "#E([^" .. accent .. "])", "#i%1")
-- Initial unmarked unstressed non-nasal o- -> /ɔ/ if another vowel follows (not 'o', 'os' by themselves).
text = rsub(text, "(#O)(.-#)", function(o, rest)
if rfind(rest, "^[^" .. accent .. "]") and rfind(rest, V) then
return "#ɔ" .. rest
else
return o .. rest
end
end)
-- All other unmarked unstressed non-nasal e, o, a -> /ɨ/ /u/ /ɐ/
local portugal_unstressed_vowel = {["A"] = "ɐ", ["E"] = "ɨ", ["O"] = "u"}
text = rsub(text, "([AEO])([^" .. accent .. "])",
function(v, after) return portugal_unstressed_vowel[v] .. after end)
end
-- Remaining vowels.
-- All remaining a -> /a/ (should always be stressed).
text = rsub(text, "A([^" .. quality .. "])", "a%1")
-- Ignore quality markers on i, u; only one quality.
text = rsub(text, "([iu])" .. quality_c, "%1")
-- Convert a/e/o + quality marker appropriately.
local vowel_quality = {
["A" .. AC] = "a", ["A" .. CFLEX] = "ɐ",
["E" .. AC] = "ɛ", ["E" .. CFLEX] = "e",
["O" .. AC] = "ɔ", ["O" .. CFLEX] = "o",
}
text = rsub(text, "([AEO]" .. quality_c .. ")", vowel_quality)
-- Stressed o in hiatus ([[voo]], [[boa]], [[perdoe]], etc.) is closed /o/.
text = rsub(text, "O(ˈ%." .. V .. ")", "o%1")
-- Stressed closed /o/ in Northern Portugal has a following optional /w/.
if style=="npt" then
text = rsub(text, "o(ˈ%." .. V .. ")", "o(w)%1")
end
-- Any remaining E or O (always without quality marker) is an error.
if rfind(text, "[EO]") then
err("Stressed e or o not occurring nasalized or in a diphthong must be marked for quality using é/ê or ó/ô")
end
-- Finally, eliminate DOTUNDER, now that we have done all vowel reductions.
text = rsub(text, DOTUNDER, "")
if brazil then
-- Epenthesize /(j)/ in [[faz]], [[mas]], [[três]], [[dez]], [[feroz]], [[luz]], [[Jesus]], etc. Note, this only
-- triggers at actual word boundaries (not before -mente), and not on nasal vowels or diphthongs. To defeat this
-- (e.g. in plurals), respell using 'ss' or 'hs'.
text = rsub(text, "(" .. V .. "ˈ)([sz]#[^@])", "%1Y%2")
-- Also should happen at least before + (cf. [[rapazinho]] respelled 'rapaz+inho', [[vozinha]] respelled
-- 'vóz+inha').
text = rsub(text, "(" .. V .. "ˈ)(%.?[sz]%+)", "%1Y%2")
-- But should not happen after /i/.
text = rsub(text, "iˈY", "iˈ")
end
-- 'S' here represents earlier ss. Word-finally it is used to prevent epenthesis of (j) and should behave
-- like 's'. Elsewhere (between vowels) it should behave like 'ç'.
text = rsub(text, "S#", "s#")
text = rsub(text, "S", "ç")
-- s, z
-- s in trans + V -> z: [[transação]], [[intransigência]]
text = rsub(text, "(trɐ" .. stress_c .. "*" .. TILDE .. ".)s(" .. V .. ")", "%1z%2")
-- word final z -> s
text = rsub(text, "z#", "s#")
-- s is voiced between vowels (not nasalized) or between vowel and voiced consonant, including across word
-- boundaries; may be fed by previous rule. We have to split this into two rules before /s/ should not be voiced
-- between nasal vowel and another vowel ([[cansar]]) but should be voiced between nasal vowel and a voiced
-- consonant ([[transgredir]]). Note that almost all occurrences of nasal vowel + s + voiced consonant are in
-- trans- which potentially could be handled above, but there may be others, e.g. [[Flensburg]].
text = rsub(text, "(" .. V .. stress_c .. "*Y?%.?)s(" .. wordsep_c .. "*h?[" .. vowel .. glide .. "])", "%1z%2")
text = rsub(text, "(" .. V .. accent_c .. "*Y?%.?)s(" .. wordsep_c .. "*h?[" .. voiced_cons .. "])", "%1z%2")
-- z before voiceless consonant, e.g. [[Nazca]]; c and q already removed
text = rsub(text, "z(" .. wordsep_c .. "*[çfkpsʃt])", "s%1")
-- Coda /s,z/
if portugal or style == "rio" then
-- In General Portugal and Rio de Janeiro; s/z before consonant (including across word boundaries) or end of utterance -> ʃ/ʒ;
-- but not word-initially (e.g. [[stressado]]).
local sibilant = {["s"] = "ʃ", ["z"] = "j"}
text = rsub(text, "([sz])(##)", function(sz, after) return sibilant[sz] .. after end)
-- s/z are maintained word-initially. To implement this, insert TEMP1 directly before the s/z we want to preserve, then check for this
-- TEMP1 not being present when converting to shibilant, then remove TEMP1.
text = rsub(text, "([# %-]#)([sz])", "%1" .. TEMP1 .. "%2")
text = rsub_repeatedly(text, "([^" .. TEMP1 .. "])([sz])(" .. wordsep_c .. "*" .. C_NOT_H_OR_GLIDE .. ")",
function(before, sz, after) return before .. sibilant[sz] .. after end)
text = rsub(text, TEMP1, "")
end
text = rsub(text, "ç", "s")
text = rsub(text, "j", "ʒ")
-- Reduce identical sibilants, including across word boundaries.
text = rsub(text, "([szʃʒ])(" .. wordsep_c .. "*)(%1)", "%2%1")
if style == "rio" then
-- Also reduce sibilant ([[descer]], [[as]] [[zonas]]); not in Portugal, but in Portugal we later
-- generate two outputs in this case, either /ʃs/ and /ʒz/ (careful pronunciation) or /ʃ/ and /ʒ/ (natural
-- pronunciation). Note that the reduction of /ʃs/ to /ʃ/ in Portugal is different from the reduction of the
-- same to /s/ in Brazil (though it shouldn't be)
text = rsub(text, "ʃ(" .. wordsep_c .. "*s)", "%1")
text = rsub(text, "ʒ(" .. wordsep_c .. "*z)", "%1")
end
-- N/M from double n/m
text = rsub(text, "[NM]", {["N"] = "n", ["M"] = "m"})
if portugal then
--Drop unneeded 'h' in 'rh' respellings for European Portuguese
text = rsub(text, "rh","r")
if style=="spt" then
--Paragoge of i in word-final r/l in Southern Portugal
text = rsub(text, "ˈr#", "ˈ.ri#")
text = rsub(text, "ˈl#", "ˈ.li#")
end
end
-- r
-- Double rr -> ʁ already handled above.
-- Initial r or l/n/s/z + r -> strong r (ʁ).
text = rsub(text, "([#" .. TILDE .. "lszʃʒ]%.?)r", "%1ʁ")
if brazil then
-- Word-final r before vowel in verbs is /(ɾ)/.
text = rsub(text, "([aɛei]ˈ)r(#" .. wordsep_c .. "*h?" .. V .. ")", "%1(ɾ)%2")
-- Coda r before vowel is /ɾ/.
text = rsub(text, "r([.#]" .. wordsep_c .. "*h?" .. V .. ")", "ɾ%1")
end
-- Word-final r in Brazil in verbs (not [[pôr]]) is usually dropped. Use a spelling like 'marh' for [[mar]]
-- to prevent this. Make sure not to do this before -mente/-zinho ([[polegarzinha]], [[popularmente]]).
if brazil then
text = rsub(text, "([aɛei]ˈ)r(#[^@])",
"%1(" .. (style == "sp" and "ɾ" or style == "sbr" and "ɻ" or "ʁ") .. ")%2")
if style ~= "sp" then
-- Coda r in Southern Brazil is [ɻ], otherwise outside of São Paulo is /ʁ/.
text = rsub(text, "r(" .. C .. "*[.#])", (style == "sbr" and "ɻ" or "ʁ") .. "%1")
end
end
-- All other r -> /ɾ/.
text = rsub(text, "r", "ɾ")
if brazil and phonetic then
-- "Strong" ʁ before voiced consonant is [ɦ] in much of Brazil, [ʁ] in Rio. Use R as a temporary symbol.
text = rsub(text, "ʁ(" .. wordsep_c .. "*[" .. voiced_cons .. "])", style == "rio" and "R%1" or "ɦ%1")
-- Other "strong" ʁ is [h] in much of Brazil, [χ] in Rio. Use H because later we remove all <h>.
text = rsub(text, "ʁ", style == "rio" and "χ" or "H")
text = rsub(text, "R", "ʁ")
end
-- Nasal Diphthong <ẽi> and <ɛi>
if portugal then
-- In Portugal, always lower e -> ɐ before j when nasalized.
text = rsub(text, "e(" .. accent_c .. TILDE .. "*%.?y)", "ɐ%1")
-- In Portugal, lower ɛ -> e before i in <ɛi> (the open pronunciation is dated)
text = rsub(text, "ɛ(" .. accent_c .. "*i)", "e%1")
end
-- Diphthong <ei> (if unchanged, it shall yield /ej/; this should be the case for African pronunciations)
if brazil then
-- In Brazil, add optional /j/ in <eir>, <eij>, <eig> and <eix> (as in [[cadeira]], [[beijo]], [[manteiga]] and
-- [[peixe]]).
text = rsub(text, "(e" .. accent_c .. "*)i(%.[ɾʒgʃ])", "%1(j)%2")
-- [In Brazil, add optional /j/ in <aix> (as in [[caixa]] and [[baixo]]).] -- This was added by an IP, see
-- [[Special:Contributions/186.212.6.138]]; this seems non-standard to me. If we are to include it, it should
-- not be done this way, but as two separate outputs with the one lacking the /j/ marked with a qualifier such
-- as "non-standard"; compare the way the initial enC- is handled (near the end of export.IPA()), where there
-- are two outputs, with /ẽC-/ marked as "careful pronunciation" and /ĩC-/ marked as "natural pronunciation".
-- (Benwing2)
-- text = rsub(text, "(a" .. accent_c .. "*)i(%.ʃ)", "%1(j)%2")
elseif style == "spt" then
-- In Southern Portugal, <ei> monophthongizes to <e>
text = rsub(text, "(e" .. accent_c .. "*)i", "%1")
elseif style == "gpt" then
-- In general Portugal, lower e -> ɐ before i in <ei>.
text = rsub(text, "e(" .. accent_c .. "*i)", "ɐ%1")
-- In general Portugal, lower e -> ɐ before j
text = rsub(text, "e(" .. accent_c .. "*%.?y)", "ɐ%1")
-- In general Portugal, lower e -> ɐ(j) before other palatals.
text = rsub(text, "e(" .. stress_c .. "*)(%.?[ʒʃɲʎ](" .. V .. "))", "ɐ%1(j)%2")
end
-- Diphthong <ou> (if unchanged, it shall yield /o(w)/)
if style == "spt" then
-- In Southern Portugal, <ou> always monophthongizes to <o>
text = rsub(text, "(o" .. accent_c .. "*)u", "%1")
elseif style == "npt" then
-- In Northern Portugal (and Galicia), <ou> is kept
text = rsub(text, "(o" .. accent_c .. "*)u", "%1w")
end
-- Else optional /w/ in <ou>
text = rsub(text, "(o" .. accent_c .. "*)u", "%1(w)")
-- Stop consonants.
if brazil then
-- Palatalize t/d + i/y -> affricates in Brazil.
text = rsub(text, "([td])(" .. word_or_component_sep_c .. "*[" .. high_front_vocalic .. "])",
function(td, high_vocalic) return palatalize_td[td] .. high_vocalic end)
elseif portugal and phonetic then
-- Fricativize voiced stops in Portugal when not utterance-initial or after a nasal; also not in /ld/.
-- Easiest way to do this is to convert all voiced stops to fricative and then back to stop in the
-- appropriate contexts.
local fricativize_stop = { ["b"] = "β", ["d"] = "ð", ["g"] = "ɣ" }
local occlude_fricative = { ["β"] = "b", ["ð"] = "d", ["ɣ"] = "g" }
text = rsub(text, "[bdg]", fricativize_stop)
text = rsub(text, "##([βðɣ])", function(bdg) return "##" .. occlude_fricative[bdg] end)
text = rsub(text, "(" .. TILDE .. wordsep_c .. "*)([βðɣ])", function(before, bdg) return before .. occlude_fricative[bdg] end)
text = rsub(text, "(l" .. wordsep_c .. "*)ð", "%1d")
end
-- Glides and l. ou -> o(w) must precede coda l -> w in Brazil, because <ol> /ow/ cannot be reduced to /o/.
-- ou -> o(w) before conversion of remaining diphthongs to vowel-glide combinations so <ow> can be used to
-- indicate a non-reducible glide.
-- Handle coda /l/.
if brazil then
-- Coda l -> /w/ in Brazil.
text = rsub(text, "l(" .. C .. "*[.#])", "w%1")
elseif phonetic then
-- Coda l -> [ɫ] in Portugal (and Rio Grande do Sul, according to Cunha-Cintra)
text = rsub(text, "l(" .. C .. "*[.#])", "ɫ%1")
end
text = rsub(text, "y", "j")
if brazil then
text = rsub(text, "Y", "(j)") -- epenthesized in [[faz]], [[três]], etc.
else
-- 'I' in Portugal represents word-initial (i) before sC, except after /i/ (e.g. [[antiestático]]), in which
-- case it is elided. In the latter case, we need to elide the word/component separators, otherwise we end up
-- with an extra syllable divider: /ˌɐ̃.ti.ʃˈta.ti.ku/ instead of correct /ˌɐ̃.tiʃˈta.ti.ku/.
text = rsub(text, "(i" .. accent_c .. "*)" .. word_or_component_sep_c .. "*#I", "%1")
text = rsub(text, "I", "(i)")
end
local vowel_termination_to_glide = brazil and phonetic and
{["i"] = "ɪ̯", ["j"] = "ɪ̯", ["u"] = "ʊ̯", ["w"] = "ʊ̯"} or
{["i"] = "j", ["j"] = "j", ["u"] = "w", ["w"] = "w"}
-- i/u as second part of diphthong becomes glide.
text = rsub(text, "(" .. V .. accent_c .. "*" .. "%(?)([ijuw])",
function(v1, v2) return v1 .. vowel_termination_to_glide[v2] end)
-- nh
if brazil and phonetic and style ~= "sbr" then
-- [[unha]] pronounced [ˈũ.j̃ɐ]; nasalization of previous vowel handled above. But initial nh- e.g. [[nhaca]],
-- [[nheengatu]], [[nhoque]] is [ɲ]. I *think* this doesn't happen in South Brazil. We do have the phonetic
-- representation [ẽj̃.pũˈj̃aɾ] given for [[empunhar]], but this is the only such case and may be a mistake.
text = rsub(text, "([^#])ɲ", "%1j" .. TILDE)
end
if portugal then
-- Suppress final -ɨ before a vowel
text = rsub(text, "ɨ#[ %-]#(" .. V .. ")", "‿%1")
--Make optional utterance-finally
-- text = rsub(text, "ɨ##", "(ɨ)##")
-- (ɨ) after l when suppressed should convert to coda ɫ, so split it later into two pronuns.
-- text = rsub(text, "l%(ɨ%)##", "L##")
-- (ɨ) after r when suppressed should also convert to coda
-- text = rsub(text, "ɾ%(ɨ%)##", "R##")
end
text = rsub(text, "g", "ɡ") -- U+0261 LATIN SMALL LETTER SCRIPT G
text = rsub(text, "[ʧʤ]", {["ʧ"] = "t͡ʃ", ["ʤ"] = "d͡ʒ"})
text = rsub(text, "tʃ", "t͡ʃ")
text = rsub(text, "dʒ", "d͡ʒ")
text = rsub(text, "h", "")
text = rsub(text, "H", "h")
return text
end
-- Generate the IPA for a single term respelling `text` in the specified `style` ('gbr', 'rio', etc.; see
-- all_style_descs above). Return value is a list of objects of the following form:
-- { phonemic = STRING, phonetic = STRING, qualifiers = {STRING, ...} }
-- Note that the returned qualifiers are only those generated automatically as a result of certain characteristics of
-- the respelling, e.g. in Brazil initial em-/en- + consonant has two outputs, one labeled "careful pronunciation" and
-- the other "natural pronunciation". User-specified qualifiers are added at the end by the caller of IPA(), and
-- prepended to the auto-generated qualifiers.
function export.IPA(text, style)
local brazil = m_table.contains(export.all_style_groups.br, style) and true or false
local portugal = m_table.contains(export.all_style_groups.pt, style) and true or false
local origtext = text
local function err(msg)
error(msg .. ": " .. origtext)
end
text = ulower(text)
-- decompose everything but ç and ü
text = mw.ustring.toNFD(text)
text = rsub(text, ".[" .. CEDILLA .. DIA .. "]", {
["c" .. CEDILLA] = "ç",
["u" .. DIA] = "ü",
})
text = reorder_accents(text, err)
-- convert commas and en/en dashes to IPA foot boundaries
text = rsub(text, "%s*[,–—]%s*", " | ")
-- question mark or exclamation point in the middle of a sentence -> IPA foot boundary
text = rsub(text, "([^%s])%s*[!?]%s*([^%s])", "%1 | %2")
-- canonicalize multiple spaces and remove leading and trailing spaces
local function canon_spaces(text)
text = rsub(text, "%s+", " ")
text = rsub(text, "^ ", "")
text = rsub(text, " $", "")
return text
end
text = canon_spaces(text)
local words = m_strutils.capturing_split(text, "([ %-]+)")
local function word_is_prefix(i)
-- Check for prefixes, either a final prefix (followed by "-" separator, then a blank word, then no more
-- words) or a non-final prefix (followed by "- " separator).
return i == #words - 2 and words[i + 1] == "-" and words[i + 2] == "" or i < #words and words[i + 1] == "- "
end
for i = 1, #words, 2 do
local word = words[i]
-- Make prefixes unstressed with vowel reduction unless they have an explicit stress marker;
-- likewise for certain monosyllabic words (e.g. [[o]], [[se]], [[de]], etc.; also [[a]], [[das]], etc.
-- in Portugal) without stress marks.
if word_is_prefix(i) and not rfind(words[i], accent_c) or unstressed_words[word] or
portugal and unstressed_full_vowel_words_brazil[word] then
-- add DOTOVER to the last vowel not the first one, or we will mess up 'que' by
-- adding the DOTOVER after the 'u'
word = rsub(word, "^(.*" .. V .. quality_c .. "*)", "%1" .. DOTOVER)
end
-- Make certain monosyllabic words (e.g. [[meu]], [[com]]; also [[a]], [[das]], etc. in Brazil)
-- without stress marks be unstressed without vowel reduction.
if unstressed_full_vowel_words[word] or brazil and unstressed_full_vowel_words_brazil[word] then
-- add DOTUNDER to the first vowel not the last one, or we will mess up 'meu' by
-- adding the DOTUNDER after the 'u'; add after a quality marker for à, às
word = rsub(word, "^(.-" .. V .. quality_c .. "*)", "%1" .. DOTUNDER)
end
-- Some unstressed words need special pronunciation.
word = unstressed_pronunciation_substitution[word] or word
words[i] = word
end
text = table.concat(words)
-- Now eliminate word-final question mark and exclamation point (converted to foot boundary above when word-medial).
text = rsub(text, "[!?]", "")
-- Apostrophe becomes tie (e.g. in [[barriga d'agua]]).
text = rsub(text, "'", "‿")
-- User-specified # as in i# (= i. or y) and u# (= u. or w) becomes TEMP1 so we can add # for word boundaries.
text = rsub(text, "#", TEMP1)
-- Put # at word beginning and end and double ## at text/foot boundary beginning/end.
text = rsub(text, " | ", "# | #")
text = "##" .. rsub(text, " ", "# #") .. "##"
-- Eliminate hyphens indicating prefixes/suffixes; but preserve a marker indicating prefixes, so we can later
-- convert primary to secondary stress.
text = rsub(text, "(" .. V .. charsep_c .. "*)(%-#)", "%1" .. PSEUDOCONS .. "%2")
text = rsub(text, "%-#", "#" .. PREFIX_MARKER)
text = rsub(text, "#%-(" .. V .. ")", "#" .. PSEUDOCONS .. "%1")
text = rsub(text, "#%-", "#")
local variants
-- Map over each element in `variants`. If `from` is found in the element, replace the element with two elements, one
-- obtained by replacing `from` with `to1` and the other by replacing `from` with `to2`. If `to2` is nil, only one
-- element replaces the original element.
local function flatmap_and_sub_pre(from, to1, qual1, to2, qual2)
variants = flatmap(variants, function(item)
if rfind(item.respelling, from) then
local retval = {
{
respelling = rsub(item.respelling, from, to1),
qualifiers = combine_qualifiers(item.qualifiers, qual1),
}
}
if to2 then
table.insert(retval,
{
respelling = rsub(item.respelling, from, to2),
qualifiers = combine_qualifiers(item.qualifiers, qual2),
}
)
end
return retval
else
return {item}
end
end)
end
if brazil then
-- Remove grave accents and macrons, which have special meaning only for Portugal. Do this before handling o^
-- and similar so we can write áutò^:... and have it correctly give 'autò-' in Portugal but 'áutu-,áuto-' in
-- Brazil.
text = rsub(text, "[" .. GR .. MACRON .. "]", "")
elseif portugal then
-- Convert grave accents and macrons to explicit dot-under + quality marker.
local grave_macron_to_quality = {
[GR] = AC,
[MACRON] = CFLEX,
}
text = rsub(text, "[" .. GR .. MACRON .. "]", function(acc) return grave_macron_to_quality[acc] .. DOTUNDER end)
-- ê*/ô* -> é/ó and é*/ó* -> ê/ô (reverse accents)
text = rsub(text, "([eo])([" .. AC .. CFLEX .. "])%*", function(eo, acc)
return eo .. (acc == CFLEX and AC or CFLEX) end)
-- Treat vowel after des- as word-initial, as in Brazil.
text = rsub(text, "(" .. word_or_component_sep_c .. ")des%^+", "%1des++")
-- Remove i*, i^ and i^^ not followed by a vowel (i.e. Brazilian epenthetic i), but not i^ and i^^ followed or
-- preceded by a vowel (which has a totally different meaning, i.e. i or y in Brazil).
-- Also remove all remaining ^.
text = rsub(text, "i%^+(" .. V .. ")", "i%1")
text = rsub(text, "(" .. V .. ")i%^+", "%1i")
text = rsub(text, "i?[*%^]+", "")
end
variants = {{respelling = text}}
if brazil then
-- Handle i^ and i^^ before a vowel = /i/ or /j/.
flatmap_and_sub_pre("i%^%^(" .. V .. ")", "y%1", nil, "i.%1", nil)
flatmap_and_sub_pre("i%^(" .. V .. ")", "i.%1", nil, "y%1", nil)
-- Handle i^ and i^^ after a vowel = /i/ or /j/; mostly useful for ui^
flatmap_and_sub_pre("(" .. V .. ")i%^%^", "%1y", nil, "%1.i", nil)
flatmap_and_sub_pre("(" .. V .. ")i%^", "%1.i", nil, "%1y", nil)
-- Handle i^ and i^^ not before a vowel = optional epenthetic /i/.
if style == "sbr" then
-- Epenthetic /i/ seems less common in South Brazil. Make i^^ not epenthesize (but still palatalize /t/ and /d/),
-- and i^ epenthesize but not as the first option.
flatmap_and_sub_pre("i%^%^(" .. NV_NOT_SPACING_CFLEX .. ")", "Ɨ%1", nil)
flatmap_and_sub_pre("i%^(" .. NV_NOT_SPACING_CFLEX .. ")", "Ɨ%1", nil, "I%1", nil)
else
flatmap_and_sub_pre("i%^%^(" .. NV_NOT_SPACING_CFLEX .. ")", "Ɨ%1", nil, "I%1", nil)
flatmap_and_sub_pre("i%^(" .. NV_NOT_SPACING_CFLEX .. ")", "I%1", nil, "Ɨ%1", nil)
end
-- Handle i* = epenthetic /i/.
flatmap_and_sub_pre("i%*", "I", nil)
-- Handle u^ and u^^ = /u/ or /w/.
flatmap_and_sub_pre("u%^%^", "w", nil, "u.", nil)
flatmap_and_sub_pre("u%^", "u.", nil, "w", nil)
if style == "sbr" then
-- The raised variant apparently does not occur in South Brazil.
flatmap_and_sub_pre("([eo])%^+", "%1", nil)
else
-- Handle e^ and e^^ = /e/ or /i/; handle o^ and o^^ = /o/ or /u/.
-- Do e^ and o^ together so we get only two outputs, not four, if they cooccur.
-- Likewise for e^^ and o^^.
flatmap_and_sub_pre("([eo])%^%^",
function(eo) return eo == "e" and "i" or "u" end, nil,
"%1", nil)
flatmap_and_sub_pre("([eo])%^",
"%1", nil,
function(eo) return eo == "e" and "i" or "u" end, nil)
end
-- Handle ê*/ô*/é*/ó* = same as without asterisk.
flatmap_and_sub_pre("([eo][" .. AC .. CFLEX .. "])%*", "%1", nil)
-- Handle des^ at beginning of word or component = des++ or dis++, and des^^ = opposite order. But apparently
-- not in South Brazil, where the raised variant doesn't occur.
if style == "sbr" then
flatmap_and_sub_pre("(" .. word_or_component_sep_c .. ")des%^+", "%1des++", nil)
else
flatmap_and_sub_pre("(" .. word_or_component_sep_c .. ")des%^%^", "%1dis++", nil, "%1des++", nil)
flatmap_and_sub_pre("(" .. word_or_component_sep_c .. ")des%^", "%1des++", nil, "%1dis++", nil)
end
for _, variant in ipairs(variants) do
if rfind(variant.respelling, "[*%^]") then
err(("* or ^ remains after applying all known replacements involving these characters (result is '%s')")
:format(variant.respelling))
end
end
end
-- Replace i# and u# sequences (above we replaced # with TEMP1).
flatmap_and_sub_pre("i" .. TEMP1, "i.", nil, "y", {"faster pronunciation"})
flatmap_and_sub_pre("u" .. TEMP1, "u.", nil, "w", {"faster pronunciation"})
-- Given a single variant element representing a preprocessed respelling along with any qualifiers resulting from the
-- preprocessing, generate the phonemic and phonetic representations using one_term_ipa() and postprocess to get the
-- final IPA. The postprocessing is there in general to handle cases where a single respelling produces multiple
-- outputs, such as Brazil -io producing either /i.u/ or /iw/. Note that user-specified qualifiers are not yet present
-- at any stage of this IPA generation; they are added at the end by the caller of IPA().
local function call_one_term_ipa(variant)
local result = {{
phonemic = one_term_ipa(variant.respelling, style, false, err),
phonetic = one_term_ipa(variant.respelling, style, true, err),
qualifiers = variant.qualifiers,
}}
local function unpack_if_list(obj)
if type(obj) == "table" and #obj == 2 and obj[1] then
return unpack(obj)
else
return obj, obj
end
end
-- Map over each element in `result`. If `from` is found in the element, replace the element with two elements, one
-- obtained by replacing `from` with `to1` in both the phonemic and phonetic representations of the existing element,
-- and the other similarly by replacing `from` with `to2` in both the phonemic and phonetic representations. If `to2`
-- is nil, only one element replaces the original element. `qual1`, if non-nil, is a list of qualifiers to be added to
-- the new element associated with `to1` (appended to any existing qualifiers). Similarly, `qual2` is a list of
-- qualifiers to be added to the new element associated with `to2`. Normally, `to1` and `to2` can be anything that can
-- be used as the replacement argument to the Lua gsub() function, i.e. a string, a function or a table. However, if
-- `to1` or `to2` is a two-element list, it is unpacked into two separate substitution objects, respectively for the
-- phonemic and phonetic representations of the element being substituted. This is used, for example, when handling
-- Ú resulting from stressed final '-io(s)', so different phonemic vs. phonetic replacements can be used (/w/ vs [ʊ̯]).
local function flatmap_and_sub_post(from, to1, qual1, to2, qual2)
result = flatmap(result, function(item)
if rfind(item.phonemic, from) or rfind(item.phonetic, from) then
local to1_phonemic, to1_phonetic = unpack_if_list(to1)
local retval = {
{
phonemic = rsub(item.phonemic, from, to1_phonemic),
phonetic = rsub(item.phonetic, from, to1_phonetic),
qualifiers = combine_qualifiers(item.qualifiers, qual1),
}
}
if to2 then
local to2_phonemic, to2_phonetic = unpack_if_list(to2)
table.insert(retval,
{
phonemic = rsub(item.phonemic, from, to2_phonemic),
phonetic = rsub(item.phonetic, from, to2_phonetic),
qualifiers = combine_qualifiers(item.qualifiers, qual2),
}
)
end
return retval
else
return {item}
end
end)
end
if brazil then
-- Convert Ẽ from initial [[em]] as a word by itself to either /ẽj̃/ and /ĩ/.
flatmap_and_sub_post("Ẽ" .. TILDE .. "#", "e" .. TILDE .. "j" .. TILDE .. "#", {"careful pronunciation"},
"i" .. TILDE .. "#", {"natural pronunciation"})
-- Convert Ẽ from initial em-/en- + consonant to either /ẽ/ and /ĩ/.
flatmap_and_sub_post("Ẽ", "e", {"careful pronunciation"}, "i", {"natural pronunciation"})
flatmap_and_sub_post("I", "i", nil, "e", nil)
-- Convert Ú resulting from stressed final '-io(s)'.
flatmap_and_sub_post("%.Ú", ".u", nil, {"w", "ʊ̯"}, nil)
elseif portugal then
flatmap_and_sub_post("ʃ(" .. wordsep_c .. "*)s",
"ʃ%1s", {"careful pronunciation"}, "%1ʃ", nil)
flatmap_and_sub_post("ʒ(" .. wordsep_c .. "*)z",
"ʒ%1z", {"careful pronunciation"}, "%1ʒ", nil)
flatmap_and_sub_post("Ɔ", "o", nil, "ɔ", nil)
-- Split (ɨ) after l or r into two pronuns, one with ɨ and the other without it (with one fewer syllables and coda).
--flatmap_and_sub_post("%.([^.# ]-)L#", ".%1lɨ#", nil, {"%1l#", "%1ɫ#"}, nil)
--flatmap_and_sub_post("%.([^.# ]-)R#", ".%1ɾɨ#", nil, {"%1ɾ#", "%1ɾ#"}, nil)
end
flatmap_and_sub_post("([ÌÙ])%.",
function(iu) return iu == "Ì" and "i." or "u." end, nil,
function(iu) return iu == "Ì" and "j" or "w" end, {"faster pronunciation"})
return result
end
-- Final changes to the generated IPA to produce what's shown to the user. We used to do this at the end of
-- one_term_ipa() but the stuff below needs to happen after the expansion of Ì. and Ù. in Brazil to either i./u.
-- or j/w, because the latter transformation involves removing a syllable boundary, which will cause a stress mark
-- on the following syllable to retract to the beginning of the newly combined syllable. To avoid lots of hassle,
-- we postpone this stress mark movement till now.
local function finalize_ipa(text, phonetic)
-- Convert Brazil i/u in hiatus to ɪ/ʊ in the phonetic representation. This needs to happen after handling of
-- Ì. and Ù., which feeds this change.
if brazil and phonetic then
local phonetic_hiatus_iu_to_actual = {["i"] = "ɪ", ["u"] = "ʊ"}
text = rsub(text, "([iu])(%." .. V .. ")", function(iu, after) return phonetic_hiatus_iu_to_actual[iu] .. after end)
end
-- Stress marks and syllable dividers.
-- Component separators that aren't transparent to syllabification need to be made into syllable dividers.
text = rsub(text, non_syl_transp_component_sep_c, ".")
-- IPA stress marks in components followed by + should be removed.
text = rsub(text, ipa_stress_c .. "([^" .. word_divider .. component_sep .. "]*%+)", "%1")
-- Component separators that are transparent to syllabification need to be removed now, before moving IPA stress marks
-- to the beginning of the syllable, so they don't interfere in this process.
text = rsub(text, syl_transp_component_sep_c .. "#?", "")
-- Move IPA stress marks to the beginning of the syllable.
text = rsub_repeatedly(text, "([#.])([^#.]*)(" .. ipa_stress_c .. ")", "%1%3%2")
-- Suppress syllable divider before IPA stress indicator.
text = rsub(text, "%.(#?" .. ipa_stress_c .. ")", "%1")
-- Make all primary stresses but the last one in a given word be secondary. May be fed by the first rule above.
text = rsub_repeatedly(text, "ˈ([^ ]+)ˈ", "ˌ%1ˈ")
-- Make primary stresses in prefixes become secondary.
text = rsub_repeatedly(text, "ˈ([^#]*#" .. PREFIX_MARKER .. ")", "ˌ%1")
-- Remove # symbols at word/text boundaries, as well as _ (which forces separate interpretation), pseudo-consonant
-- markers (at edges of some prefixes/suffixes), and prefix markers, and recompose.
text = rsub(text, "[#_" .. PSEUDOCONS .. PREFIX_MARKER .. "]", "")
text = mw.ustring.toNFC(text)
return text
end
variants = flatmap(variants, call_one_term_ipa)
for i, variant in ipairs(variants) do
variants[i].phonemic = finalize_ipa(variants[i].phonemic, false)
variants[i].phonetic = finalize_ipa(variants[i].phonetic, true)
end
return variants
end
-- For bot usage; {{#invoke:pt-pronunc|IPA_json|SPELLING|style=STYLE}}
-- where
--
-- 1. SPELLING is the word or respelling to generate pronunciation for;
-- 2. required parameter style= indicates the pronunciation style to generate
-- (e.g. "rio" for Rio/Carioca pronunciation, "lisbon" for Lisbon pronunciation;
-- see the comment above export.IPA() above for the full list);
-- 3. phonetic=1 specifies to generate the phonetic rather than phonemic pronunciation;
function export.IPA_json(frame)
local iparams = {
[1] = {},
["style"] = {required = true},
}
local iargs = require("Module:parameters").process(frame.args, iparams)
local pronuns = export.IPA(iargs[1], iargs.style)
return require("Module:JSON").toJSON(pronuns)
end
-- "Canonicalize" a single respelling (after splitting multiple respellings on comma and parsing off inline
-- modifiers). This currently handles '+' and substitution notation.
function export.canonicalize_respelling(text, pagename)
if not text or text == "+" then
text = pagename
end
-- No such substitutions currently.
-- text = rsub(text, "%[([hHxXjJ])%]", function(sound)
-- return explicit_sound_to_substitution[ulower(sound)]
-- end)
-- Implement substitution notation.
if rfind(text, "^%[.*%]$") then
local subs = rsplit(rmatch(text, "^%[(.*)%]$"), ";")
text = pagename
for _, sub in ipairs(subs) do
local fromto = rsplit(sub, ":")
if #fromto < 2 then
error("Bad substitution spec " .. sub .. " in {{pt-IPA}}, should have a colon in it")
end
local from, to
if #fromto == 2 then
from, to = fromto[1], fromto[2]
else
from = fromto[1]
table.remove(fromto, 1)
to = table.concat(fromto, ":")
end
local newtext = text
if rfind(from, "^%^") then
-- whole-word match
from = rmatch(from, "^%^(.*)$")
newtext = rsub(text, "%f[%a]" .. require("Module:string utilities").pattern_escape(from) .. "%f[%A]", to)
else
newtext = rsub(text, require("Module:string utilities").pattern_escape(from), to)
end
if from ~= to and newtext == text then
error("Substitution spec " .. sub .. " didn't match respelling '" .. text .. "'")
end
text = newtext
end
end
return text
end
function export.express_styles(inputs, args_style, pagename)
local pronuns_by_style = {}
local expressed_styles = {}
local function dostyle(style)
pronuns_by_style[style] = {}
for _, val in ipairs(inputs[style].terms) do
local respelling = val.term
respelling = export.canonicalize_respelling(respelling, pagename)
local refs
if #val.ref == 0 then
refs = nil
else
refs = {}
for _, refspec in ipairs(val.ref) do
local this_refs = require("Module:references").parse_references(refspec)
for _, this_ref in ipairs(this_refs) do
table.insert(refs, this_ref)
end
end
end
local pronuns = export.IPA(respelling, style)
for _, pronun in ipairs(pronuns) do
local qualifiers = m_table.deepCopy(val.q)
if pronun.qualifiers then
for _, qual in ipairs(pronun.qualifiers) do
m_table.insertIfNot(qualifiers, qual)
end
end
pronun.qualifiers = #qualifiers > 0 and qualifiers or nil
pronun.refs = refs
m_table.insertIfNot(pronuns_by_style[style], pronun)
end
end
end
local function all_available(styles)
local available_styles = {}
for _, style in ipairs(styles) do
if pronuns_by_style[style] then
table.insert(available_styles, style)
end
end
return available_styles
end
local function express_style(styles, indent)
local hidden_tag, tag
indent = indent or 1
if type(styles) == "string" then
styles = {styles}
tag = export.all_style_descs[styles[1]]
hidden_tag = export.all_style_descs[style_to_style_group[styles[1]]]
else
tag = false
hidden_tag = false
end
styles = all_available(styles)
if #styles == 0 then
return
end
local style = styles[1]
-- If style specified, make sure it matches the requested style.
local style_matches
if not args_style then
style_matches = true
else
local or_styles = rsplit(args_style, "%s*,%s*")
for _, or_style in ipairs(or_styles) do
local and_styles = rsplit(or_style, "%s*%+%s*")
local and_matches = true
for _, and_style in ipairs(and_styles) do
local negate
if and_style:find("^%-") then
and_style = and_style:gsub("^%-", "")
negate = true
end
local this_style_matches = false
for _, part in ipairs(styles) do
if part == and_style then
this_style_matches = true
break
end
end
if negate then
this_style_matches = not this_style_matches
end
if not this_style_matches then
and_matches = false
end
end
if and_matches then
style_matches = true
break
end
end
end
if not style_matches then
return
end
local new_style = {
tag = tag,
represented_styles = styles,
pronuns = pronuns_by_style[style],
indent = indent,
bullets = inputs[style].bullets,
pre = inputs[style].pre,
post = inputs[style].post,
}
for _, hidden_tag_style in ipairs(expressed_styles) do
if hidden_tag_style.tag == hidden_tag then
table.insert(hidden_tag_style.styles, new_style)
return
end
end
table.insert(expressed_styles, {
tag = hidden_tag,
styles = {new_style},
})
end
for style, _ in pairs(inputs) do
dostyle(style)
end
local function diff(style1, style2)
if not pronuns_by_style[style1] or not pronuns_by_style[style2] then
return true
end
return not m_table.deepEquals(pronuns_by_style[style1], pronuns_by_style[style2])
end
local gbr_sp_different = diff("gbr", "sp")
local gbr_rio_different = diff("gbr", "rio")
local gbr_sbr_different = diff("gbr", "sbr")
local gpt_cpt_different = diff("gpt", "cpt")
local gpt_spt_different = diff("gpt", "spt")
local gpt_npt_different = diff("gpt", "npt")
local gbr_gpt_different = diff("gbr", "gpt") -- general differences between BP and EP
if not gbr_sp_different and not gbr_rio_different and gbr_sbr_different and
not gpt_cpt_different and not gpt_spt_different and not gpt_npt_different and
not gbr_gpt_different then
-- All the same
express_style(export.all_styles)
else
-- Within Brazil
express_style("gbr")
if gbr_sp_different then
express_style("sp", 2)
end
if gbr_rio_different then
express_style("rio", 2)
end
if gbr_sbr_different then
express_style("sbr", 2)
end
-- Within Portugal
express_style("gpt")
if gpt_npt_different then
express_style("npt", 2)
end
if gpt_cpt_different then
express_style("cpt", 2)
end
if gpt_spt_different then
express_style("spt", 2)
end
end
return expressed_styles
end
function export.show(frame)
-- Create parameter specs
local params = {
[1] = {}, -- this replaces style group 'all'
["style"] = {},
["pagename"] = {},
}
for group, _ in pairs(export.all_style_groups) do
if group ~= "all" then
params[group] = {}
end
end
for _, style in ipairs(export.all_styles) do
params[style] = {}
end
-- Parse arguments
local parargs = frame:getParent().args
local args = require("Module:parameters").process(parargs, params)
local pagename = args.pagename or mw.title.getCurrentTitle().subpageText
-- Set inputs
local inputs = {}
-- If 1= specified, do all styles.
if args[1] then
for _, style in ipairs(export.all_styles) do
inputs[style] = args[1]
end
end
-- Then do remaining style groups other than 'all', overriding 1= if given.
for group, styles in pairs(export.all_style_groups) do
if group ~= "all" and args[group] then
for _, style in ipairs(styles) do
inputs[style] = args[group]
end
end
end
-- Then do individual style settings.
for _, style in ipairs(export.all_styles) do
if args[style] then
inputs[style] = args[style]
end
end
-- If no inputs given, set all styles based on current pagename.
if not next(inputs) then
for _, style in ipairs(export.all_styles) do
inputs[style] = "+"
end
end
-- Parse the arguments.
local put
for style, input in pairs(inputs) do
if input:find("[<%[]") then
local function parse_err(msg)
error(msg .. ": " .. style .. "=" .. input)
end
if not put then
put = require("Module:parse utilities")
end
-- We don't want to split off a comma followed by a space, as in [[rei morto, rei posto]], so replace
-- comma+space with a special character that we later undo.
input = rsub(input, ", ", TEMP1)
-- Parse balanced segment runs involving either [...] (substitution notation) or <...> (inline modifiers). We do this
-- because we don't want commas inside of square or angle brackets to count as respelling delimiters. However, we
-- need to rejoin square-bracketed segments with nearby ones after splitting alternating runs on comma. For example,
-- if we are given "a[x]a<q:learned>,[vol:vôl;ei:éi,ei]<q:nonstandard>", after calling
-- parse_multi_delimiter_balanced_segment_run() we get the following output:
--
-- {"a", "[x]", "a", "<q:learned>", ",", "[vol:vôl;ei:éi,ei]", "", "<q:nonstandard>", ""}
--
-- After calling split_alternating_runs(), we get the following:
--
-- {{"a", "[x]", "a", "<q:learned>", ""}, {"", "[vol:vôl;ei:éi,ei]", "", "<q:nonstandard>", ""}}
--
-- We need to rejoin stuff on either side of the square-bracketed portions.
local segments = put.parse_multi_delimiter_balanced_segment_run(input, {{"<", ">"}, {"[", "]"}})
-- Not with spaces around the comma; see above for why we don't want to split off comma followed by space.
local comma_separated_groups = put.split_alternating_runs(segments, ",")
local parsed = {terms = {}}
for i, group in ipairs(comma_separated_groups) do
-- Rejoin bracketed segments with nearby ones, as described above.
local j = 2
while j <= #group do
if group[j]:find("^%[") then
group[j - 1] = group[j - 1] .. group[j] .. group[j + 1]
table.remove(group, j)
table.remove(group, j)
else
j = j + 2
end
end
for j, segment in ipairs(group) do
group[j] = rsub(segment, TEMP1, ", ")
end
local term = {term = group[1], ref = {}, q = {}}
for j = 2, #group - 1, 2 do
if group[j + 1] ~= "" then
parse_err("Extraneous text '" .. group[j + 1] .. "' after modifier")
end
local modtext = group[j]:match("^<(.*)>$")
if not modtext then
parse_err("Internal error: Modifier '" .. group[j] .. "' isn't surrounded by angle brackets")
end
local prefix, arg = modtext:match("^([a-z]+):(.*)$")
if not prefix then
parse_err("Modifier " .. group[j] .. " lacks a prefix, should begin with one of " ..
"'pre:', 'post:', 'ref:', 'bullets:' or 'q:'")
end
if prefix == "ref" or prefix == "q" then
table.insert(term[prefix], arg)
elseif prefix == "pre" or prefix == "post" or prefix == "bullets" then
if i < #comma_separated_groups then
parse_err("Modifier '" .. prefix .. "' should occur after the last comma-separated term")
end
if parsed[prefix] then
parse_err("Modifier '" .. prefix .. "' occurs twice, second occurrence " .. group[j])
end
if prefix == "bullets" then
if not arg:find("^[0-9]+$") then
parse_err("Modifier 'bullets' should have a number as argument")
end
parsed.bullets = tonumber(arg)
else
parsed[prefix] = arg
end
else
parse_err("Unrecognized prefix '" .. prefix .. "' in modifier " .. group[j]
.. ", should be one of 'pre', 'post', 'ref', 'bullets' or 'q'")
end
end
table.insert(parsed.terms, term)
end
if not parsed.bullets then
parsed.bullets = 1
end
inputs[style] = parsed
else
local terms = {}
-- We don't want to split on comma+space, which should become a foot boundary as in
-- [[rei morto, rei posto]].
local subbed_input = rsub(input, ", ", TEMP1)
for _, term in ipairs(rsplit(subbed_input, ",")) do
term = rsub(term, TEMP1, ", ")
table.insert(terms, {term = term, ref = {}, q = {}})
end
inputs[style] = {
terms = terms,
bullets = 1,
}
end
end
local expressed_styles = export.express_styles(inputs, args.style, pagename)
local lines = {}
local function format_style(tag, expressed_style, is_first)
local pronunciations = {}
local formatted_pronuns = {}
-- Loop through each pronunciation. For each one, add the phonemic and (if different) phonetic versions to
-- `pronunciations`, for formatting by [[Module:IPA]], and also create an approximation of the formatted
-- version so that we can compute the appropriate width of the HTML switcher div box that holds the different
-- per-dialect variants.
for i, pronun in ipairs(expressed_style.pronuns) do
table.insert(pronunciations, {
pron = "/" .. pronun.phonemic .. "/",
qualifiers = pronun.qualifiers,
separator = i > 1 and ", " or nil,
})
local formatted_phonemic = "/" .. pronun.phonemic .. "/"
if pronun.qualifiers then
formatted_phonemic = "(" .. table.concat(pronun.qualifiers, ", ") .. ") " .. formatted_phonemic
end
if i > 1 then
formatted_phonemic = ", " .. formatted_phonemic
end
table.insert(formatted_pronuns, formatted_phonemic)
-- Check if phonetic and phonemic are the same. If so, we skip displaying the phonetic version; but in this
-- case, we need to attach any references to the phonemic version.
if pronun.phonetic == pronun.phonemic then
pronunciations[#pronunciations].refs = pronun.refs
else
table.insert(pronunciations, {
pron = "[" .. pronun.phonetic .. "]",
refs = pronun.refs,
separator = " ",
})
local reftext = ""
if pronun.refs then
reftext = string.rep("[1]", #pronun.refs)
end
table.insert(formatted_pronuns, " [" .. pronun.phonetic .. "]" .. reftext)
end
end
-- Number of bullets: When indent = 1, we want the number of bullets given by `expressed_style.bullets`,
-- and when indent = 2, we want `expressed_style.bullets + 1`, hence we subtract 1.
local bullet = string.rep("*", expressed_style.bullets + expressed_style.indent - 1) .. " "
-- Here we construct the formatted line in `formatted`, and also try to construct the equivalent without HTML
-- and wiki markup in `formatted_for_len`, so we can compute the approximate textual length for use in sizing
-- the toggle box with the "more" button on the right.
local pre = is_first and expressed_style.pre and expressed_style.pre .. " " or ""
local pre_for_len = pre .. (tag and "(" .. tag .. ") " or "")
pre = pre .. (tag and m_qual.format_qualifier(tag) .. " " or "")
local post = is_first and (expressed_style.post and " " .. expressed_style.post or "") or ""
local formatted = bullet .. pre .. m_IPA.format_IPA_full { lang = lang, items = pronunciations, separator = "" } .. post
local formatted_for_len = bullet .. pre .. "IPA(key): " .. table.concat(formatted_pronuns) .. post
return formatted, formatted_for_len
end
for i, style_group in ipairs(expressed_styles) do
if #style_group.styles == 1 then
style_group.formatted, style_group.formatted_for_len =
format_style(style_group.styles[1].tag, style_group.styles[1], i == 1)
else
style_group.formatted, style_group.formatted_for_len =
format_style(style_group.tag, style_group.styles[1], i == 1)
for j, style in ipairs(style_group.styles) do
style.formatted, style.formatted_for_len =
format_style(style.tag, style, i == 1 and j == 1)
end
end
end
-- Remove any HTML from the formatted text, since it doesn't contribute to the textual length, and return the
-- resulting length in characters.
local function textual_len(text)
text = rsub(text, "<.->", "")
return ulen(text)
end
local maxlen = 0
for i, style_group in ipairs(expressed_styles) do
local this_len = textual_len(style_group.formatted_for_len)
if #style_group.styles > 1 then
for _, style in ipairs(style_group.styles) do
this_len = math.max(this_len, textual_len(style.formatted_for_len))
end
end
maxlen = math.max(maxlen, this_len)
end
for i, style_group in ipairs(expressed_styles) do
if #style_group.styles == 1 then
table.insert(lines, "<div>\n" .. style_group.formatted .. "</div>")
else
local inline = '\n<div class="vsShow" style="display:none">\n' .. style_group.formatted .. "</div>"
local full_prons = {}
for _, style in ipairs(style_group.styles) do
table.insert(full_prons, style.formatted)
end
local full = '\n<div class="vsHide">\n' .. table.concat(full_prons, "\n") .. "</div>"
local em_length = math.floor(maxlen * 0.68) -- from [[Module:grc-pronunciation]]
table.insert(lines, '<div class="vsSwitcher" data-toggle-category="pronunciations" style="width: ' .. em_length .. 'em; max-width:100%;"><span class="vsToggleElement" style="float: right;"> </span>' .. inline .. full .. "</div>")
end
end
-- major hack to get bullets working on the next line
return table.concat(lines, "\n") .. "\n<span></span>"
end
return export
47lzuc4gudylslxt60ec5e9fgziqihl
Module:labels/data/lang/pt
828
36250
178617
170852
2026-11-09T03:44:31Z
GinawaSaHapon
3989
178617
Scribunto
text/plain
local labels = {}
------------------------------------ Africa ------------------------------------
labels["Aprika"] = {
aliases = {"African", "Africa"},
Wikipedia = true,
regional_categories = "African",
parent = true,
}
labels["Angola"] = {
aliases = {"Angolan", "AO", "ao"},
Wikipedia = "Angolan Portuguese",
regional_categories = "Angolan",
parent = "Africa",
}
labels["Cabo Verde"] = {
aliases = {"Cape Verdean", "Verdean", "Cape Verde", "Cabo Verdean", "CV", "cv"},
aliases = {"Cape Verdean"},
Wikipedia = "Cape Verdean Portuguese",
regional_categories = "Cape Verdean",
parent = "Africa",
}
labels["Ginea-Bissau"] = {
aliases = {"Guinean", "Guinea-Bissauan", "GW", "gw", "Guinea-Bissau"},
Wikipedia = "Guinean Portuguese",
regional_categories = true,
parent = "Africa",
}
labels["Mozambique"] = {
aliases = {"Mozambican", "mz", "moz"},
Wikipedia = "Mozambican Portuguese",
regional_categories = "Mozambican",
parent = "Africa",
}
labels["São Tomé at Príncipe"] = {
aliases = {"Santomean", "São Tomé", "São Toméan", "Sao Tomean", "Sao Tome and Principe", "São Tomé and Príncipe", "Sao Tome", "ST", "st"},
Wikipedia = "São Tomean Portuguese",
regional_categories = "Santomean",
parent = "Africa",
}
labels["Timog Aprika"] = {
aliases = {"South African", "ZA", "za", "South Africa"},
Wikipedia = true,
regional_categories = "South African",
parent = "Africa",
}
------------------------------------ Asia ------------------------------------
labels["Asya"] = {
aliases = {"Asian", "Asia"},
Wikipedia = true,
regional_categories = "Asian",
parent = true,
}
labels["Goa"] = {
aliases = {"Goan"},
Wikipedia = "Goan Portuguese",
regional_categories = "Goan",
parent = "India",
}
labels["India"] = {
aliases = {"Indian"},
Wikipedia = {"Goan Portuguese", "Portuguese India"},
regional_categories = "Indian",
parent = "Asia",
}
labels["Macau"] = {
aliases = {"Macao", "Macanese", "MO", "mo"},
Wikipedia = "Macau Portuguese",
regional_categories = "Macanese",
parent = "Asia",
}
labels["Timor-Leste"] = {
aliases = {"Timor Leste", "Timor", "East Timor", "TL", "tl", "Timorese"},
Wikipedia = "Timorese Portuguese",
regional_categories = true,
parent = "Asia",
}
------------------------------------ Brazil ------------------------------------
labels["Brasil"] = {
aliases = {"Brazilian", "BR", "br", "Brazil"},
Wikipedia = "Brazilian Portuguese",
regional_categories = "Brazilian",
parent = true,
}
labels["Alagoas"] = {
region = "[[Alagoas]], a state of [[Brazil]]",
aliases = {"Alagoano", "Alagoan", "BR-AL", "AL"},
Wikipedia = true,
regional_categories = "Alagoan",
parent = "Northeast Brazil",
}
labels["Amazonas"] = {
region = "[[Amazonas]], a state of [[Brazil]]",
aliases = {"Amazonense", "BR-AM", "AM"},
Wikipedia = "Amazonas (Brazilian state)",
regional_categories = "Amazonense",
parent = "North Brazil",
}
labels["Bahia"] = {
region = "[[Bahia]], a state of [[Brazil]]",
aliases = {"Baiano", "Bahian", "BR-BA", "BA"},
Wikipedia = true,
regional_categories = "Bahian",
parent = "Northeast Brazil",
}
labels["Caipira"] = {
aliases = {"caipira"},
Wikipedia = "Caipira dialect",
regional_categories = true,
region = "{{w|Paulistania}}",
parent = "Southeast Brazil",
}
labels["Ceará"] = {
region = "[[Ceará]], a state of [[Brazil]]",
aliases = {"Cearense", "BR-CE", "CE"},
Wikipedia = true,
regional_categories = "Cearense",
parent = "Northeast Brazil",
}
labels["Gitnang Kanlurang Brasil"] = {
region = "the {{w|Central-West Region, Brazil}}",
aliases = {"Centro-Oestino", "Centro-Oeste", "Central-Western Brazilian", "Central-West Brazilian", "Central-West Brazil"},
Wikipedia = "Central-West Region, Brazil",
regional_categories = "Central-Western Brazilian",
parent = "Brazil",
}
labels["Goiás"] = {
region = "[[Goiás]], a state of [[Brazil]]",
aliases = {"Goiano", "BR-GO", "GO"},
Wikipedia = true,
regional_categories = "Goiano",
parent = "Central-West Brazil",
}
labels["Maranhão"] = {
region = "[[Maranhão]], a state of [[Brazil]]",
aliases = {"Maranhense", "BR-MA", "MA"},
Wikipedia = true,
regional_categories = "Maranhense",
parent = "Northeast Brazil",
}
labels["Mato Grosso"] = {
region = "[[Mato Grosso]] and [[Mato Grosso do Sul]], two adjacent states of [[Brazil]]",
aliases = {"Mato-Grossense", "BR-MT", "MT"},
Wikipedia = true,
regional_categories = "Mato-Grossense",
parent = "Central-West Brazil",
}
labels["Minas Gerais"] = {
region = "[[Minas Gerais]], a state of [[Brazil]]",
aliases = {"Mineiro", "BR-MG", "MG"},
Wikipedia = true,
regional_categories = "Mineiro",
parent = "Southeast Brazil",
}
-- Norte: See also "Northern Portugal"
labels["Hilagang Brasil"] = {
aliases = {"Nortista", "Norteiro", "Northern Brazil", "North Brazilian", "Northern Brazilian", "Amazon", "Amazonian", "North Brazil"},
Wikipedia = "North Region, Brazil",
regional_categories = "Northern Brazilian",
parent = "Brazil",
}
labels["Hilagang-silangang Brasil"] = {
aliases = {"Nordestino", "Nordeste", "Northeastern Brazilian", "Northeast Brazilian", "Northeastern Brazil", "Northeast Brazil"},
Wikipedia = "Northeast Region, Brazil",
regional_categories = "Northeastern Brazilian",
parent = "Brazil",
}
labels["Pará"] = {
region = "[[Pará]], a state of [[Brazil]]",
aliases = {"Paraense", "BR-PA", "PA"},
Wikipedia = true,
regional_categories = "Paraense",
parent = "North Brazil",
}
labels["Paraná"] = {
region = "[[Paraná]], a state of [[Brazil]]",
aliases = {"Paranaense", "BR-PR", "PR"},
Wikipedia = "Paraná (state)",
regional_categories = "Paranaense",
parent = "South Brazil",
}
labels["Pernambuco"] = {
region = "[[Pernambuco]], a state of [[Brazil]]",
aliases = {"Pernambucano", "Pernambucan", "BR-PE", "PE"},
Wikipedia = true,
regional_categories = "Pernambucan",
parent = "Northeast Brazil",
}
labels["Piauí"] = {
region = "[[Piauí]], a state of [[Brazil]]",
aliases = {"Piauiense", "BR-PI", "PI"},
Wikipedia = true,
regional_categories = "Piauiense",
parent = "Northeast Brazil",
}
labels["Rio Grande do Norte"] = {
region = "[[Rio Grande do Norte]], a state of [[Brazil]]",
aliases = {"Potiguar", "Norte-Rio-Grandense", "BR-RN", "RN"},
Wikipedia = true,
regional_categories = "Potiguar",
parent = "Northeast Brazil",
}
labels["Rio Grande do Sul"] = {
region = "[[Rio Grande do Sul]], a state of [[Brazil]]",
aliases = {"Gaúcho", "Gaucho", "BR-RS", "RS"},
Wikipedia = true,
regional_categories = "Gaúcho",
parent = "South Brazil",
}
labels["Rio de Janeiro"] = {
region = "[[Rio de Janeiro]], a city and surrounding state of [[Brazil]]",
aliases = {"Fluminense", "Carioca"},
Wikipedia = {"Carioca#Sociolect", true},
regional_categories = "Carioca",
parent = "Southeast Brazil",
}
labels["Santa Catarina"] = {
region = "[[Santa Catarina]], a state of [[Brazil]]",
aliases = {"Catarinense", "BR-SC", "SC"},
Wikipedia = "Santa Catarina (state)",
regional_categories = "Catarinense",
parent = "South Brazil",
}
labels["São Paulo"] = {
region = "[[São Paulo]], a city and surrounding state of [[Brazil]]",
aliases = {"Sao Paulo", "Paulista", "BR-SP", "SP"},
Wikipedia = {"Paulistano dialect", "São Paulo (state)"},
regional_categories = "Paulista",
parent = "Southeast Brazil",
}
labels["Sergipe"] = {
region = "[[Sergipe]], a state of [[Brazil]]",
aliases = {"Sergipano", "Sergipan", "BR-SE", "SE"},
Wikipedia = true,
regional_categories = "Sergipan",
parent = "Northeast Brazil",
}
labels["Timog Brasil"] = {
aliases = {"Southern Brazilian", "South Brazilian", "South Brazil"},
Wikipedia = "South Region, Brazil",
regional_categories = "Southern Brazilian",
parent = "Brazil",
}
labels["Timog-silangang Brasil"] = {
aliases = {"Sudestino", "Sudeste", "Southeastern Brazilian", "Southeast Brazilian", "Southeast Brazil"},
Wikipedia = "Southeast Region, Brazil",
regional_categories = "Southeastern Brazilian",
parent = "Brazil",
}
------------------------------------ Portugal ------------------------------------
labels["Portugal"] = {
aliases = {"Portuguese", "European", "Europe", "PT", "pt"},
Wikipedia = "European Portuguese",
regional_categories = "European",
parent = true,
}
labels["Alentejo"] = {
aliases = {"Alentejan", "alent"},
region = "[[Alentejo]], a {{w|NUTS statistical regions of Portugal|region}} of [[Portugal]]",
Wikipedia = "Alentejan Portuguese",
regional_categories = "Alentejano",
parent = "Portugal",
}
labels["Algarve"] = {
aliases = {"Algarvio", "alg"},
region = "[[Algarve]], a {{w|NUTS statistical regions of Portugal|region}} of [[Portugal]]",
regional_categories = "Algarvian",
parent = "Portugal",
}
labels["Azores"] = {
the = true,
aliases = {"Azorean", "Azorian", "azo"},
Wikipedia = true,
regional_categories = "Azorean",
parent = "Portugal",
}
labels["Beira"] = {
region = "[[Beira]], a {{w|Provinces of Portugal|traditional province}} of [[Portugal]]",
Wikipedia = "Beira (Portugal)",
regional_categories = "Beirão",
parent = "Central Portugal",
}
labels["Gitnang Portugal"] = {
aliases = {"Região do Centro", "Centro", "Central Portugal"},
Wikipedia = "Central Region, Portugal",
regional_categories = true,
parent = "Portugal",
}
labels["Madeira"] = {
aliases = {"Madeiran", "mad"},
Wikipedia = true,
regional_categories = "Madeiran",
parent = "Portugal",
}
labels["Minho"] = {
region = "[[Minho]], a {{w|Provinces of Portugal|traditional province}} of [[Portugal]]",
aliases = {"Minhoto", "Alto Minho"},
Wikipedia = "Minho Province",
regional_categories = "Minhoto",
parent = "Northern Portugal",
}
-- Norte: See also "North Brazil"
labels["Hilagang Portugal"] = {
aliases = {"Nortenho", "North Portugal", "Northern Portugal"},
Wikipedia = "Northern Portuguese",
regional_categories = true,
parent = "Portugal",
}
labels["Porto"] = {
region = "[[Porto]], a city and {{w|Porto District|district}} of [[Portugal]]",
aliases = {"Oporto", "Portuense"},
Wikipedia = true,
regional_categories = "Porto",
parent = "Portugal",
}
labels["Trás-os-Montes"] = {
Wikipedia = true,
regional_categories = "Transmontane",
parent = "Northern Portugal",
}
------------------------------------ Rest of the World ------------------------------------
labels["Uruguay"] = {
Wikipedia = "Uruguayan Portuguese",
regional_categories = "Uruguayan",
parent = true,
}
labels["Estados Unidos"] = {
region = "the [[United States]]",
-- America/American: should these be aliases of 'North America'?
aliases = {"U.S.", "United States", "United States of America", "USA", "America", "American", "US"},
Wikipedia = {"Portuguese Americans", "Brazilian Americans"},
regional_categories = "American",
parent = true,
}
-- Variants
labels["Pajubá"] = {
fulldef = "Brazilian [[cryptolect]] spoken by practitioners of Afro-Brazilian religions and the LGBT community",
noreg = true,
aliases = {"pajubá", "pajuba",},
Wikipedia = true,
plain_categories = "Pajubá",
parent = true,
othercat = {"Portuguese LGBTQ slang", "Portuguese cant",},
}
-- Standardization labels
labels["bago pamantayan"] = {
aliases = {"pre-1911", "pre-1943", "pre-1911/43", "pre-1911/1943", "pre-standardization"},
display = "pre-standardization spelling",
}
labels["1931"] = {
display = "1931-prescribed spelling",
}
labels["bago reporma"] = {
aliases = {"pre-1945", "pre-1971", "pre-1973", "pre-reform"},
display = "pre-reform spelling",
}
labels["bago 1990"] = {
aliases = {"pre-1990 spelling", "pre-AO90", "pre-90", "AO45", "pre-1990"},
display = "pre-1990 spelling",
Wikipedia = "Portuguese Language Orthographic Agreement of 1990",
}
-- Spelling labels
-- also used for [[Template:standard spelling of]] et al
labels["Pagbaybay ng Portuges ng Brasil"] = {
aliases = {"Brazilian Portuguese form", "Brazilian spelling", "Brazilian form", "Brazilian Portuguese spelling"},
display = "Brazilian Portuguese spelling",
Wikipedia = "Portuguese orthography#Brazilian vs. European spelling",
plain_categories = "Brazilian Portuguese forms",
}
-- kludge, needed here for [[Template:pt-verb form of]] et al
labels["Anyo ng pandiwa ng Portuges ng Brasil"] = {
aliases = {"Brazilian verb form", "Brazilian Portuguese verb form"},
display = "Brazilian Portuguese spelling",
Wikipedia = "Portuguese orthography#Brazilian vs. European spelling",
plain_categories = "Brazilian Portuguese verb forms",
}
-- also used for [[Template:standard spelling of]] et al
labels["Pagbaybay ng Portuges ng Portugal"] = {
aliases = {"European Portuguese form", "European spelling", "European form", "Portuguese spelling", "Portuguese form", "European Portuguese spelling"},
display = "European Portuguese spelling",
Wikipedia = "Portuguese orthography#Brazilian vs. European spelling",
plain_categories = "European Portuguese forms",
}
-- kludge, needed here for [[Template:pt-verb form of]] et al
labels["Anyo ng pandiwa ng Portuges ng Portugal"] = {
aliases = {"European verb form", "Portuguese verb form", "European Portuguese verb form"},
display = "European Portuguese spelling",
Wikipedia = "Portuguese orthography#Brazilian vs. European spelling",
plain_categories = "European Portuguese verb forms",
}
return require("Module:labels").finalize_data(labels)
rrikj6b9dhp3tfcn89c3b33r3hec74h
Module:lt-common
828
37222
178630
173842
2026-11-09T09:52:37Z
Hiyuune
5522
178630
Scribunto
text/plain
local export = {}
local m_str_utils = require("Module:string utilities")
local put = require("Module:parse utilities")
local u = m_str_utils.char
local ugsub = m_str_utils.gsub
local ulower = m_str_utils.lower
local uupper = m_str_utils.upper
local ufind = m_str_utils.find
local ulen = m_str_utils.len
local ucodepoint = m_str_utils.codepoint
-- Module:string utilities has no normalization functions
local toNFC = mw.ustring.toNFC
local toNFD = mw.ustring.toNFD
-- =============================================================================
-- Unicode constants
-- =============================================================================
local GRAVE = u(0x0300) -- combining grave accent
local ACUTE = u(0x0301) -- combining acute accent
local TILDE = u(0x0303) -- combining tilde
local MACRON = u(0x0304) -- combining macron
local DOTABOVE = u(0x0307) -- combining dot above
local CARON = u(0x030C) -- combining caron
local OGONEK = u(0x0328) -- combining ogonek
local ANY_ACCENT = "[" .. GRAVE .. ACUTE .. TILDE .. "]"
-- Lowercase aliases used by the display and sort-key functions
local grave = GRAVE
local acute = ACUTE
local tilde = TILDE
local macron = MACRON
local dotabove = DOTABOVE
local caron = CARON
local ogonek = OGONEK
local accents = ANY_ACCENT
-- Accent constants shared with the other Lithuanian modules
export.GRAVE = GRAVE
export.ACUTE = ACUTE
export.TILDE = TILDE
export.MACRON = MACRON
export.DOTABOVE = DOTABOVE
export.CARON = CARON
export.OGONEK = OGONEK
export.ANY_ACCENT = ANY_ACCENT
-- =============================================================================
-- Private Use Area (PUA) replacement hints
-- =============================================================================
-- PUA codepoints (U+E000–U+E022) used by old Lithuanian font encodings for
-- accented letters, mapped to their standard Unicode spelling for the error
-- messages of reject_pua.
local INVALID_CHARS = {
[0xE000] = "Ą́", [0xE001] = "ą́", [0xE002] = "Ą̃", [0xE003] = "ą̃",
[0xE004] = "Ę́", [0xE005] = "ę́", [0xE006] = "Ę̃", [0xE007] = "ę̃",
[0xE008] = "Ė́", [0xE009] = "ė́", [0xE00A] = "Ė̃", [0xE00B] = "ė̃",
[0xE00C] = "i̇̀", [0xE00D] = "i̇́", [0xE00E] = "i̇̃",
[0xE00F] = "Į̇́", [0xE010] = "į̇́", [0xE011] = "Į̇̃", [0xE012] = "į̇̃",
[0xE013] = "J̃", [0xE014] = "j̇̃",
[0xE015] = "L̃", [0xE016] = "l̃",
[0xE017] = "M̃", [0xE018] = "m̃",
[0xE019] = "R̃", [0xE01A] = "r̃",
[0xE01B] = "Ų́", [0xE01C] = "ų́", [0xE01D] = "Ų̃", [0xE01E] = "ų̃",
[0xE01F] = "Ū́", [0xE020] = "ū́", [0xE021] = "Ū̃", [0xE022] = "ū̃",
}
export.INVALID_CHARS = INVALID_CHARS
-- =============================================================================
-- Internal helper functions
-- =============================================================================
local dotless_to_dotted = {
["ı"] = "i",
["ȷ"] = "j",
}
local function char_to_dotted_form(base, below)
return (dotless_to_dotted[base] or base) .. below
end
local function normalize_dotted_chars(text)
-- Remove any dots above, and convert dotless forms to dotted.
-- On entry, text must be in NFD form.
return ugsub(text, "([iıjȷ])(" .. ogonek .. "?)" .. dotabove, char_to_dotted_form)
end
local function char_to_accent_form(base, below)
-- Add a 'dot above' after the base.
if base == "i" or base == "j" then
return base .. below .. dotabove
end
-- Convert any dotless chars combining with accents to the dotted form,
-- so that they normalize properly. This shouldn't happen, but just in case.
return char_to_dotted_form(base, below)
end
local function stripped_text_form(text)
-- Remove accents.
text = ugsub(toNFD(text), accents .. "+", "")
-- Normalize dotless characters and dot-above diacritics.
return normalize_dotted_chars(text)
end
-- =============================================================================
-- Input validation
-- =============================================================================
-- Reject Private Use Area characters (U+E000–U+F8FF). When the character is
-- a known non-standard Lithuanian glyph, the error message includes the
-- recommended standard Unicode replacement (see INVALID_CHARS above).
function export.reject_pua(s)
if not s then return end
for i = 1, ulen(s) do
local cp = ucodepoint(s, i)
if cp >= 0xE000 and cp <= 0xF8FF then
local replacement = INVALID_CHARS[cp]
if replacement then
error(string.format(
"lt-common: private use area character U+%04X \"%s\" detected. " ..
"Please use \"%s\" instead.", cp, u(cp), replacement))
else
error(string.format(
"lt-common: private use area character U+%04X detected in \"%s\". " ..
"Please use a standard Unicode character instead.", cp, s))
end
end
end
end
-- =============================================================================
-- Input normalization
-- =============================================================================
-- Detect nonstandard encoding patterns in the input.
-- Returns: dotless_flag (found ı/ȷ), precomp_i_flag (found precomposed í/ì/ĩ)
function export.detect_nonstandard(s)
if not s then return false, false end
local nfd_s = toNFD(s)
local dotless_flag = ufind(nfd_s, "[ıȷ]") ~= nil
local precomp_i_flag = ufind(nfd_s, "[íìĩ]") ~= nil
return dotless_flag, precomp_i_flag
end
-- Normalize input to clean canonical NFC.
-- Handles dotless i/j (ı, ȷ) and stray dot-above combinations.
function export.canonicalize_input(s)
if not s then return s end
s = toNFD(s)
-- Remove stray dot-above after i/j (with or without ogonek)
s = ugsub(s, "([iıjȷ])(" .. OGONEK .. "?)" .. DOTABOVE, function(base, below)
base = (base == "ı") and "i" or (base == "ȷ") and "j" or base
return base .. below
end)
-- Convert any remaining dotless i/j to standard forms
s = ugsub(s, "ı", "i")
s = ugsub(s, "ȷ", "j")
return toNFC(s)
end
-- =============================================================================
-- Partial NFD conversion (stem_ac representation)
-- =============================================================================
-- Convert canonical NFC to partial NFD (stem_ac).
-- Applies full NFD, then recomposes non-accent diacritics.
-- Only grave/acute/tilde remain as combining characters.
function export.to_stem_ac(s)
if not s then return s end
s = toNFD(s)
-- Recompose ogonek vowels
s = ugsub(s, "a" .. OGONEK, "ą")
s = ugsub(s, "e" .. OGONEK, "ę")
s = ugsub(s, "i" .. OGONEK, "į")
s = ugsub(s, "u" .. OGONEK, "ų")
-- Recompose macron vowel
s = ugsub(s, "u" .. MACRON, "ū")
-- Recompose dot-above e
s = ugsub(s, "e" .. DOTABOVE, "ė")
-- Recompose caron consonants
s = ugsub(s, "c" .. CARON, "č")
s = ugsub(s, "s" .. CARON, "š")
s = ugsub(s, "z" .. CARON, "ž")
return s
end
-- =============================================================================
-- Accent manipulation
-- =============================================================================
-- Strip all accent marks (grave/acute/tilde) from partial NFD text.
function export.to_stem_bare(stem_ac)
if not stem_ac then return stem_ac end
return ugsub(stem_ac, ANY_ACCENT, "")
end
-- Check if partial NFD text contains any accent marks.
function export.has_accent(stem_ac)
return ufind(stem_ac, ANY_ACCENT) ~= nil
end
-- =============================================================================
-- Syllables
-- =============================================================================
local SYLLABLE_VOWELS = {
a = true, e = true, i = true, o = true, u = true, y = true,
["ą"] = true, ["ę"] = true, ["ė"] = true, ["į"] = true, ["ų"] = true, ["ū"] = true,
}
-- Vowel pairs that form one syllable; a vowel after i is in the same
-- syllable as the i (softening i, the diphthong ie).
local DIPHTHONGS = {
ai = true, au = true, ei = true, ui = true, ie = true, uo = true,
ou = true, eu = true, oi = true,
}
-- Syllable count of a word or stem and the 1-based index of the syllable
-- that carries an accent mark (nil without one). Accepts NFC, partial NFD
-- and full NFD text: combining marks are skipped, so ą, ė, ū and č count as
-- their base letters; an accent mark on l, m, n or r (a mixed diphthong)
-- belongs to the syllable of the vowel before it.
function export.stressed_syllable(text)
local count, stressed, prev = 0, nil, nil
for ch in mw.ustring.gmatch(text, ".") do
local cp = ucodepoint(ch)
if cp == 0x0300 or cp == 0x0301 or cp == 0x0303 then
stressed = math.max(count, 1)
elseif cp >= 0x0300 and cp <= 0x036F then
-- another combining mark: part of the letter before it
else
local low = ulower(ch)
if SYLLABLE_VOWELS[low] then
if prev == nil or (prev ~= "i" and not DIPHTHONGS[prev .. low]) then
count = count + 1
end
prev = low
else
prev = nil
end
end
end
return count, stressed
end
-- =============================================================================
-- Complete input pipeline
-- =============================================================================
-- Process raw user input through the complete normalization pipeline.
-- Returns: stem_bare, stem_ac, dotless_flag, precomp_flag
function export.process_input(raw)
if not raw then return raw, raw, false, false end
export.reject_pua(raw)
local dotless_flag, precomp_flag = export.detect_nonstandard(raw)
local canon = export.canonicalize_input(raw)
local stem_ac = export.to_stem_ac(canon)
local stem_bare = export.to_stem_bare(stem_ac)
return stem_bare, stem_ac, dotless_flag, precomp_flag
end
-- =============================================================================
-- Display and text processing
-- =============================================================================
function export.makeDisplayText(text, lang, sc)
if not text then return text end
-- Normalize dotless characters and dot-above diacritics (while retaining accents).
text = normalize_dotted_chars(toNFD(text))
-- Add a 'dot above' between "i" or "j" and an accent.
text = ugsub(text, "([iıjȷ])(" .. ogonek .. "?)%f" .. accents, char_to_accent_form)
return toNFC(text)
end
-- Called from [[Module:languages]] since [[Module:lt-common]] is set as the stripDiacritics handler in
-- [[Module:languages/data/2]].
function export.stripDiacritics(text, lang, sc)
if not text then return text end
return toNFC(stripped_text_form(text))
end
local sortkey_substitutes = {
[ogonek] = u(0xF000),
[caron] = u(0xF001),
[macron] = u(0xF002),
[dotabove] = u(0xF003),
["y"] = "i" .. u(0xF004),
}
function export.makeSortKey(text, lang, sc)
if not text then return text end
-- Normalize to the stripped-text form and convert diacritics to Private Use
-- Area characters so they sort after all other characters.
text = stripped_text_form(ulower(text))
:gsub(".[\128-\191]*", sortkey_substitutes)
return toNFC(uupper(text))
end
-- =============================================================================
-- Footnote / multi-value spec parsing
-- =============================================================================
--
-- One field of a declension pattern (lemma, AP, genitive, a slot override)
-- may hold colon-separated variants, each followed by optional bracketed
-- footnotes: `VAL1[note1]:VAL2[note2]`. The caller first isolates the `[...]`
-- runs with `put.parse_balanced_segment_run(text, "[", "]")`, so that a "." or
-- ":" inside a footnote is not taken for a delimiter, and only then splits.
-- Given one delimiter-separated run (alternating plain text and "[...]" runs,
-- as returned by parse_balanced_segment_run / split_alternating_runs), return
-- its footnotes with their brackets (e.g. "[archaic]"), or nil if there are
-- none. Errors if text follows a footnote ("val[note]x").
function export.fetch_footnotes(separated_group, parse_err)
local footnotes
for j = 2, #separated_group - 1, 2 do
if separated_group[j + 1] ~= "" then
parse_err("Extraneous text after bracketed footnotes: '" .. table.concat(separated_group) .. "'")
end
if not footnotes then
footnotes = {}
end
table.insert(footnotes, separated_group[j])
end
return footnotes
end
-- Given a bracket-isolated run and a sub-delimiter (e.g. ":"), split it into
-- one or more {form=VALUE, footnotes={...}} specs. `spectype` is used only
-- in error messages. With `allow_blank`, a wholly blank run returns nil
-- instead of erroring (for optional fields such as the genitive).
function export.fetch_specs(segments, separator, spectype, allow_blank, parse_err)
local separated_groups = put.split_alternating_runs_and_strip_spaces(segments, separator)
if allow_blank and #separated_groups == 1 and #separated_groups[1] == 1 and
separated_groups[1][1] == "" then
return nil
end
local specs = {}
for _, separated_group in ipairs(separated_groups) do
local form = separated_group[1]
if form == "" then
parse_err("Blank form not allowed here, but saw '" .. table.concat(segments) .. "'")
end
local new_spec = {form = form, footnotes = export.fetch_footnotes(separated_group, parse_err)}
for _, existing_spec in ipairs(specs) do
if existing_spec.form == new_spec.form then
parse_err("Duplicate " .. spectype .. " spec '" .. table.concat(separated_group) .. "'")
end
end
table.insert(specs, new_spec)
end
return specs
end
-- =============================================================================
-- Calls written for a legacy template
-- =============================================================================
-- The legacy versions of the templates ({{lt-noun}}, {{lt-conj}}, …) are kept
-- at "<name>/old". A call written for the legacy template is rendered through
-- it with the same arguments, and the entry is listed at
-- [[Wiktionary:Tracking/<name>/legacy]] (pages outside the main namespace,
-- such as documentation, are not listed).
function export.render_legacy(frame, name)
local args = {}
for k, v in pairs(frame:getParent().args) do
args[k] = v
end
if mw.title.getCurrentTitle().namespace == 0 then
require("Module:debug/track")(name .. "/legacy")
end
return frame:expandTemplate{title = "Template:" .. name .. "/old", args = args}
end
-- True for a call of the legacy {{lt-conj}} (the forms in positional
-- parameters), false for a pattern of the new one ("inf,pres,past…").
function export.is_legacy_conj(args)
if args[2] ~= nil then
return true
end
local first = args[1]
return first ~= nil and not mw.ustring.find(first, "^[^,]*,[^,]*,")
end
-- =============================================================================
-- Headword scraping
-- =============================================================================
-- Module:template parser is loaded on first use.
local function find_templates(...)
find_templates = require("Module:template parser").find_templates
return find_templates(...)
end
-- Scan the wikitext of `pagename` for calls to `{{infltemp}}` (e.g. "lt-ndecl")
-- and return the inflection spec (first positional argument) of the matching call.
-- `infltemp` is one template name or a list of names treated as the same
-- template (e.g. a template and its redirect).
-- If `inflid` is given, match only the call whose id= equals `inflid`.
-- `accept`, if given, is called with the arguments of each call and skips the
-- call when it returns false (e.g. a call written for the legacy template).
-- Returns (infl_string, nil) on success, (nil, error_string) on failure.
function export.find_lt_inflection(pagename, infltemp, inflid, accept)
local wanted_names = {}
if type(infltemp) == "table" then
for _, name in ipairs(infltemp) do wanted_names[name] = true end
infltemp = table.concat(infltemp, "}}/{{") -- display name for messages
else
wanted_names[infltemp] = true
end
local title = mw.title.new(pagename)
if not title then
return nil, ("Bad pagename '%s': couldn't create title object"):format(pagename)
end
local content = title:getContent()
if not content then
return nil, ("Couldn't fetch page content for '[[%s]]'; page may not exist"):format(pagename)
end
local sets_by_id = {} -- id string → infl string
local sets_no_id = {} -- list of infl strings (templates without id=)
for template in find_templates(content) do
local targs = wanted_names[template:get_name()] and template:get_arguments()
if targs and (not accept or accept(targs)) then
local infl = targs[1]
if not infl or infl == "" then
return nil, ("Found {{%s}} on '[[%s]]' but it has no inflection spec in 1="):
format(infltemp, pagename)
end
if targs.id then
if sets_by_id[targs.id] then
return nil, ("Found duplicate id='%s' for {{%s}} on '[[%s]]'"):
format(targs.id, infltemp, pagename)
end
sets_by_id[targs.id] = infl
else
table.insert(sets_no_id, infl)
end
end
end
local has_id = next(sets_by_id) ~= nil
local has_no_id = #sets_no_id > 0
-- Nothing found at all.
if not has_id and not has_no_id then
return nil, ("No {{%s}} found on '[[%s]]'"):format(infltemp, pagename)
end
-- Inconsistent: some calls have id= and some don't.
if has_id and has_no_id then
return nil, ("On '[[%s]]', some {{%s}} calls have id= and some don't; " ..
"all must be consistent"):format(pagename, infltemp)
end
-- Multiple calls, all without id=: caller must add id= params.
if not has_id and #sets_no_id > 1 then
return nil, ("On '[[%s]]', found %d {{%s}} calls without id=; " ..
"add id= to each and specify id= in the headword template to disambiguate"):
format(pagename, #sets_no_id, infltemp)
end
-- From here, either exactly one no-id call, or all calls have id=.
-- Caller specified an id: find the matching call.
if inflid then
if not has_id then
-- Only one no-id call exists; id= mismatch is suspicious but unambiguous — return it.
return sets_no_id[1], nil
end
local match = sets_by_id[inflid]
if match then
return match, nil
end
local available = {}
for k in pairs(sets_by_id) do table.insert(available, "'" .. k .. "'") end
table.sort(available)
return nil, ("On '[[%s]]', no {{%s}} with id='%s' found; available: %s"):
format(pagename, infltemp, inflid, table.concat(available, ", "))
end
-- No id specified by caller.
if not has_id then
-- Exactly one no-id call (multiple case already caught above).
return sets_no_id[1], nil
end
-- All calls have id= but caller didn't specify one.
local keys = {}
for k in pairs(sets_by_id) do table.insert(keys, k) end
table.sort(keys)
if #keys == 1 then
-- Unambiguous single id= call: return it.
return sets_by_id[keys[1]], nil
end
local quoted = {}
for _, k in ipairs(keys) do table.insert(quoted, "'" .. k .. "'") end
return nil, ("On '[[%s]]', found multiple {{%s}} calls with id= (%s); " ..
"specify id= in the headword template to disambiguate"):
format(pagename, infltemp, table.concat(quoted, ", "))
end
return export
hpc4gjkid8oyrvsu24g7a14g4ydt1g2
Padron:R:pt:Infopédia
10
39024
178625
2026-11-09T06:08:40Z
GinawaSaHapon
3989
Nilikha ang pahina na may '{{#invoke:quote|call_quote_template | template = cite-web | pt | start_year = 2003 | year = {{CURRENTYEAR}} | entry = {{{entry|{{{2|{{{1|{{pagename}}}}}}}}}}} | entryurl = https://www.infopedia.pt/dicionarios/<!-- -->{{#switch: {{{dict|}}}<!-- -->| lgp = lingua-gestual<!-- -->| abbreviations = siglas-abreviaturas<!-- -->| toponymy = toponimia<!-- -->| anthroponymy = antroponimia<!-- -->| me...'
178625
wikitext
text/x-wiki
{{#invoke:quote|call_quote_template
| template = cite-web
| pt
| start_year = 2003
| year = {{CURRENTYEAR}}
| entry = {{{entry|{{{2|{{{1|{{pagename}}}}}}}}}}}
| entryurl = https://www.infopedia.pt/dicionarios/<!--
-->{{#switch: {{{dict|}}}<!--
-->| lgp = lingua-gestual<!--
-->| abbreviations = siglas-abreviaturas<!--
-->| toponymy = toponimia<!--
-->| anthroponymy = antroponimia<!--
-->| medicine = terms-medicos<!--
-->| orthography = vocabulario<!--
-->| phrases = locucoes-expressoes<!--
-->| pre-reform = lingua-portuguesa-aao<!--
-->| illustrated = basico-ilustrado<!--
-->| learners = portugues-estrangeiros<!--
-->| #default = lingua-portuguesa<!--
-->}}/{{urlencode:{{{1|{{pagename}}}}}}}<!--
-->{{#if: {{{expression|}}}
| ?express={{urlencode:{{{expression|}}}}}
}}<!--
-->|work=[[w:pt:Infopédia|Dicionário Infopédia <!--
-->{{#switch: {{{dict|}}}<!--
-->| lgp = da Língua Gestual Portuguesa<!--
-->| abbreviations = das Siglas e Abreviaturas<!--
-->| toponymy = da Toponímia<!--
-->| anthroponymy = de Antroponímia<!--
-->| medicine = dos Termos Médicos<!--
-->| orthography = do Vocabulário Ortográfico<!--
-->| phrases = das Locucões Latinas e Expressões Estrangeiras<!--
-->| pre-reform = da Língua Portuguesa sem Acordo<!--
-->| illustrated = Básica Ilustrado<!--
-->| learners = de Português para Estrangeiros<!--
-->| #default = da Língua Portuguesa<!--
-->}}]]
| location = Porto
| publisher = Porto Editora
| allowparams = 1, 2, entry, subentry, dict, expression
| propagateparams = archiveurl, archivedate, accessdate, passage, text, asquote, brackets, nocat, subst, usenodot
}}<!--
--><noinclude>{{documentation}}</noinclude>
3ve1ttfbku397q5i8wu9lpm6rcmog8s
livro
0
39025
178627
2026-11-09T06:13:50Z
GinawaSaHapon
3989
Nilikha ang pahina na may '=={{=pt=}}== ===Pagbigkas=== {{pt-IPA}} * {{IPA|pt|[ˈli.vɾʷ]|a=Gitnang Brasil, panlalawigan}} * {{audio|pt|pt-br-livro.ogg|a=Brazil}} * {{audio|pt|LL-Q5146 (por)-Santamarcanda-livro.wav|a=<<Portugal>> (<<Porto>>)}} * {{rhymes|pt|ivɾu|s=2}} * {{hyph|pt|li|vro}} ===Etimolohiya 1=== Mula {{inh|pt|roa-opt|livro}}, isang maagang hiram ng {{der|pt|la|liber|librum}}. Kapatid ng {{cog|gl,es,it|libro}}, {{cog|ca|llibre}}, {{cog|oc|libre}}, at {{cog|fr|livre}}. ====...'
178627
wikitext
text/x-wiki
=={{=pt=}}==
===Pagbigkas===
{{pt-IPA}}
* {{IPA|pt|[ˈli.vɾʷ]|a=Gitnang Brasil, panlalawigan}}
* {{audio|pt|pt-br-livro.ogg|a=Brazil}}
* {{audio|pt|LL-Q5146 (por)-Santamarcanda-livro.wav|a=<<Portugal>> (<<Porto>>)}}
* {{rhymes|pt|ivɾu|s=2}}
* {{hyph|pt|li|vro}}
===Etimolohiya 1===
Mula {{inh|pt|roa-opt|livro}}, isang maagang hiram ng {{der|pt|la|liber|librum}}. Kapatid ng {{cog|gl,es,it|libro}}, {{cog|ca|llibre}}, {{cog|oc|libre}}, at {{cog|fr|livre}}.
====Pangngalan====
{{pt-noun|m|dim=livrinho|aug=livrão}}
# [[aklat]]
# {{lb|pt|anatomy}} {{l|tl|libro|id=anatomiya}} {{gl|ikatlong bahagi ng sikmura ng mga ruminante}}
#: {{syn|pt|folhoso}}
=====Hinangong kataga=====
{{col|pt
|livrão
|livraria
|livreco
|livreiro
|livrete
|livrinho
|livro branco
|livro de bolso
|livro de bordo
|livro de cheques
|livro de frases
|livro de fumos
|livro de honra
|livro de horas
|Livro de Mórmon
|livro de ouro
|livro de ponto
|livro de reclamações
|livro didático
|livro digital
|livro eletrônico
|livro-texto
|marca-livro
|ser um livro aberto
}}
=====Kaugnay na kataga=====
{{col|pt|líber|livreto}}
=====Anak na kataga=====
* {{desc|kea|libru}}
* {{desc|mzs|livro}}
* {{desc|tet|livru|bor=1}}
===Etimolohiya 2===
====Pandiwa====
{{head|pt|verb form}}
# {{pt-verb form of|livrar}}
===Magbasa pa===
* {{R:pt:Aulete}}
* {{R:pt:Infopédia}}
* {{R:pt:Priberam}}
lxewmckuclca3s7ofqijkf8yd2f8q3o
178631
178627
2026-11-09T09:54:16Z
Hiyuune
5522
/* {{=pt=}} */
178631
wikitext
text/x-wiki
=={{=pt=}}==
{{wp|pt:}}
===Pagbigkas===
{{pt-IPA}}
* {{IPA|pt|[ˈli.vɾʷ]|a=Gitnang Brasil, panlalawigan}}
* {{audio|pt|pt-br-livro.ogg|a=Brazil}}
* {{audio|pt|LL-Q5146 (por)-Santamarcanda-livro.wav|a=<<Portugal>> (<<Porto>>)}}
* {{rhymes|pt|ivɾu|s=2}}
* {{hyph|pt|li|vro}}
===Etimolohiya 1===
Mula {{inh|pt|roa-opt|livro}}, isang maagang hiram ng {{der|pt|la|liber|librum}}. Kapatid ng {{cog|gl,es,it|libro}}, {{cog|ca|llibre}}, {{cog|oc|libre}}, at {{cog|fr|livre}}.
====Pangngalan====
{{pt-noun|m|dim=livrinho|aug=livrão}}
# [[aklat]]
# {{lb|pt|anatomy}} {{l|tl|libro|id=anatomiya}} {{gl|ikatlong bahagi ng sikmura ng mga ruminante}}
#: {{syn|pt|folhoso}}
=====Hinangong kataga=====
{{col|pt
|livrão
|livraria
|livreco
|livreiro
|livrete
|livrinho
|livro branco
|livro de bolso
|livro de bordo
|livro de cheques
|livro de frases
|livro de fumos
|livro de honra
|livro de horas
|Livro de Mórmon
|livro de ouro
|livro de ponto
|livro de reclamações
|livro didático
|livro digital
|livro eletrônico
|livro-texto
|marca-livro
|ser um livro aberto
}}
=====Kaugnay na kataga=====
{{col|pt|líber|livreto}}
=====Anak na kataga=====
* {{desc|kea|libru}}
* {{desc|mzs|livro}}
* {{desc|tet|livru|bor=1}}
===Etimolohiya 2===
====Pandiwa====
{{head|pt|verb form}}
# {{pt-verb form of|livrar}}
===Magbasa pa===
* {{R:pt:Aulete}}
* {{R:pt:Infopédia}}
* {{R:pt:Priberam}}
l7wgz2qu5nmeqlnas1zjpjrtkmtjsrz
libros
0
39026
178629
2026-11-09T06:21:39Z
GinawaSaHapon
3989
Nilikha ang pahina na may '=={{=es=}}== ===Pagbigkas=== {{es-pr}} ===Pangngalan=== {{head|es|noun form|g=m-p}} # {{plural of|es|libro}}'
178629
wikitext
text/x-wiki
=={{=es=}}==
===Pagbigkas===
{{es-pr}}
===Pangngalan===
{{head|es|noun form|g=m-p}}
# {{plural of|es|libro}}
jc8tdj922v536uj0ov0oxrqm82j7xf3