ویکیفەرھەنگ
ckbwiktionary
https://ckb.wiktionary.org/wiki/%D9%88%DB%8C%DA%A9%DB%8C%D9%81%DB%95%D8%B1%DA%BE%DB%95%D9%86%DA%AF:%D8%AF%DB%95%D8%B3%D8%AA%D9%BE%DB%8E%DA%A9
MediaWiki 1.47.0-wmf.15
case-sensitive
میدیا
تایبەت
وتووێژ
بەکارھێنەر
لێدوانی بەکارھێنەر
ویکیفەرھەنگ
لێدوانی ویکیفەرھەنگ
پەڕگە
وتووێژی پەڕگە
میدیاویکی
وتووێژی میدیاویکی
داڕێژە
وتووێژی داڕێژە
یارمەتی
وتووێژی یارمەتی
پۆل
وتووێژی پۆل
TimedText
TimedText talk
مۆدیوول
لێدوانی مۆدیوول
Event
Event talk
مۆدیوول:en-headword
828
29
36881
36793
2026-08-16T23:48:37Z
Ghybu
12
plural
36881
Scribunto
text/plain
local export = {}
local pos_functions = {}
local force_cat = false -- for testing; if true, categories appear in non-mainspace pages
local require = require
local require_when_needed = require("Module:require when needed")
local en_utilities_module = "Module:en-utilities"
local headword_utilities_module = "Module:headword utilities"
local headword_module = "Module:headword"
local inflection_utilities_module = "Module:inflection utilities"
local parse_utilities_module = "Module:parse utilities"
local JSON_module = "Module:JSON"
local links_module = "Module:links"
local parameters_module = "Module:parameters"
local string_utilities_module = "Module:string utilities"
local table_module = "Module:table"
local utilities_module = "Module:utilities"
local iut = require_when_needed(inflection_utilities_module)
local put = require_when_needed(parse_utilities_module)
local add_links_to_multiword_term = require_when_needed(headword_utilities_module, "add_links_to_multiword_term")
local add_suffix = require_when_needed(en_utilities_module, "add_suffix")
local apply_link_modifiers = require_when_needed(headword_utilities_module, "apply_link_modifiers")
local concat = table.concat
local format_categories = require_when_needed(utilities_module, "format_categories")
local full_headword = require_when_needed(headword_module, "full_headword")
local get_link_page = require_when_needed(links_module, "get_link_page")
local insert = table.insert
local is_regular_plural = require_when_needed(en_utilities_module, "is_regular_plural")
local list_to_set = require_when_needed(table_module, "listToSet")
local remove = table.remove
local remove_links = require_when_needed(links_module, "remove_links")
local process_params = require_when_needed(parameters_module, "process")
local singularize = require_when_needed(en_utilities_module, "singularize")
local split = require_when_needed(string_utilities_module, "split")
local toJSON = require_when_needed(JSON_module, "toJSON")
local toNFD = mw.ustring.toNFD
local ulen = require_when_needed(string_utilities_module, "len")
local ulower = require_when_needed(string_utilities_module, "lower")
local umatch = require_when_needed(string_utilities_module, "match")
local lang = require("Module:languages").getByCode("en")
local langname = lang:getCanonicalName()
local function glossary_link(entry, text)
text = text or entry
return "[[Appendix:Glossary#" .. entry .. "|" .. text .. "]]"
end
local function track(page)
require("Module:debug/track")("en-headword/" .. page)
return true
end
------------------------------------------- UTILITY FUNCTIONS ------------------------------------------
-- These functions are used directly in the <> format as well as in the utility functions #2 below.
local function compute_double_last_cons_stem(term)
local last_cons = term:match("([bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ])$")
if not last_cons then
error("Verb stem '" .. term .. "' must end in a consonant to use ++")
end
return term .. last_cons
end
local function compute_plusplus_s_form(term, default_s_form)
if term:find("[sz]$") then
-- regas -> regasses, derez -> derezzes
return compute_double_last_cons_stem(term) .. "es"
else
return default_s_form
end
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 boolean = {type = "boolean"}
local params = {
["head"] = {list = true},
["id"] = true,
["json"] = boolean,
["sort"] = true,
["splithyph"] = boolean,
["nosplithyph"] = boolean,
["hyphspace"] = boolean,
["nolink"] = boolean,
["nolinkhead"] = {type = "boolean", alias_of = "nolink"},
["nosuffix"] = boolean,
["nomultiwordcat"] = boolean,
["pagename"] = true, -- for testing
}
local pos_data = pos_functions[poscat]
if pos_data then
for key, val in pairs(pos_data.params) do
params[key] = val
end
end
local args = process_params(frame:getParent().args, params, nil, "en-headword", "show")
local pagename = args.pagename or mw.loadData("Module:headword/data").pagename -- Accounts for unsupported titles.
local user_specified_heads = args.head
local heads = user_specified_heads
local autohead
if args.nolink or not pagename:find("[ '%-]") then
autohead = pagename
else
local en_no_split_apostrophe_words = list_to_set{
"one's",
"someone's",
"he's",
"she's",
"it's",
}
local en_include_hyphen_prefixes = list_to_set{
-- We don't include things that are also words even though they are often (perhaps mostly) prefixes, e.g.
-- "be", "counter", "cross", "extra", "half", "mid", "over", "pan", "under".
"acro",
"acousto",
"Afro",
"agro",
"anarcho",
"angio",
"Anglo",
"ante",
"anti",
"arch",
"auto",
"bi",
"bio",
"cis",
"co",
"cryo",
"crypto",
"de",
"demi",
"eco",
"electro",
"Euro",
"ex",
"Greco",
"hemi",
"hydro",
"hyper",
"hypo",
"infra",
"Indo",
"inter",
"intra",
"Judeo",
"macro",
"meta",
"micro",
"mini",
"multi",
"neo",
"neuro",
"non",
"para",
"peri",
"post",
"pre",
"pro",
"proto",
"pseudo",
"re",
"semi",
"sub",
"super",
"trans",
"un",
"vice",
}
local function is_english(term)
local title = mw.title.new(term)
if title and title.exists then
local content = title:getContent()
if content and content:find("==English==\n") then
return true
end
end
return false
end
local function en_split_hyphen_when_space(word)
if not word:find("-", nil, true) then
return nil
end
if args.hyphspace then
return "[[" .. word:gsub("%-+", " ") .. "|" .. word .. "]]"
end
if args.nosplithyph then
return "[[" .. word .. "]]"
end
if not args.splithyph then
local space_word = word:gsub("%-+", " ")
if is_english(space_word) then
return "[[" .. space_word .. "|" .. word .. "]]"
end
if is_english(word) then
return "[[" .. word .. "]]"
end
end
return nil
end
local function en_split_apostrophe(word)
local base = word:match("^(.*)'s$")
if base then
return "[[" .. base .. "]][[-'s|'s]]"
end
base = word:match("^(.*)'$")
if base then
if base:find("s$") then
local sg = singularize(base)
if is_english(sg) then
return "[[" .. sg .. "|" .. base .. "]][[-'|']]"
end
end
return "[[" .. base .. "]][[-'|']]"
end
return "[[" .. word .. "]]"
end
autohead = add_links_to_multiword_term(pagename, {
split_hyphen_when_space = en_split_hyphen_when_space,
split_apostrophe = en_split_apostrophe,
no_split_apostrophe_words = en_no_split_apostrophe_words,
include_hyphen_prefixes = en_include_hyphen_prefixes,
})
end
if #heads == 0 then
heads = {autohead}
else
for i, head in ipairs(heads) do
if head:find("^~") then
head = apply_link_modifiers(autohead, head:sub(2))
heads[i] = head
end
if head == autohead then
track("redundant-head")
end
end
end
local data = {
lang = lang,
pos_category = poscat,
categories = {},
heads = heads,
user_specified_heads = user_specified_heads,
no_redundant_head_cat = #user_specified_heads == 0,
inflections = {},
nomultiwordcat = args.nomultiwordcat,
sort_key = args.sort,
pagename = args.pagename,
-- This is always set, and in the case of unsupported titles, it's the displayed version (e.g. 'C|N>K' instead of
-- 'Unsupported titles/C through N to K').
displayed_pagename = pagename,
id = args.id,
force_cat_output = force_cat,
}
local is_suffix = false
if not args.nosuffix and pagename:find("^%-") and not pagename:find("^%-%-") and poscat ~= "suffix forms" then
is_suffix = true
data.pos_category = "suffixes"
local singular_poscat = singularize(poscat)
insert(data.categories, langname .. " " .. singular_poscat .. "-forming suffixes")
insert(data.inflections, {label = singular_poscat .. "-forming suffix"})
end
if pos_data then
pos_data.func(args, data, is_suffix)
end
local extra_categories = {}
if pagename:find("[Qq][^Uu]") or pagename:find("[Qq]$") then
insert(data.categories, langname .. " words containing Q not followed by U")
end
-- toNFD performs decomposition, so letters that decompose to an ASCII
-- vowel and a diacritic, such as é, are counted as vowels anddo not do not
-- need to be included in the pattern.
if not umatch(ulower(toNFD(pagename)), "[aeiouyæœøəªºαεηιουω]") then
insert(data.categories, langname .. " words without vowels")
end
if pagename:find("yre$") then
insert(data.categories, langname .. ' words ending in "-yre"')
end
if not pagename:find(" ") and ulen(pagename) >= 25 then
insert(extra_categories, "Long " .. langname .. ' words')
end
if pagename:find("^[^aeiou ]*a[^aeiou ]*e[^aeiou ]*i[^aeiou ]*o[^aeiou ]*u[^aeiou ]*$") then
insert(data.categories, langname .. ' words that use all vowels in alphabetical order')
end
if args.json then
return toJSON(data)
end
return full_headword(data)
.. (#extra_categories > 0
and format_categories(extra_categories, lang, args.sort)
or "")
end
-- This function does the common work between adjectives and adverbs
local function make_comparatives(params, data)
local comp_parts = {label = glossary_link("comparative"), accel = {form = "comparative"}}
local sup_parts = {label = glossary_link("superlative"), accel = {form = "superlative"}}
local pagename = data.displayed_pagename
if #params == 0 then
insert(params, {"more"})
end
-- Go over each parameter given and create a comparative and superlative
-- form.
for i, val in ipairs(params) do
local comp = val[1]
local comp_qual = val[2]
local sup = val[3]
local sup_qual = val[4]
local comp_part, sup_part
if comp == "more" and pagename ~= "many" and pagename ~= "much" then
comp_part = "more [[" .. pagename .. "]]"
sup_part = sup or "most [[" .. pagename .. "]]"
elseif comp == "further" and pagename ~= "far" then
comp_part = "further [[" .. pagename .. "]]"
sup_part = sup or "furthest [[" .. pagename .. "]]"
elseif comp == "er" then
-- Add the "-er" and "-est" suffixes.
comp_part = add_suffix(pagename, "r")
sup_part = sup or add_suffix(pagename, "st.superlative")
elseif comp == "ier" then
if pagename:sub(-1) ~= "y" then
error("Can't specify 'ier' comparative unless the term ends with 'y'.")
end
comp_part = pagename:gsub("e?y$", "ier")
sup_part = sup or pagename:gsub("e?y$", "iest")
elseif comp == "-" or sup == "-" then
-- Allowing '-' makes it more flexible to not have some forms
if comp ~= "-" then
comp_part = comp
end
if sup ~= "-" then
sup_part = sup
end
else
-- If the full comparative was given, but no superlative, then
-- create it by replacing the ending -er with -est.
if not sup then
if comp:sub(-2) == "er" then
sup = comp:sub(1, -3) .. "est"
else
error("The superlative of \"" .. comp .. "\" cannot be generated automatically. Please provide it with the \"sup" .. (i == 1 and "" or i) .. "=\" parameter.")
end
end
comp_part = comp
sup_part = sup
end
if comp_part then
insert(comp_parts, {term = comp_part, q = {comp_qual}})
end
if sup_part then
insert(sup_parts, {term = sup_part, q = {sup_qual}})
end
end
insert(data.inflections, comp_parts)
insert(data.inflections, sup_parts)
end
local function make_heads_definite(args, data)
if args.def == "~" then
local newheads = {}
for _, head in ipairs(data.heads) do
insert(newheads, head)
insert(newheads, "the " .. head)
end
data.heads = newheads
else
for i, head in ipairs(data.heads) do
data.heads[i] = "the " .. head
end
end
end
local function non_op()
end
pos_functions["ھاوەڵناوەکان"] = {
params = {
[1] = {list = true, allow_holes = true},
["def"] = true,
["the"] = {alias_of = "def"},
["comp_qual"] = {list = "comp\1_qual", allow_holes = true},
["sup"] = {list = true, allow_holes = true},
["sup_qual"] = {list = "sup\1_qual", allow_holes = true},
},
func = function(args, data)
local shift = 0
local is_not_comparable = false
local is_comparative_only = false
if args.def then
make_heads_definite(args, data)
end
-- If the first parameter is ?, then don't show anything, just return.
if args[1][1] == "?" then
return
-- If the first parameter is -, then move all parameters up one position.
elseif args[1][1] == "-" then
shift = 1
is_not_comparable = true
-- If the only argument is +, then remember this and clear parameters
elseif args[1][1] == "+" and args[1].maxindex == 1 then
shift = 1
is_comparative_only = true
end
-- Gather all the comparative and superlative parameters.
local params = {}
for i = 1, args[1].maxindex - shift do
local comp = args[1][i + shift]
local comp_qual = args["comp_qual"][i + shift]
local sup = args["sup"][i]
local sup_qual = args["sup_qual"][i + shift]
if comp or sup then
insert(params, {comp, comp_qual, sup, sup_qual})
end
end
if shift == 1 then
-- If the first parameter is "-" but there are no parameters,
-- then show "not comparable" only and return.
-- If there are parameters, then show "not generally comparable"
-- before the forms.
if #params == 0 then
if is_not_comparable then
insert(data.inflections, {label = "not " .. glossary_link("comparable")})
insert(data.categories, langname .. " uncomparable adjectives")
return
end
if is_comparative_only then
insert(data.inflections, {label = glossary_link("comparative") .. " form only"})
insert(data.categories, langname .. " comparative-only adjectives")
return
end
else
insert(data.inflections, {label = "not generally " .. glossary_link("comparable")})
end
end
-- Process the parameters
make_comparatives(params, data)
end
}
pos_functions["adverbs"] = {
params = {
[1] = {list = true, allow_holes = true},
["comp_qual"] = {list = "comp\1_qual", allow_holes = true},
["sup"] = {list = true, allow_holes = true},
["sup_qual"] = {list = "sup\1_qual", allow_holes = true},
},
func = function(args, data)
local shift = 0
-- If the first parameter is ?, then don't show anything, just return.
if args[1][1] == "?" then
return
-- If the first parameter is -, then move all parameters up one position.
elseif args[1][1] == "-" then
shift = 1
end
-- Gather all the comparative and superlative parameters.
local params = {}
for i = 1, args[1].maxindex - shift do
local comp = args[1][i + shift]
local comp_qual = args["comp_qual"][i + shift]
local sup = args["sup"][i]
local sup_qual = args["sup_qual"][i + shift]
if comp or sup then
insert(params, {comp, comp_qual, sup, sup_qual})
end
end
if shift == 1 then
-- If the first parameter is "-" but there are no parameters,
-- then show "not comparable" only and return. If there are parameters,
-- then show "not generally comparable" before the forms.
if #params == 0 then
insert(data.inflections, {label = "not " .. glossary_link("comparable")})
insert(data.categories, langname .. " uncomparable adverbs")
return
else
insert(data.inflections, {label = "not generally " .. glossary_link("comparable")})
end
end
-- Process the parameters
make_comparatives(params, data)
end
}
pos_functions["conjunctions"] = {
params = {
[1] = { alias_of = "head" },
},
func = non_op,
}
pos_functions["interjections"] = {
params = {
[1] = { alias_of = "head" },
},
func = non_op,
}
local function escape(str)
return (str:gsub("\\([:#])", "\\\\%1")
:gsub("[:#]", "\\%0"))
end
local function canonicalize_plural(pl, pagename, pos)
if pl == "+" then
return escape(add_suffix(pagename, "s.plural", pos))
elseif pl == "++" then
return escape(compute_plusplus_s_form(pagename, add_suffix(pagename, "s.plural", pos)))
elseif pl == "*" then
return escape(pagename)
elseif pl == "ies" then
if pagename:sub(-1) == "y" then
return escape(pagename:gsub("e?y$", pl))
end
error("Can't specify 'ies' plural unless the term ends with 'y'.")
elseif pl == "s" or pl == "es" or pl == "'s" then
return escape(pagename .. pl)
end
end
local function do_nouns(args, data, pos)
local pagename = data.displayed_pagename
pos = pos or "noun"
local function gather_inflections_with_quals(infl_field, qual_field, label)
-- Gather all the plural parameters from the numbered parameters.
local infls = {}
if label then
infls.label = label
end
for i, infl in ipairs(args[infl_field]) do
local qual = args[qual_field][i]
if qual then
insert(infls, {term = infl, q = {qual}})
else
insert(infls, infl)
end
end
return infls
end
if args.def then
make_heads_definite(args, data)
end
local plurals = gather_inflections_with_quals(1, "plqual")
if plurals[1] == "p" then
-- plurale tantum
if #plurals > 1 then
error("With plurale tantum noun, can't specify more than one plural")
end
data.genders = {"p"} -- this should auto-insert the correct 'pluralia tantum' category
if #args.sg > 0 then
insert(data.inflections, {label = "normally plural"})
insert(data.inflections, gather_inflections_with_quals("sg", "sgqual", "singular"))
else
insert(data.inflections, {label = "plural only"})
end
if #args.attr > 0 then
insert(data.inflections, gather_inflections_with_quals("attr", "attrqual", "attributive"))
end
return
end
local need_default_plural = pos == "noun"
if plurals[1] == "-" then
-- Uncountable noun; may occasionally have a plural
remove(plurals, 1) -- Remove the "-"
insert(data.categories, "ناوە نەژماردەنییە " .. langname .. "یەکان")
-- If plural forms were given explicitly, then show "usually"
if #plurals > 0 then
insert(data.inflections, {label = "بەگشتی " .. glossary_link("نەژماردەنی")})
insert(data.categories, "ناوە ژماردەنییە " .. langname .. "یەکان")
else
insert(data.inflections, {label = glossary_link("نەژماردەنی")})
end
need_default_plural = false
elseif plurals[1] == "~" then
-- Mixed countable/uncountable noun, always has a plural
remove(plurals, 1) -- Remove the "~"
insert(data.inflections, {label = glossary_link("ژماردەنی") .. " و " .. glossary_link("نەژماردەنی")})
insert(data.categories, "ناوە نەژماردەنییە " .. langname .. "یەکان")
insert(data.categories, "ناوە ژماردەنییە " .. langname .. "یەکان")
-- If no plural was given, add a default one now
if #plurals == 0 then
plurals[1] = escape(add_suffix(pagename, "s.plural", pos))
end
elseif pos == "proper noun" then
-- For proper nouns, the default is uncountable
insert(data.categories, "ناوە نەژماردەنییە " .. langname .. "یەکان")
else
-- For common nouns, the default is countable, has a plural
insert(data.categories, "ناوە ژماردەنییە " .. langname .. "یەکان")
end
-- Plural is unknown
if plurals[1] == "?" then
remove(plurals, 1) -- Remove the "?"
-- Not desired; see [[Wiktionary:Tea_room/2021/August#"Plural unknown or uncertain"]]
-- insert(data.inflections, {label = "plural unknown or uncertain"})
insert(data.categories, langname .. " nouns with unknown or uncertain plurals")
if #plurals > 0 then
error("Can't specify explicit plurals along with '?' for unknown/uncertain plural")
end
return
end
-- Plural is not attested
if plurals[1] == "!" then
remove(plurals, 1) -- Remove the "!"
insert(data.inflections, {label = "plural not attested"})
insert(data.categories, langname .. " nouns with unattested plurals")
if #plurals > 0 then
error("Can't specify explicit plurals along with '!' for unattested plural")
end
return
end
-- If no plural was given, maybe add a default one, otherwise (when "-" was given) return.
if #plurals == 0 then
if not need_default_plural then
return
end
plurals[1] = escape(add_suffix(pagename, "s.plural", pos))
end
-- There are plural forms to show, so show them.
plurals.label = "کۆ:"
plurals.accel = {form = "p"}
local irregular, indeclinable
for i, pl in ipairs(plurals) do
local pl_type = type(pl)
local pl_term = pl_type == "table" and pl.term or pl
local canon_pl = canonicalize_plural(pl_term, pagename, pos)
if canon_pl then
pl_term = canon_pl
if pl_type == "table" then
pl.term = pl_term
else
plurals[i] = pl_term
end
end
pl_term = get_link_page(pl_term, lang)
if not (pagename:find(" ") or is_regular_plural(pl_term, pagename)) then
irregular = true
if pl_term == pagename then
indeclinable = true
end
end
end
if irregular then
insert(data.categories, langname .. " nouns with irregular plurals")
end
if indeclinable then
insert(data.categories, langname .. " indeclinable nouns")
end
insert(data.inflections, plurals)
end
-- Return the parameters to be used for nouns and proper nouns. Currently the same.
local function get_noun_params(is_proper)
return {
[1] = {list = true, disallow_holes = true},
["def"] = true,
["the"] = {alias_of = "def"},
["pl\1qual"] = {list = true, allow_holes = true},
-- The following four only used for pluralia tantum (1=p)
["sg"] = {list = true, disallow_holes = true},
["sg\1qual"] = {list = true, allow_holes = true},
["attr"] = {list = true, disallow_holes = true},
["attr\1qual"] = {list = true, allow_holes = true},
}
end
pos_functions["ناوەکان"] = {
params = get_noun_params(false),
func = do_nouns,
}
pos_functions["proper nouns"] = {
params = get_noun_params("is proper"),
func = function(args, data) return do_nouns(args, data, "proper noun") end,
}
local function base_default_verb_forms(verb)
return escape(add_suffix(verb, "s.verb")), escape(add_suffix(verb, "ing")), escape(add_suffix(verb, "d"))
end
local function default_verb_forms(verb)
local full_s_form, full_ing_form, full_ed_form = base_default_verb_forms(verb)
if verb:find(" ") then
local first, rest = verb:match("^(.-)( .*)$")
local first_s_form, first_ing_form, first_ed_form = base_default_verb_forms(first)
return full_s_form, full_ing_form, full_ed_form, first_s_form .. rest, first_ing_form .. rest, first_ed_form .. rest
else
return full_s_form, full_ing_form, full_ed_form, nil, nil, nil
end
end
pos_functions["verbs"] = {
params = {
[1] = {list = "pres_3sg", allow_holes = true},
["pres_3sg_qual"] = {list = "pres_3sg\1_qual", allow_holes = true},
[2] = {list = "pres_ptc", allow_holes = true},
["pres_ptc_qual"] = {list = "pres_ptc\1_qual", allow_holes = true},
[3] = {list = "past", allow_holes = true},
["past_qual"] = {list = "past\1_qual", allow_holes = true},
[4] = {list = "past_ptc", allow_holes = true},
["past_ptc_qual"] = {list = "past_ptc\1_qual", allow_holes = true},
["noautolinkverb"] = {type = "boolean"},
},
func = function(args, data)
-- Get parameters
local par1 = args[1][1]
local par2 = args[2][1]
local par3 = args[3][1]
local par4 = args[4][1]
local pres_3sgs, pres_ptcs, pasts, past_ptcs
local pagename = data.displayed_pagename
------------------------------------------- UTILITY FUNCTIONS #2 ------------------------------------------
-- These functions are used in both in the separate-parameter format and in the override params such as past_ptc2=.
local new_default_s, new_default_ing, new_default_ed, split_default_s, split_default_ing, split_default_ed =
default_verb_forms(pagename)
local function compute_double_last_cons_stem_of_split_verb(verb, ending)
local first, rest = verb:match("^(.-)( .*)$")
if not first then
error("Verb '" .. verb .. "' must have a space in it to use ++*")
end
local last_cons = first:match("([bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ])$")
if not last_cons then
error("First word '" .. first .. "' must end in a consonant to use ++*")
end
return first .. last_cons .. ending .. rest
end
local function check_non_nil_star_form(form)
if form == nil then
error("Verb '" .. pagename .. "' must have a space in it to use * or ++*")
end
return form
end
local function sub_tilde(form)
if not form then
return nil
end
local retval = form:gsub("~", pagename) -- discard second return value
return retval
end
local function canonicalize_s_form(form)
if form == "+" then
return new_default_s
elseif form == "*" then
return check_non_nil_star_form(split_default_s)
elseif form == "++" then
return compute_plusplus_s_form(pagename, new_default_s)
elseif form == "++*" then
if pagename:find("^[^ ]*[sz] ") then
return compute_double_last_cons_stem_of_split_verb(pagename, "es")
else
return check_non_nil_star_form(split_default_s)
end
else
return sub_tilde(form)
end
end
local function canonicalize_ing_form(form)
if form == "+" then
return new_default_ing
elseif form == "*" then
return check_non_nil_star_form(split_default_ing)
elseif form == "++" then
return compute_double_last_cons_stem(pagename) .. "ing"
elseif form == "++*" then
return compute_double_last_cons_stem_of_split_verb(pagename, "ing")
else
return sub_tilde(form)
end
end
local function canonicalize_ed_form(form)
if form == "+" then
return new_default_ed
elseif form == "*" then
return check_non_nil_star_form(split_default_ed)
elseif form == "++" then
return compute_double_last_cons_stem(pagename) .. "ed"
elseif form == "++*" then
return compute_double_last_cons_stem_of_split_verb(pagename, "ed")
else
return sub_tilde(form)
end
end
local function canonicalize_en_form(form)
if form == "n" then
return add_suffix(pagename, "n")
end
return canonicalize_ed_form(form)
end
--------------------------------- MAIN PARSING/CONJUGATING CODE --------------------------------
local past_ptcs_given
if par1 and par1:find("<") then
-------------------------- ANGLE-BRACKET FORMAT --------------------------
if par2 or par3 or par4 then
error("Can't specify 2=, 3= or 4= when 1= contains angle brackets: " .. par1)
end
-- In the angle bracket format, we always copy the full past tense specs to the past participle
-- specs if none of the latter are given, so act as if the past participle is always given.
-- There is a separate check to see if the past tense and past participle are identical, in any case.
past_ptcs_given = true
-- (1) Parse the indicator specs inside of angle brackets.
local function parse_indicator_spec(angle_bracket_spec)
local inside = angle_bracket_spec:match("^<(.*)>$")
assert(inside)
local segments = put.parse_balanced_segment_run(inside, "[", "]")
local comma_separated_groups = put.split_alternating_runs(segments, ",")
if #comma_separated_groups > 4 then
error("Too many comma-separated parts in indicator spec: " .. angle_bracket_spec)
end
local function fetch_qualifiers(separated_group)
local qualifiers
for j = 2, #separated_group - 1, 2 do
if separated_group[j + 1] ~= "" then
error("Extraneous text after bracketed qualifiers: '" .. concat(separated_group) .. "'")
end
if not qualifiers then
qualifiers = {}
end
insert(qualifiers, separated_group[j])
end
return qualifiers
end
local function fetch_specs(comma_separated_group)
if not comma_separated_group then
return {{}}
end
local specs = {}
local colon_separated_groups = put.split_alternating_runs(comma_separated_group, ":")
for _, colon_separated_group in ipairs(colon_separated_groups) do
local form = colon_separated_group[1]
if form == "*" or form == "++*" then
error("* and ++* not allowed inside of indicator specs: " .. angle_bracket_spec)
end
if form == "" then
form = nil
end
insert(specs, {form = form, q = fetch_qualifiers(colon_separated_group)})
end
return specs
end
local s_specs = fetch_specs(comma_separated_groups[1])
local ing_specs = fetch_specs(comma_separated_groups[2])
local ed_specs = fetch_specs(comma_separated_groups[3])
local en_specs = fetch_specs(comma_separated_groups[4])
for _, spec in ipairs(s_specs) do
if spec.form == "++" and #ing_specs == 1 and not ing_specs[1].form and not ing_specs[1].q
and #ed_specs == 1 and not ed_specs[1].form and not ed_specs[1].q then
ing_specs[1].form = "++"
ed_specs[1].form = "++"
break
end
end
return {
forms = {},
s_specs = s_specs,
ing_specs = ing_specs,
ed_specs = ed_specs,
en_specs = en_specs,
}
end
local parse_props = {
parse_indicator_spec = parse_indicator_spec,
}
local alternant_multiword_spec = iut.parse_inflected_text(par1, parse_props)
-- (2) Check for user-specified brackets; remove any links from the lemma, but remember the original
-- form so we can use it below in the 'lemma_linked' form.
-- Check to see if there are brackets in the pre-text or post-text. If so, use the linked lemma (with the
-- verb autolinked unless noautolinkverb is given). Otherwise, use the default headword algorithm.
local function check_bracket(val)
if val:find("%[%[") then
alternant_multiword_spec.saw_bracket = true
end
end
for _, alternant_or_word_spec in ipairs(alternant_multiword_spec.alternant_or_word_specs) do
check_bracket(alternant_or_word_spec.before_text)
if alternant_or_word_spec.alternants then
for _, multiword_spec in ipairs(alternant_or_word_spec.alternants) do
for _, word_spec in ipairs(multiword_spec.word_specs) do
check_bracket(word_spec.before_text)
end
check_bracket(multiword_spec.post_text)
end
end
end
check_bracket(alternant_multiword_spec.post_text)
iut.map_word_specs(alternant_multiword_spec, function(base)
if base.lemma == "" then
base.lemma = pagename
end
base.orig_lemma = base.lemma
base.lemma = remove_links(base.lemma)
if args.noautolinkverb or base.orig_lemma:find("%[%[") then
base.linked_lemma = base.orig_lemma
else
base.linked_lemma = "[[" .. base.orig_lemma .. "]]"
end
end)
-- (3) Conjugate the verbs according to the indicator specs parsed above.
local all_verb_slots = {
lemma = "infinitive",
lemma_linked = "infinitive",
s_form = "3|s|pres",
ing_form = "pres|ptcp",
ed_form = "past",
en_form = "past|ptcp",
}
local function conjugate_verb(base)
local def_s_form, def_ing_form, def_ed_form = base_default_verb_forms(base.lemma)
local function process_specs(slot, specs, default_form, canonicalize_plusplus)
for _, spec in ipairs(specs) do
local form = spec.form
if not form or form == "+" then
form = default_form
elseif form == "++" then
form = canonicalize_plusplus()
end
-- If there's a ~ in the form, substitute it with the lemma,
-- but make sure to first replace % in the lemma with %% so that
-- it doesn't get interpreted as a capture replace expression.
if form:find("~") then
-- Assign to a var because gsub returns multiple values.
local subbed_lemma = base.lemma:gsub("%%", "%%%%")
form = form:gsub("~", subbed_lemma)
end
-- If the form is -, don't insert any forms, which will result
-- in there being no overall forms (in fact it will be nil).
-- We check for that down below and substitute a single "-" as
-- the form, which in turn gets turned into special labels like
-- "no present participle".
if form ~= "-" then
iut.insert_form(base.forms, slot, {form = form, footnotes = spec.q})
end
end
end
process_specs("s_form", base.s_specs, def_s_form,
function() return compute_plusplus_s_form(base.lemma, def_s_form) end)
process_specs("ing_form", base.ing_specs, def_ing_form,
function() return compute_double_last_cons_stem(base.lemma) .. "ing" end)
process_specs("ed_form", base.ed_specs, def_ed_form,
function() return compute_double_last_cons_stem(base.lemma) .. "ed" end)
-- If the -en spec is completely missing, substitute the -ed spec in its entirely.
-- Otherwise, if individual -en forms are missing or use +, we will substitute the
-- default -ed form, as with the -ed spec.
local en_specs = base.en_specs
if #en_specs == 1 and not en_specs[1].form and not en_specs[1].q then
en_specs = base.ed_specs
end
process_specs("en_form", en_specs, def_ed_form,
function() return compute_double_last_cons_stem(base.lemma) .. "ed" end)
iut.insert_form(base.forms, "lemma", {form = base.lemma})
-- Add linked version of lemma for use in head=. We write this in a general fashion in case
-- there are multiple lemma forms (which isn't possible currently at this level, although it's
-- possible overall using the ((...,...)) notation).
iut.insert_forms(base.forms, "lemma_linked", iut.map_forms(base.forms.lemma, function(form)
if form == base.lemma and base.linked_lemma:find("%[%[") then
return base.linked_lemma
else
return form
end
end))
end
local inflect_props = {
slot_table = all_verb_slots,
inflect_word_spec = conjugate_verb,
}
iut.inflect_multiword_or_alternant_multiword_spec(alternant_multiword_spec, inflect_props)
-- (4) Fetch the forms and put the conjugated lemmas in data.heads if not explicitly given.
local function fetch_forms(slot)
local forms = alternant_multiword_spec.forms[slot]
-- See above. This should only occur if the user explicitly used -
-- for a spec.
if not forms or #forms == 0 then
forms = {{form = "-"}}
end
return forms
end
pres_3sgs = fetch_forms("s_form")
pres_ptcs = fetch_forms("ing_form")
pasts = fetch_forms("ed_form")
past_ptcs = fetch_forms("en_form")
-- Use the "linked" form of the lemma as the head if no head= explicitly given and the user specified brackets
-- in one of the lemmas. Otherwise we use the default headword-linking algorithm.
if #data.user_specified_heads == 0 and alternant_multiword_spec.saw_bracket then
data.heads = {}
for _, lemma_obj in ipairs(alternant_multiword_spec.forms.lemma_linked) do
local quals, refs = iut.convert_footnotes_to_qualifiers_and_references(lemma_obj.footnotes)
insert(data.heads, {term = lemma_obj.form, q = quals, refs = refs})
end
end
else
-------------------------- SEPARATE-PARAM FORMAT --------------------------
local pres_3sg, pres_ptc, past
if par1 and not (par2 or par3) then
-- Use of a single parameter other than "++", "*" or "++*" is now the "legacy" format,
-- and no longer supported.
if par1 == "es" or par1 == "ies" or par1 == "d" then
error("Legacy parameter 1=es/ies/d no longer supported, just use 'en-verb' without params")
elseif par1 == "++" or par1 == "*" or par1 == "++*" then
pres_3sg = canonicalize_s_form(par1)
pres_ptc = canonicalize_ing_form(par1)
past = canonicalize_ed_form(par1)
else
error("Legacy parameter 1=STEM no longer supported, just use 'en-verb' without params")
end
else
if par2 then
track("xxx2")
end
if par3 then
track("xxx3")
end
end
if not pres_3sg or not pres_ptc or not past then
-- Either all three should be set above, or none of them.
assert(not pres_3sg and not pres_ptc and not past)
if par1 then
pres_3sg = canonicalize_s_form(par1)
else
pres_3sg = new_default_s
end
if par2 then
pres_ptc = canonicalize_ing_form(par2)
else
pres_ptc = new_default_ing
end
if par3 then
past = canonicalize_ed_form(par3)
else
past = new_default_ed
end
end
local past_ptc
if par4 then
past_ptcs_given = true
past_ptc = canonicalize_en_form(par4)
track("xxx4")
else
past_ptc = past
end
pres_3sgs = {{form = pres_3sg}}
pres_ptcs = {{form = pres_ptc}}
pasts = {{form = past}}
past_ptcs = {{form = past_ptc}}
end
------------------------------------------- HANDLE OVERRIDES ------------------------------------------
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 collect_forms(label, accel_form, defaults, overrides, override_qualifiers, canonicalize)
if defaults[1].form == "-" then
return {label = "no " .. label}
else
local into_table = {label = label, accel = {form = accel_form}}
local maxindex = math.max(#defaults, overrides.maxindex)
local qualifiers = override_qualifiers[1] and {override_qualifiers[1]} or strip_brackets(defaults[1].footnotes)
insert(into_table, {term = defaults[1].form, q = qualifiers})
-- Present 3rd singular
for i = 2, maxindex do
local override_form = canonicalize(overrides[i])
if override_form then
-- If there is an override such as past_ptc2=..., only use the qualifier specified
-- using an override (past_ptc2_qual=...), if any; it doesn't make sense to combine
-- an override form with a qualifier specified inside of angle brackets.
insert(into_table, {term = override_form, q = {override_qualifiers[i]}})
elseif defaults[i] then
-- If the form comes from inside angle brackets, allow any override qualifier
-- (past_ptc2_qual=...) to override any qualifier specified inside of angle brackets.
-- FIXME: Maybe we should throw an error here if both exist.
local qualifiers = override_qualifiers[i] and {override_qualifiers[i]} or strip_brackets(defaults[i].footnotes)
insert(into_table, {term = defaults[i].form, q = qualifiers})
end
end
return into_table
end
end
local pres_3sg_infls = collect_forms("third-person singular simple present", "s-verb-form",
pres_3sgs, args[1], args.pres_3sg_qual, canonicalize_s_form)
local pres_ptc_infls = collect_forms("present participle", "ing-form",
pres_ptcs, args[2], args.pres_ptc_qual, canonicalize_ing_form)
local past_infls = collect_forms("simple past", "spast",
pasts, args[3], args.past_qual, canonicalize_ed_form)
local past_ptc_infls = collect_forms("past participle", "past|part",
past_ptcs, args[4], args.past_ptc_qual, canonicalize_en_form)
-- Are the past forms identical to the past participle forms? If so, we use a single
-- combined "simple past and past participle" label on the past tense forms.
-- We check for two conditions: Either no past participle forms were given at all, or
-- they were given but are identical in every way (all forms and qualifiers) to the past
-- tense forms. The former "no explicit past participle forms" check is important in the
-- "separate-parameter" format; if past tense overrides are given and no past participle
-- forms given, the past tense overrides should apply to the past participle as well.
-- In the angle-bracket format, it's expected that all forms and qualifiers are specified
-- using that format, and we explicitly copy past tense forms and qualifiers to past
-- participle ones if the latter are omitted, so we disable to "no explicit past participle
-- forms" check.
if args[4].maxindex > 0 or args.past_ptc_qual.maxindex > 0 then
past_ptcs_given = true
end
local identical = true
-- For the past and past participle to be identical, there must be
-- the same number of inflections, and each inflection must match
-- in term and qualifiers.
if #past_infls ~= #past_ptc_infls then
identical = false
else
for key, val in ipairs(past_infls) do
if past_ptc_infls[key].term ~= val.term then
identical = false
break
else
local quals1 = past_ptc_infls[key].q
local quals2 = val.q
if (not not quals1) ~= (not not quals2) then
-- one is nil, the other is not
identical = false
elseif quals1 and quals2 then
-- qualifiers present in both; each qualifier must match
if #quals1 ~= #quals2 then
identical = false
else
for k, v in ipairs(quals1) do
if v ~= quals2[k] then
identical = false
break
end
end
end
end
if not identical then
break
end
end
end
end
-- Insert the forms
insert(data.inflections, pres_3sg_infls)
insert(data.inflections, pres_ptc_infls)
if not past_ptcs_given or identical then
if past_ptcs[1].form == "-" then
past_infls.label = "no simple past or past participle"
else
past_infls.label = "simple past and past participle"
past_infls.accel = {form = "ed-form"}
end
insert(data.inflections, past_infls)
else
insert(data.inflections, past_infls)
insert(data.inflections, past_ptc_infls)
end
if pagename:find(" ") then
-- Check for placeholder "it"
local words = split(pagename, " ")
for _, word in ipairs(words) do
if word == "it" or word == "its" or word == "it's" then
insert(data.categories, langname .. ' terms with placeholder "it"')
break
end
end
-- Check for phrasal verbs
local phrasal_adverbs = list_to_set{
-- NOTE: This should only contain common phrasal adverbs, not random words like [[low]],
-- [[adrift]], etc.
"aback",
"about",
"above",
"across",
"after",
"against",
"ahead",
"along",
"apart",
"around",
"as",
"aside",
"at",
"away",
"back",
"before",
"behind",
"below",
"between",
"beyond",
"by",
"down",
"for",
"forth",
"from",
"in",
"into",
"of",
"off",
"on",
"onto",
"out",
"over",
"past",
"round",
"through",
"to",
"together",
"towards",
"under",
"up",
"upon",
"with",
"without",
}
local allowed_non_adverb_words = list_to_set{
"it",
"one",
"oneself",
"someone",
}
local base = pagename
local seen_adverbs = {}
-- Only consider a verb to be phrasal if it consists of a single base verb followed exclusively by either
-- adverbs from `phrasal_adverbs` or placeholder words from `allowed_non_adverb_words`, where at
-- least one following word is from `phrasal_adverbs` (hence [[can it]] is not a phrasal verb).
while true do
local prev, word = base:match("^(.+) (.-)$")
if not prev then
break
end
if phrasal_adverbs[word] then
insert(seen_adverbs, word)
elseif allowed_non_adverb_words[word] then
-- do nothing
else
break
end
base = prev
end
if not base:find(" ") and #seen_adverbs > 0 then
insert(data.categories, langname .. " phrasal verbs")
for i = #seen_adverbs, 1, -1 do
insert(data.categories, langname .. ' phrasal verbs formed with "' .. seen_adverbs[i] ..
'"')
end
end
end
end
}
return export
bxvenhr6r01p9okklvn4ko2af7vd95p
مۆدیوول:headword
828
35
36858
36857
2026-08-16T12:33:04Z
Ghybu
12
+ pluralize_pos(pos)
36858
Scribunto
text/plain
local export = {}
-- Named constants for all modules used, to make it easier to swap out sandbox versions.
local debug_track_module = "Module:debug/track"
local en_utilities_module = "Module:en-utilities"
local gender_and_number_module = "Module:gender and number"
local headword_data_module = "Module:headword/data"
local headword_page_module = "Module:headword/page"
local links_module = "Module:links"
local load_module = "Module:load"
local pages_module = "Module:pages"
local palindromes_module = "Module:palindromes"
local pron_qualifier_module = "Module:pron qualifier"
local scripts_module = "Module:scripts"
local scripts_data_module = "Module:scripts/data"
local script_utilities_module = "Module:script utilities"
local script_utilities_data_module = "Module:script utilities/data"
local string_utilities_module = "Module:string utilities"
local table_module = "Module:table"
local utilities_module = "Module:utilities"
local concat = table.concat
local insert = table.insert
local ipairs = ipairs
local max = math.max
local new_title = mw.title.new
local pairs = pairs
local require = require
local toNFC = mw.ustring.toNFC
local toNFD = mw.ustring.toNFD
local type = type
local ufind = mw.ustring.find
local ugmatch = mw.ustring.gmatch
local ugsub = mw.ustring.gsub
local umatch = mw.ustring.match
--[==[
Loaders for functions in other modules, which overwrite themselves with the target function when called. This ensures modules are only loaded when needed, retains the speed/convenience of locally-declared pre-loaded functions, and has no overhead after the first call, since the target functions are called directly in any subsequent calls.]==]
local function debug_track(...)
debug_track = require(debug_track_module)
return debug_track(...)
end
local function encode_entities(...)
encode_entities = require(string_utilities_module).encode_entities
return encode_entities(...)
end
local function extend(...)
extend = require(table_module).extend
return extend(...)
end
local function find_best_script_without_lang(...)
find_best_script_without_lang = require(scripts_module).findBestScriptWithoutLang
return find_best_script_without_lang(...)
end
local function format_categories(...)
format_categories = require(utilities_module).format_categories
return format_categories(...)
end
local function format_genders(...)
format_genders = require(gender_and_number_module).format_genders
return format_genders(...)
end
local function format_pron_qualifiers(...)
format_pron_qualifiers = require(pron_qualifier_module).format_qualifiers
return format_pron_qualifiers(...)
end
local function full_link(...)
full_link = require(links_module).full_link
return full_link(...)
end
local function get_current_L2(...)
get_current_L2 = require(pages_module).get_current_L2
return get_current_L2(...)
end
local function get_link_page(...)
get_link_page = require(links_module).get_link_page
return get_link_page(...)
end
local function get_script(...)
get_script = require(scripts_module).getByCode
return get_script(...)
end
local function is_palindrome(...)
is_palindrome = require(palindromes_module).is_palindrome
return is_palindrome(...)
end
local function language_link(...)
language_link = require(links_module).language_link
return language_link(...)
end
local function load_data(...)
load_data = require(load_module).load_data
return load_data(...)
end
local function pattern_escape(...)
pattern_escape = require(string_utilities_module).pattern_escape
return pattern_escape(...)
end
local function pluralize(pos)
--pluralize = require(en_utilities_module).pluralize
pluralize = require("Module:ckb-nouns").declination
return pluralize(pos).defPl
end
local function process_page(...)
process_page = require(headword_page_module).process_page
return process_page(...)
end
local function remove_links(...)
remove_links = require(links_module).remove_links
return remove_links(...)
end
local function shallow_copy(...)
shallow_copy = require(table_module).shallowCopy
return shallow_copy(...)
end
local function tag_text(...)
tag_text = require(script_utilities_module).tag_text
return tag_text(...)
end
local function tag_transcription(...)
tag_transcription = require(script_utilities_module).tag_transcription
return tag_transcription(...)
end
local function tag_translit(...)
tag_translit = require(script_utilities_module).tag_translit
return tag_translit(...)
end
local function trim(...)
trim = require(string_utilities_module).trim
return trim(...)
end
local function ulen(...)
ulen = require(string_utilities_module).len
return ulen(...)
end
--[==[
Loaders for objects, which load data (or some other object) into some variable, which can then be accessed as "foo or get_foo()", where the function get_foo sets the object to "foo" and then returns it. This ensures they are only loaded when needed, and avoids the need to check for the existence of the object each time, since once "foo" has been set, "get_foo" will not be called again.]==]
local m_data
local function get_data()
m_data = load_data(headword_data_module)
return m_data
end
local script_data
local function get_script_data()
script_data = load_data(scripts_data_module)
return script_data
end
local script_utilities_data
local function get_script_utilities_data()
script_utilities_data = load_data(script_utilities_data_module)
return script_utilities_data
end
-- If set to true, categories always appear, even in non-mainspace pages
local test_force_categories = false
-- Add a tracking category to track entries with certain (unusually undesirable) properties. `track_id` is an identifier
-- for the particular property being tracked and goes into the tracking page. Specifically, this adds a link in the
-- page text to [[Wiktionary:Tracking/headword/TRACK_ID]], meaning you can find all entries with the `track_id` property
-- by visiting [[Special:WhatLinksHere/Wiktionary:Tracking/headword/TRACK_ID]].
--
-- If `lang` (a language object) is given, an additional tracking page [[Wiktionary:Tracking/headword/TRACK_ID/CODE]] is
-- linked to where CODE is the language code of `lang`, and you can find all entries in the combination of `track_id`
-- and `lang` by visiting [[Special:WhatLinksHere/Wiktionary:Tracking/headword/TRACK_ID/CODE]]. This makes it possible to
-- isolate only the entries with a specific tracking property that are in a given language. Note that if `lang`
-- references at etymology-only language, both that language's code and its full parent's code are tracked.
local function track(track_id, lang)
local tracking_page = "headword/" .. track_id
if lang and lang:hasType("etymology-only") then
debug_track{tracking_page, tracking_page .. "/" .. lang:getCode(),
tracking_page .. "/" .. lang:getFullCode()}
elseif lang then
debug_track{tracking_page, tracking_page .. "/" .. lang:getCode()}
else
debug_track(tracking_page)
end
return true
end
local function text_in_script(text, script_code)
local sc = get_script(script_code)
if not sc then
error("Internal error: Bad script code " .. script_code)
end
local characters = sc.characters
local out
if characters then
text = ugsub(text, "%W", "")
out = ufind(text, "[" .. characters .. "]")
end
if out then
return true
else
return false
end
end
local spacingPunctuation = "[%s%p]+"
--[[ List of punctuation or spacing characters that are found inside of words.
Used to exclude characters from the regex above. ]]
local wordPunc = "-#%%&@־׳״'.·*’་•:᠊"
local notWordPunc = "[^" .. wordPunc .. "]+"
-- Format a term (either a head term or an inflection term) along with any left or right qualifiers, labels, references
-- or customized separator: `part` is the object specifying the term (and `lang` the language of the term), which should
-- optionally contain:
-- * left qualifiers in `q`, an array of strings;
-- * right qualifiers in `qq`, an array of strings;
-- * left labels in `l`, an array of strings;
-- * right labels in `ll`, an array of strings;
-- * references in `refs`, an array either of strings (formatted reference text) or objects containing fields `text`
-- (formatted reference text) and optionally `name` and/or `group`;
-- * a separator in `separator`, defaulting to " <i>or</i> " if this is not the first term (j > 1), otherwise "".
-- `formatted` is the formatted version of the term itself, and `j` is the index of the term.
local function format_term_with_qualifiers_and_refs(lang, part, formatted, j)
local function part_non_empty(field)
local list = part[field]
if not list then
return nil
end
if type(list) ~= "table" then
error(("Internal error: Wrong type for `part.%s`=%s, should be \"table\""):format(field, mw.dumpObject(list)))
end
return list[1]
end
if part_non_empty("q") or part_non_empty("qq") or part_non_empty("l") or
part_non_empty("ll") or part_non_empty("refs") then
formatted = format_pron_qualifiers {
lang = lang,
text = formatted,
q = part.q,
qq = part.qq,
l = part.l,
ll = part.ll,
refs = part.refs,
}
end
local separator = part.separator or j > 1 and " <i>or</i> " -- use "" to request no separator
if separator then
formatted = separator .. formatted
end
return formatted
end
--[==[Return true if the given head is multiword according to the algorithm used in full_headword().]==]
function export.head_is_multiword(head)
for possibleWordBreak in ugmatch(head, spacingPunctuation) do
if umatch(possibleWordBreak, notWordPunc) then
return true
end
end
return false
end
do
local function workaround_to_exclude_chars(s)
return (ugsub(s, notWordPunc, "\2%1\1"))
end
--[==[Add links to a multiword head.]==]
function export.add_multiword_links(head, default)
head = "\1" .. ugsub(head, spacingPunctuation, workaround_to_exclude_chars) .. "\2"
if default then
head = head
:gsub("(\1[^\2]*)\\([:#][^\2]*\2)", "%1\\\\%2")
:gsub("(\1[^\2]*)([:#][^\2]*\2)", "%1\\%2")
end
--Escape any remaining square brackets to stop them breaking links (e.g. "[citation needed]").
head = encode_entities(head, "[]", true, true)
--[=[
use this when workaround is no longer needed:
head = "[[" .. ugsub(head, WORDBREAKCHARS, "]]%1[[") .. "]]"
Remove any empty links, which could have been created above
at the beginning or end of the string.
]=]
return (head
:gsub("\1\2", "")
:gsub("[\1\2]", {["\1"] = "[[", ["\2"] = "]]"}))
end
end
-- In ckb.wiktionary.org, we don't have these. So, we don't need them; disabled.
-- local function non_categorizable(full_raw_pagename)
-- return full_raw_pagename:find("^Appendix:Gestures/") or
-- -- Unsupported titles with descriptive names.
-- (full_raw_pagename:find("^Unsupported titles/") and not full_raw_pagename:find("`"))
-- end
local function tag_text_and_add_quals_and_refs(data, head, formatted, j)
-- Add language and script wrapper.
formatted = tag_text(formatted, data.lang, head.sc, "head", nil, j == 1 and data.id or nil)
-- Add qualifiers, labels, references and separator.
return format_term_with_qualifiers_and_refs(data.lang, head, formatted, j)
end
-- Format a headword with transliterations.
local function format_headword(data)
-- Are there non-empty transliterations?
local has_translits = false
local has_manual_translits = false
------ Format the headwords. ------
local head_parts = {}
local unique_head_parts = {}
local has_multiple_heads = not not data.heads[2]
for j, head in ipairs(data.heads) do
if head.tr or head.ts then
has_translits = true
end
if head.tr and head.tr_manual or head.ts then
has_manual_translits = true
end
local formatted
-- Apply processing to the headword, for formatting links and such.
if head.term:find("[[", nil, true) and head.sc:getCode() ~= "Image" then
formatted = language_link{term = head.term, lang = data.lang}
else
formatted = data.lang:makeDisplayText(head.term, head.sc, true)
end
local head_part = tag_text_and_add_quals_and_refs(data, head, formatted, j)
insert(head_parts, head_part)
-- If multiple heads, try to determine whether all heads display the same. To do this we need to effectively
-- rerun the text tagging and addition of qualifiers and references, using 1 for all indices.
if has_multiple_heads then
local unique_head_part
if j == 1 then
unique_head_part = head_part
else
unique_head_part = tag_text_and_add_quals_and_refs(data, head, formatted, 1)
end
unique_head_parts[unique_head_part] = true
end
end
local set_size = 0
if has_multiple_heads then
for _ in pairs(unique_head_parts) do
set_size = set_size + 1
end
end
if set_size == 1 then
head_parts = head_parts[1]
else
head_parts = concat(head_parts)
end
if has_manual_translits then
-- [[Special:WhatLinksHere/Wiktionary:Tracking/headword/manual-tr]]
-- [[Special:WhatLinksHere/Wiktionary:Tracking/headword/manual-tr/LANGCODE]]
track("manual-tr", data.lang)
end
------ Format the transliterations and transcriptions. ------
local translits_formatted
if has_translits then
local translit_parts = {}
for _, head in ipairs(data.heads) do
if head.tr or head.ts then
local this_parts = {}
if head.tr then
insert(this_parts, tag_translit(head.tr, data.lang:getCode(), "head", nil, head.tr_manual))
if head.ts then
insert(this_parts, " ")
end
end
if head.ts then
insert(this_parts, "/" .. tag_transcription(head.ts, data.lang:getCode(), "head") .. "/")
end
insert(translit_parts, concat(this_parts))
end
end
translits_formatted = " (" .. concat(translit_parts, " <i>or</i> ") .. ")"
local langname = data.lang:getCanonicalName()
local transliteration_page = new_title(langname .. " transliteration", "Wiktionary")
local saw_translit_page = false
if transliteration_page and transliteration_page.exists then
translits_formatted = " [[Wiktionary:" .. langname .. " transliteration|•]]" .. translits_formatted
saw_translit_page = true
end
-- If data.lang is an etymology-only language and we didn't find a translation page for it, fall back to the
-- full parent.
if not saw_translit_page and data.lang:hasType("etymology-only") then
langname = data.lang:getFullName()
transliteration_page = new_title(langname .. " transliteration", "Wiktionary")
if transliteration_page and transliteration_page.exists then
translits_formatted = " [[Wiktionary:" .. langname .. " transliteration|•]]" .. translits_formatted
end
end
else
translits_formatted = ""
end
------ Paste heads and transliterations/transcriptions. ------
local lemma_gloss
if data.gloss then
lemma_gloss = ' <span class="ib-content qualifier-content">' .. data.gloss .. '</span>'
else
lemma_gloss = ""
end
return head_parts .. translits_formatted .. lemma_gloss
end
local function format_headword_genders(data)
local retval = ""
if data.genders and data.genders[1] then
if data.gloss then
retval = ","
end
local pos_for_cat
if not data.nogendercat then
local no_gender_cat = (m_data or get_data()).no_gender_cat
if not (no_gender_cat[data.lang:getCode()] or no_gender_cat[data.lang:getFullCode()]) then
pos_for_cat = (m_data or get_data()).pos_for_gender_number_cat[data.pos_category:gsub("^reconstructed ", "")]
end
end
local text, cats = format_genders(data.genders, data.lang, pos_for_cat)
if cats then
extend(data.categories, cats)
end
retval = retval .. " " .. text
end
return retval
end
local function format_inflection_parts(data, parts)
local any_part_translit = false
for j, part in ipairs(parts) do
if type(part) ~= "table" then
part = {term = part}
end
local partaccel = part.accel
local face = part.face or "bold"
if face ~= "bold" and face ~= "plain" and face ~= "hypothetical" then
error("The face `" .. face .. "` " .. (
(script_utilities_data or get_script_utilities_data()).faces[face] and
"should not be used for non-headword terms on the headword line." or
"is invalid."
))
end
-- Here the final part 'or data.nolinkinfl' allows to have 'nolinkinfl=true'
-- right into the 'data' table to disable inflection links of the entire headword
-- when inflected forms aren't entry-worthy, e.g.: in Vulgar Latin
local nolinkinfl = part.face == "hypothetical" or (part.nolink and track("nolink") or part.nolinkinfl) or (
data.nolink and track("nolink") or data.nolinkinfl)
local formatted
if part.label then
-- FIXME: There should be a better way of italicizing a label. As is, this isn't customizable.
formatted = "<i>" .. part.label .. "</i>"
else
-- Convert the term into a full link. Don't show a transliteration here unless enable_auto_translit is
-- requested, either at the `parts` level (i.e. per inflection) or at the `data.inflections` level (i.e.
-- specified for all inflections). This is controllable in {{head}} using autotrinfl=1 for all inflections,
-- or fNautotr=1 for an individual inflection (remember that a single inflection may be associated with
-- multiple terms). The reason for doing this is to avoid clutter in headword lines by default in languages
-- where the script is relatively straightforward to read by learners (e.g. Greek, Russian), but allow it
-- to be enabled in languages with more complex scripts (e.g. Arabic).
local tr = part.translit or (not (parts.enable_auto_translit or data.inflections.enable_auto_translit) and "-" or nil)
if tr ~= "-" then
any_part_translit = true
end
formatted = full_link(
{
term = not nolinkinfl and part.term or nil,
alt = part.alt or (nolinkinfl and part.term or nil),
lang = part.lang or data.lang,
sc = part.sc or parts.sc or nil,
gloss = part.gloss,
pos = part.pos,
lit = part.lit,
id = part.id,
genders = part.genders,
tr = tr,
ts = part.transcription,
accel = partaccel or parts.accel,
},
face
)
end
parts[j] = format_term_with_qualifiers_and_refs(part.lang or data.lang, part,
formatted, j)
end
local parts_output
if parts[1] then
parts_output = (parts.label and " " or "") .. concat(parts)
elseif parts.request then
parts_output = " <small>[please provide]</small>"
insert(data.categories, "Requests for inflections in " .. data.lang:getFullName() .. " entries")
else
parts_output = ""
end
local parts_label = parts.label and ("<i>" .. parts.label .. "</i>") or ""
return format_term_with_qualifiers_and_refs(data.lang, parts, parts_label .. parts_output, 1), any_part_translit
end
-- Format the inflections following the headword.
local function format_inflections(data)
local any_part_translit = false
if data.inflections and data.inflections[1] then
-- Format each inflection individually.
for key, infl in ipairs(data.inflections) do
local this_any_part_translit
data.inflections[key], this_any_part_translit = format_inflection_parts(data, infl)
if this_any_part_translit then
any_part_translit = true
end
end
local concat_result = concat(data.inflections, ", ")
return " (" .. concat_result .. ")"
else
return ""
end
end
--[==[
Returns the plural form of `pos`, a raw part of speech input, which could be singular or
plural. Irregular plural POS are taken into account (e.g. "kanji" pluralizes to
"kanji").
]==]
function export.pluralize_pos(pos)
-- Make the plural form of the part of speech
return (m_data or get_data()).irregular_plurals[pos] or
pos:sub(-3) == "کان" and pos or
pluralize(pos)
end
--[==[
Return "lemma" if the given POS is a lemma, "non-lemma form" if a non-lemma form, or nil
if unknown. The POS passed in must be in its plural form ("nouns", "prefixes", etc.).
If you have a POS in its singular form, call {export.pluralize_pos()} above to pluralize it
in a smart fashion that knows when to add "-s" and when to add "-es", and also takes
into account any irregular plurals.
If `best_guess` is given and the POS is in neither the lemma nor non-lemma list, guess
based on whether it ends in " forms"; otherwise, return nil.
]==]
function export.pos_lemma_or_nonlemma(plpos, best_guess)
local isLemma = (m_data or get_data()).lemmas
-- Is it a lemma category?
if isLemma[plpos] then
return "lemma"
end
local plpos_no_recon = plpos:gsub("^reconstructed ", "")
if isLemma[plpos_no_recon] then
return "lemma"
end
-- Is it a nonlemma category?
local isNonLemma = (m_data or get_data()).nonlemmas
if isNonLemma[plpos] or isNonLemma[plpos_no_recon] then
return "non-lemma form"
end
local plpos_no_mut = plpos:gsub("^mutated ", "")
if isLemma[plpos_no_mut] or isNonLemma[plpos_no_mut] then
return "non-lemma form"
elseif best_guess then
return plpos:find(" forms$") and "non-lemma form" or "lemma"
else
return nil
end
end
--[==[
Canonicalize a part of speech as specified in 2= in {{tl|head}}. This checks for POS aliases and non-lemma form
aliases ending in 'f', and then pluralizes if the POS term does not have an invariable plural.
]==]
function export.canonicalize_pos(pos)
-- FIXME: Temporary code to throw an error for alias 'pre' (= preposition) that will go away.
if pos == "pre" then
-- Don't throw error on 'pref' as it's an alias for "prefix".
error("POS 'pre' for 'preposition' no longer allowed as it's too ambiguous; use 'prep'")
end
-- Likewise for pro = pronoun.
if pos == "pro" or pos == "prof" then
error("POS 'pro' for 'pronoun' no longer allowed as it's too ambiguous; use 'pron'")
end
local data = m_data or get_data()
if data.pos_aliases[pos] then
pos = data.pos_aliases[pos]
elseif pos:sub(-1) == "f" then
pos = pos:sub(1, -2)
pos = (data.pos_aliases[pos] or pos) .. " forms"
end
return export.pluralize_pos(pos)
end
-- Find and return the maximum index in the array `data[element]` (which may have gaps in it), and initialize it to a
-- zero-length array if unspecified. Check to make sure all keys are numeric (other than "maxindex", which is set by
-- [[Module:parameters]] for list parameters), all values are strings, and unless `allow_blank_string` is given,
-- no blank (zero-length) strings are present.
local function init_and_find_maximum_index(data, element, allow_blank_string)
local maxind = 0
if not data[element] then
data[element] = {}
end
local typ = type(data[element])
if typ ~= "table" then
error(("In full_headword(), `data.%s` must be an array but is a %s"):format(element, typ))
end
for k, v in pairs(data[element]) do
if k ~= "maxindex" then
if type(k) ~= "number" then
error(("Unrecognized non-numeric key '%s' in `data.%s`"):format(k, element))
end
if k > maxind then
maxind = k
end
if v then
if type(v) ~= "string" then
error(("For key '%s' in `data.%s`, value should be a string but is a %s"):format(k, element, type(v)))
end
if not allow_blank_string and v == "" then
error(("For key '%s' in `data.%s`, blank string not allowed; use 'false' for the default"):format(k, element))
end
end
end
end
return maxind
end
--[==[
-- Add the page to various maintenance categories for the language and the
-- whole page. These are placed in the headword somewhat arbitrarily, but
-- mainly because headword templates are mandatory for entries (meaning that
-- in theory it provides full coverage).
--
-- This is provided as an external entry point so that modules which transclude
-- information from other entries (such as {{tl|ja-see}}) can take advantage
-- of this feature as well, because they are used in place of a conventional
-- headword template.]==]
do
-- Handle any manual sortkeys that have been specified in raw categories
-- by tracking if they are the same or different from the automatically-
-- generated sortkey, so that we can track them in maintenance
-- categories.
local function handle_raw_sortkeys(tbl, sortkey, page, lang, lang_cats)
sortkey = sortkey or lang:makeSortKey(page.pagename)
-- If there are raw categories with no sortkey, then they will be
-- sorted based on the default MediaWiki sortkey, so we check against
-- that.
if tbl == true then
if page.raw_defaultsort ~= sortkey then
insert(lang_cats, lang:getFullName() .. " terms with non-redundant non-automated sortkeys")
end
return
end
local redundant, different
for k in pairs(tbl) do
if k == sortkey then
redundant = true
else
different = true
end
end
if redundant then
insert(lang_cats, lang:getFullName() .. " terms with redundant sortkeys")
end
if different then
insert(lang_cats, lang:getFullName() .. " terms with non-redundant non-automated sortkeys")
end
return sortkey
end
function export.maintenance_cats(page, lang, lang_cats, page_cats)
extend(page_cats, page.cats)
lang = lang:getFull() -- since we are just generating categories
local canonical = lang:getCanonicalName()
local tbl, sortkey = page.wikitext_topic_cat[lang:getCode()]
if tbl then
sortkey = handle_raw_sortkeys(tbl, sortkey, page, lang, lang_cats)
insert(lang_cats, canonical .. " entries with topic categories using raw markup")
end
tbl = page.wikitext_langname_cat[canonical]
if tbl then
handle_raw_sortkeys(tbl, sortkey, page, lang, lang_cats)
insert(lang_cats, canonical .. " entries with language name categories using raw markup")
end
if get_current_L2() ~= canonical then
insert(lang_cats, canonical .. " entries with incorrect language header")
-- [[Special:WhatLinksHere/Wiktionary:Tracking/headword/incorrect language header]]
-- [[Special:WhatLinksHere/Wiktionary:Tracking/headword/incorrect language header/LANGCODE]]
track("incorrect language header", lang)
end
end
end
--[==[This is the primary external entry point.
{{lua|full_headword(data)}}
This is used by {{temp|head}} and various language-specific headword templates (e.g. {{temp|ru-adj}} for Russian adjectives, {{temp|de-noun}} for German nouns, etc.) to display an entire headword line.
See [[#Further explanations for full_headword()]]
]==]
function export.full_headword(data)
-- Prevent data from being destructively modified.
local data = shallow_copy(data)
------------ 1. Basic checks for old-style (multi-arg) calling convention. ------------
if data.getCanonicalName then
error("In full_headword(), the first argument `data` needs to be a Lua object (table) of properties, not a language object")
end
if not data.lang or type(data.lang) ~= "table" or not data.lang.getCode then
error("In full_headword(), the first argument `data` needs to be a Lua object (table) and `data.lang` must be a language object")
end
if data.id and type(data.id) ~= "string" then
error("The id in the data table should be a string.")
end
------------ 2. Initialize pagename etc. ------------
local langcode = data.lang:getCode()
local full_langcode = data.lang:getFullCode()
local langname = data.lang:getCanonicalName()
local full_langname = data.lang:getFullName()
local raw_pagename, page = data.pagename
if raw_pagename and raw_pagename ~= (m_data or get_data()).pagename then -- for testing, doc pages, etc.
page = process_page(raw_pagename)
else
page = (m_data or get_data()).page
end
-- Check the namespace against the language type.
local namespace = page.namespace
if namespace == "" then
if data.lang:hasType("reconstructed") then
error("Entries in " .. langname .. " must be placed in the Reconstruction: namespace")
elseif data.lang:hasType("appendix-constructed") then
error("Entries in " .. langname .. " must be placed in the Appendix: namespace")
end
elseif namespace == "Citations" or namespace == "Thesaurus" then
error("Headword templates should not be used in the " .. namespace .. ": namespace.")
end
------------ 3. Initialize `data.heads` table; if old-style, convert to new-style. ------------
if type(data.heads) == "table" and type(data.heads[1]) == "table" then
-- new-style
if data.translits or data.transcriptions then
error("In full_headword(), if `data.heads` is new-style (array of head objects), `data.translits` and `data.transcriptions` cannot be given")
end
else
-- convert old-style `heads`, `translits` and `transcriptions` to new-style
local maxind = max(
init_and_find_maximum_index(data, "heads"),
init_and_find_maximum_index(data, "translits", true),
init_and_find_maximum_index(data, "transcriptions", true)
)
for i = 1, maxind do
data.heads[i] = {
term = data.heads[i],
tr = data.translits[i],
ts = data.transcriptions[i],
}
end
end
-- Make sure there's at least one head.
if not data.heads[1] then
data.heads[1] = {}
end
------------ 4. Initialize and validate `data.categories` and `data.whole_page_categories`, and determine `pos_category` if not given, and add basic categories. ------------
-- EXPERIMENTAL: see [[Wiktionary:Beer parlour/2024/June#Decluttering the altform mess]]
if data.altform then
data.noposcat = true
end
init_and_find_maximum_index(data, "categories")
init_and_find_maximum_index(data, "whole_page_categories")
local pos_category_already_present = false
if data.categories[1] then
local escaped_langname = pattern_escape(full_langname)
local matches_lang_pattern = "^" .. escaped_langname .. " "
for _, cat in ipairs(data.categories) do
-- Does the category begin with the language name? If not, tag it with a tracking category.
if not cat:find(matches_lang_pattern) then
-- [[Special:WhatLinksHere/Wiktionary:Tracking/headword/no lang category]]
-- [[Special:WhatLinksHere/Wiktionary:Tracking/headword/no lang category/LANGCODE]]
track("no lang category", data.lang)
end
end
-- If `pos_category` not given, try to infer it from the first specified category. If this doesn't work, we
-- throw an error below.
if not data.pos_category and data.categories[1]:find(matches_lang_pattern) then
data.pos_category = data.categories[1]:gsub(matches_lang_pattern, "")
-- Optimization to avoid inserting category already present.
pos_category_already_present = true
end
end
if not data.pos_category then
error("`data.pos_category` not specified and could not be inferred from the categories given in "
.. "`data.categories`. Either specify the plural part of speech in `data.pos_category` "
.. "(e.g. \"proper nouns\") or ensure that the first category in `data.categories` is formed from the "
.. "language's canonical name plus the plural part of speech (e.g. \"Norwegian Bokmål proper nouns\")."
)
end
-- Insert a category at the beginning for the part of speech unless it's already present or `data.noposcat` given.
if not pos_category_already_present and not data.noposcat then
local pos_category = data.pos_category .. " بە " .. full_langname
-- FIXME: [[User:Theknightwho]] Why is this special case here? Please add an explanatory comment.
if pos_category ~= "Translingual Han characters" then
insert(data.categories, 1, pos_category)
end
end
-- Try to determine whether the part of speech refers to a lemma or a non-lemma form; if we can figure this out,
-- add an appropriate category.
local postype = export.pos_lemma_or_nonlemma(data.pos_category)
if not postype then
-- We don't know what this category is, so tag it with a tracking category.
-- [[Special:WhatLinksHere/Wiktionary:Tracking/headword/unrecognized pos]]
-- [[Special:WhatLinksHere/Wiktionary:Tracking/headword/unrecognized pos/LANGCODE]]
track("unrecognized pos", data.lang)
-- [[Special:WhatLinksHere/Wiktionary:Tracking/headword/unrecognized pos/POS]]
-- [[Special:WhatLinksHere/Wiktionary:Tracking/headword/unrecognized pos/POS/LANGCODE]]
track("unrecognized pos/pos/" .. data.pos_category, data.lang)
elseif not data.noposcat then
insert(data.categories, 1, postype .. " بە " .. full_langname)
end
-- EXPERIMENTAL: see [[Wiktionary:Beer parlour/2024/June#Decluttering the altform mess]]
if data.altform then
insert(data.categories, 1, full_langname .. " alternative forms")
end
------------ 5. Create a default headword, and add links to multiword page names. ------------
-- Determine if term is reconstructed
local is_reconstructed = namespace == "Reconstruction" or data.lang:hasType("reconstructed")
-- Create a default headword based on the pagename, which is determined in
-- advance by the data module so that it only needs to be done once.
local default_head = page.pagename
-- Add links to multi-word page names when appropriate
if not (is_reconstructed or data.nolinkhead) then
local no_links = (m_data or get_data()).no_multiword_links
if not (no_links[langcode] or no_links[full_langcode]) and export.head_is_multiword(default_head) then
default_head = export.add_multiword_links(default_head, true)
end
end
if is_reconstructed then
default_head = "*" .. default_head
end
------------ 6. Fill in missing values in `data.heads`. ------------
-- True if any script among the headword scripts has spaces in it.
local any_script_has_spaces = false
-- True if any term has a redundant head= param.
local has_redundant_head_param = false
for _, head in ipairs(data.heads) do
------ 6a. If missing head, replace with default head.
if not head.term then
head.term = default_head
elseif head.term == default_head then
has_redundant_head_param = true
elseif head.term:find("^[!?]$") then
-- If explicit head= just consists of ! or ?, add it to the end of the default head.
head.term = default_head .. head.term
end
if is_reconstructed then
local head_term = head.term
if head_term:find("%[%[") then
head_term = remove_links(head_term)
end
if head_term:sub(1, 1) ~= "*" then
error("The headword '" .. head_term .. "' must begin with '*' to indicate that it is reconstructed.")
end
end
------ 6b. Try to detect the script(s) if not provided. If a per-head script is provided, that takes precedence,
------ otherwise fall back to the overall script if given. If neither given, autodetect the script.
local auto_sc = data.lang:findBestScript(head.term)
if (
auto_sc:getCode() == "None" and
find_best_script_without_lang(head.term):getCode() ~= "None"
) then
insert(data.categories, full_langname .. " terms in nonstandard scripts")
end
if not (head.sc or data.sc) then -- No script code given, so use autodetected script.
head.sc = auto_sc
else
if not head.sc then -- Overall script code given.
head.sc = data.sc
end
-- Track uses of sc parameter.
if head.sc:getCode() == auto_sc:getCode() then
insert(data.categories, full_langname .. " terms with redundant script codes")
else
insert(data.categories, full_langname .. " terms with non-redundant manual script codes")
end
end
-- If using a discouraged character sequence, add to maintenance category.
if head.sc:hasNormalizationFixes() == true then
local composed_head = toNFC(head.term)
if head.sc:fixDiscouragedSequences(composed_head) ~= composed_head then
insert(data.whole_page_categories, "Pages using discouraged character sequences")
end
end
any_script_has_spaces = any_script_has_spaces or head.sc:hasSpaces()
------ 6c. Create automatic transliterations for any non-Latin headwords without manual translit given
------ (provided automatic translit is available, e.g. not in Persian or Hebrew).
-- Make transliterations
head.tr_manual = nil
-- Try to generate a transliteration if necessary
if head.tr == "-" then
head.tr = nil
else
local notranslit = (m_data or get_data()).notranslit
if not (notranslit[langcode] or notranslit[full_langcode]) and head.sc:isTransliterated() then
head.tr_manual = not not head.tr
local text = head.term
if not data.lang:link_tr(head.sc) then
text = remove_links(text)
end
local automated_tr, tr_categories
automated_tr, head.tr_fail, tr_categories = data.lang:transliterate(text, head.sc)
if automated_tr or head.tr_fail then
local manual_tr = head.tr
if manual_tr then
if (remove_links(manual_tr) == remove_links(automated_tr)) and (not head.tr_fail) then
insert(data.categories, full_langname .. " terms with redundant transliterations")
elseif not head.tr_fail then
insert(data.categories, full_langname .. " terms with non-redundant manual transliterations")
end
end
if not manual_tr then
head.tr = automated_tr
extend(data.categories, tr_categories)
end
end
-- There is still no transliteration?
-- Add the entry to a cleanup category.
if not head.tr then
head.tr = "<small>transliteration needed</small>"
-- FIXME: No current support for 'Request for transliteration of Classical Persian terms' or similar.
-- Consider adding this support in [[Module:category tree/poscatboiler/data/entry maintenance]].
insert(data.categories, "Requests for transliteration of " .. full_langname .. " terms")
else
-- Otherwise, trim it.
head.tr = trim(head.tr)
end
end
end
-- Link to the transliteration entry for languages that require this.
if head.tr and data.lang:link_tr(head.sc) then
head.tr = full_link{
term = head.tr,
lang = data.lang,
sc = get_script("Latn"),
tr = "-"
}
end
end
------------ 7. Maybe tag the title with the appropriate script code, using the `display_title` mechanism. ------------
-- Assumes that the scripts in "toBeTagged" will never occur in the Reconstruction namespace.
-- (FIXME: Don't make assumptions like this, and if you need to do so, throw an error if the assumption is violated.)
-- Avoid tagging ASCII as Hani even when it is tagged as Hani in the headword, as in [[check]]. The check for ASCII
-- might need to be expanded to a check for any Latin characters and whitespace or punctuation.
local display_title
-- Where there are multiple headwords, use the script for the first. This assumes the first headword is similar to
-- the pagename, and that headwords that are in different scripts from the pagename aren't first. This seems to be
-- about the best we can do (alternatively we could potentially do script detection on the pagename).
local dt_script = data.heads[1].sc
local dt_script_code = dt_script:getCode()
local page_non_ascii = namespace == "" and not page.pagename:find("^[%z\1-\127]+$")
local unsupported_pagename, unsupported = page.full_raw_pagename:gsub("^Unsupported titles/", "")
if unsupported == 1 and page.unsupported_titles[unsupported_pagename] then
display_title = 'Unsupported titles/<span class="' .. dt_script_code .. '">' .. page.unsupported_titles[unsupported_pagename] .. '</span>'
elseif page_non_ascii and (m_data or get_data()).toBeTagged[dt_script_code]
or (dt_script_code == "Jpan" and (text_in_script(page.pagename, "Hira") or text_in_script(page.pagename, "Kana")))
or (dt_script_code == "Kore" and text_in_script(page.pagename, "Hang")) then
display_title = '<span class="' .. dt_script_code .. '">' .. page.full_raw_pagename .. '</span>'
-- Keep Han entries region-neutral in the display title.
elseif page_non_ascii and (dt_script_code == "Hant" or dt_script_code == "Hans") then
display_title = '<span class="Hani">' .. page.full_raw_pagename .. '</span>'
elseif namespace == "Reconstruction" then
local matched
display_title, matched = ugsub(
page.full_raw_pagename,
"^(Reconstruction:[^/]+/)(.+)$",
function(before, term)
return before .. tag_text(term, data.lang, dt_script)
end
)
if matched == 0 then
display_title = nil
end
end
-- FIXME: Generalize this.
-- If the current language uses ur-Arab (for Urdu, etc.), ku-Arab (Central Kurdish) or pa-Arab
-- (Shahmukhi, for Punjabi) and there's more than one language on the page, don't set the display title
-- because these three scripts display in Nastaliq and we don't want this for terms that also exist in other
-- languages that don't display in Nastaliq (e.g. Arabic or Persian) to display in Nastaliq. Because the word
-- "Urdu" occurs near the end of the alphabet, Urdu fonts tend to override the fonts of other languages.
-- FIXME: This is checking for more than one language on the page but instead needs to check if there are any
-- languages using scripts other than the ones just mentioned.
if (dt_script_code == "ur-Arab" or dt_script_code == "ku-Arab" or dt_script_code == "pa-Arab") and page.L2_list.n > 1 then
display_title = nil
end
if display_title then
mw.getCurrentFrame():callParserFunction(
"DISPLAYTITLE",
display_title
)
end
------------ 8. Insert additional categories. ------------
if data.force_cat_output then
-- [[Special:WhatLinksHere/Wiktionary:Tracking/headword/force cat output]]
track("force cat output")
end
if has_redundant_head_param then
if not data.no_redundant_head_cat then
insert(data.categories, full_langname .. " terms with redundant head parameter")
end
end
-- If the first head is multiword (after removing links), maybe insert into "LANG multiword terms".
if not data.nomultiwordcat and any_script_has_spaces and postype == "lemma" then
local no_multiword_cat = (m_data or get_data()).no_multiword_cat
if not (no_multiword_cat[langcode] or no_multiword_cat[full_langcode]) then
-- Check for spaces or hyphens, but exclude prefixes and suffixes.
-- Use the pagename, not the head= value, because the latter may have extra
-- junk in it, e.g. superscripted text that throws off the algorithm.
local no_hyphen = (m_data or get_data()).hyphen_not_multiword_sep
-- Exclude hyphens if the data module states that they should for this language.
local checkpattern = (no_hyphen[langcode] or no_hyphen[full_langcode]) and ".[%s፡]." or ".[%s%-፡]."
if umatch(page.pagename, checkpattern) then
insert(data.categories, full_langname .. " multiword terms")
end
end
end
if data.sccat then
for _, head in ipairs(data.heads) do
insert(data.categories, full_langname .. " " .. data.pos_category .. " in " ..
head.sc:getDisplayForm())
end
end
-- Reconstructed terms often use weird combinations of scripts and realistically aren't spelled so much as notated.
if namespace ~= "Reconstruction" then
-- Map from languages to a string containing the characters to ignore when considering whether a term has
-- multiple written scripts in it. Typically these are Greek or Cyrillic letters used for their phonetic
-- values.
local characters_to_ignore = {
["aaq"] = "α", -- Penobscot
["acy"] = "δθ", -- Cypriot Arabic
["anc"] = "γ", -- Ngas
["aou"] = "χ", -- A'ou
["awg"] = "β", -- Anguthimri
["bhp"] = "β", -- Bima
["byk"] = "θ", -- Biao
["cdy"] = "θ", -- Chadong
["clm"] = "χ", -- Klallam
["col"] = "χ", -- Colombia-Wenatchi
["coo"] = "χ", -- Comox; FIXME: others? E.g. Greek theta (θ)?
["ets"] = "θ", -- Yekhee
["gmw-gts"] = "χ", -- Gottscheerish
["hur"] = "θ", -- Halkomelem
["izh"] = "ь", -- Ingrian
["kic"] = "θ", -- Kickapoo
["lil"] = "χ", -- Lillooet
["mhz"] = "β", -- Mor (Austronesian)
["neg"]= "ӡ", -- Negidal (normally in Cyrillic)
["oui"] = "γβ", -- Old Uyghur: FIXME: others? E.g. Greek delta (δ)?
["pox"] = "χ", -- Polabian
["rom"] = "Θθ", -- Romani: International Standard; two different thetas???
["sah"] = "ь", -- Yakut (1929 - 1939 Latin spelling)
["sjw"] = "θ", -- Shawnee
["squ"] = "χ", -- Squamish
["str"] = "χθ", -- Saanich; uses two Greek letters
["twa"] = "χ", -- Twana
["yha"] = "θ", -- Baha
["za"] = "зч", -- Zhuang; 1957-1982 alphabet used two Cyrillic letters (as well as some others like
-- ƃ, ƅ, ƨ, ɯ and ɵ that look like Cyrillic or Greek but are actually Latin)
["zlw-slv"] = "χђћ", -- Slovincian; FIXME: χ is Greek, the other two are Cyrillic, but I'm not sure
-- the currect characters are being chosen in the entry names
["zng"] = "θ", -- Mang
}
-- Determine how many real scripts are found in the pagename, where we exclude symbols and such. We exclude
-- scripts whose `character_category` is false as well as Zmth (mathematical notation symbols), which has a
-- category of "Mathematical notation symbols". When counting scripts, we need to elide language-specific
-- variants because e.g. Beng and as-Beng have slightly different characters but we don't want to consider them
-- two different scripts (e.g. [[এৰ]] has two characters which are detected respectively as Beng and as-Beng).
local seen_scripts = {}
local num_seen_scripts = 0
local num_loops = 0
local canon_pagename = page.pagename
local ch_to_ignore = characters_to_ignore[full_langcode]
if ch_to_ignore then
canon_pagename = ugsub(canon_pagename, "[" .. ch_to_ignore .. "]", "")
end
while true do
if canon_pagename == "" or num_seen_scripts >= 2 or num_loops >= 10 then
break
end
-- Make sure we don't get into a loop checking the same script over and over again; happens with e.g. [[ᠪᡳ]]
num_loops = num_loops + 1
local pagename_script = find_best_script_without_lang(canon_pagename, "None only as last resort")
local script_chars = pagename_script.characters
if not script_chars then
-- we are stuck; this happens with None
break
end
local script_code = pagename_script:getCode()
local replaced
canon_pagename, replaced = ugsub(canon_pagename, "[" .. script_chars .. "]", "")
if (
replaced and
script_code ~= "Zmth" and
(script_data or get_script_data())[script_code] and
script_data[script_code].character_category ~= false
) then
script_code = script_code:gsub("^.-%-", "")
if not seen_scripts[script_code] then
seen_scripts[script_code] = true
num_seen_scripts = num_seen_scripts + 1
end
end
end
if num_seen_scripts > 1 then
insert(data.categories, full_langname .. " terms written in multiple scripts")
end
end
-- Categorise for unusual characters. Takes into account combining characters, so that we can categorise for characters with diacritics that aren't encoded as atomic characters (e.g. U̠). These can be in two formats: single combining characters (i.e. character + diacritic(s)) or double combining characters (i.e. character + diacritic(s) + character). Each can have any number of diacritics.
local standard = data.lang:getStandardCharacters()
if standard then
local function char_category(char)
local specials = {
["#"] = "number sign",
["("] = "parentheses",
[")"] = "parentheses",
["<"] = "angle brackets",
[">"] = "angle brackets",
["["] = "square brackets",
["]"] = "square brackets",
["_"] = "underscore",
["{"] = "braces",
["|"] = "vertical line",
["}"] = "braces",
["ß"] = "ẞ",
["\205\133"] = "", -- this is UTF-8 for U+0345 ( ͅ)
["\239\191\189"] = "replacement character",
}
char = toNFD(char)
:gsub(".[\128-\191]*", function(m)
local new_m = specials[m]
new_m = new_m or m:uupper()
return new_m
end)
return toNFC(char)
end
if full_langcode ~= "hi" and full_langcode ~= "lo" then
local standard_chars_scripts = {}
for _, head in ipairs(data.heads) do
standard_chars_scripts[head.sc:getCode()] = true
end
-- Iterate over the scripts, in case there is more than one (as they can have different sets of standard characters).
for code in pairs(standard_chars_scripts) do
local sc_standard = data.lang:getStandardCharacters(code)
if sc_standard then
if page.pagename_len > 1 then
local explode_standard = {}
local function explode(char)
explode_standard[char] = true
return ""
end
local sc_standard = ugsub(sc_standard, page.comb_chars.combined_double, explode)
sc_standard = ugsub(sc_standard,page.comb_chars.combined_single, explode)
:gsub(".[\128-\191]*", explode)
local num_cat_inserted
for char in pairs(page.explode_pagename) do
if not explode_standard[char] then
if char:find("[0-9]") then
if not num_cat_inserted then
insert(data.categories, full_langname .. " terms spelled with numbers")
num_cat_inserted = true
end
elseif ufind(char, page.emoji_pattern) then
insert(data.categories, full_langname .. " terms spelled with emoji")
else
local upper = char_category(char)
if not explode_standard[upper] then
char = upper
end
insert(data.categories, full_langname .. " terms spelled with " .. char)
end
end
end
end
-- If a diacritic doesn't appear in any of the standard characters, also categorise for it generally.
sc_standard = toNFD(sc_standard)
for diacritic in ugmatch(page.decompose_pagename, page.comb_chars.diacritics_single) do
if not umatch(sc_standard, diacritic) then
insert(data.categories, full_langname .. " terms spelled with ◌" .. diacritic)
end
end
for diacritic in ugmatch(page.decompose_pagename, page.comb_chars.diacritics_double) do
if not umatch(sc_standard, diacritic) then
insert(data.categories, full_langname .. " terms spelled with ◌" .. diacritic .. "◌")
end
end
end
end
-- Ancient Greek, Hindi and Lao handled the old way for now, as their standard chars still need to be converted to the new format (because there are a lot of them).
elseif ulen(page.pagename) ~= 1 then
for character in ugmatch(page.pagename, "([^" .. standard .. "])") do
local upper = char_category(character)
if not umatch(upper, "[" .. standard .. "]") then
character = upper
end
insert(data.categories, full_langname .. " terms spelled with " .. character)
end
end
end
if data.heads[1].sc:isSystem("alphabet") then
local pagename, i = page.pagename:ulower(), 2
while umatch(pagename, "(%a)" .. ("%1"):rep(i)) do
i = i + 1
insert(data.categories, full_langname .. " terms with " .. i .. " consecutive instances of the same letter")
end
end
-- Categorise for palindromes
if not data.nopalindromecat and namespace ~= "Reconstruction" and ulen(page.pagename) > 2
-- FIXME: Use of first script here seems hacky. What is the clean way of doing this in the presence of
-- multiple scripts?
and is_palindrome(page.pagename, data.lang, data.heads[1].sc) then
insert(data.categories, full_langname .. " palindromes")
end
if namespace == "" and not data.lang:hasType("reconstructed") then
for _, head in ipairs(data.heads) do
if page.full_raw_pagename ~= get_link_page(remove_links(head.term), data.lang, head.sc) then
-- [[Special:WhatLinksHere/Wiktionary:Tracking/headword/pagename spelling mismatch]]
-- [[Special:WhatLinksHere/Wiktionary:Tracking/headword/pagename spelling mismatch/LANGCODE]]
track("pagename spelling mismatch", data.lang)
break
end
end
end
-- Add to various maintenance categories.
export.maintenance_cats(page, data.lang, data.categories, data.whole_page_categories)
------------ 9. Format and return headwords, genders, inflections and categories. ------------
-- Format and return all the gathered information. This may add more categories (e.g. gender/number categories),
-- so make sure we do it before evaluating `data.categories`.
local text = '<span class="headword-line">' ..
format_headword(data) ..
format_headword_genders(data) ..
format_inflections(data) .. '</span>'
-- Language-specific categories.
local cats = format_categories(
data.categories, data.lang, data.sort_key, page.encoded_pagename,
data.force_cat_output or test_force_categories, data.heads[1].sc
)
-- Language-agnostic categories.
local whole_page_cats = format_categories(
data.whole_page_categories, nil, "-"
)
return text .. cats .. whole_page_cats
end
return export
mt4fwysv0ep9mzbubsuta7j4xdqcsnn
36890
36858
2026-08-17T01:05:45Z
Ghybu
12
mw.ustring.sub
36890
Scribunto
text/plain
local export = {}
-- Named constants for all modules used, to make it easier to swap out sandbox versions.
local debug_track_module = "Module:debug/track"
local en_utilities_module = "Module:en-utilities"
local gender_and_number_module = "Module:gender and number"
local headword_data_module = "Module:headword/data"
local headword_page_module = "Module:headword/page"
local links_module = "Module:links"
local load_module = "Module:load"
local pages_module = "Module:pages"
local palindromes_module = "Module:palindromes"
local pron_qualifier_module = "Module:pron qualifier"
local scripts_module = "Module:scripts"
local scripts_data_module = "Module:scripts/data"
local script_utilities_module = "Module:script utilities"
local script_utilities_data_module = "Module:script utilities/data"
local string_utilities_module = "Module:string utilities"
local table_module = "Module:table"
local utilities_module = "Module:utilities"
local concat = table.concat
local insert = table.insert
local ipairs = ipairs
local max = math.max
local new_title = mw.title.new
local pairs = pairs
local require = require
local toNFC = mw.ustring.toNFC
local toNFD = mw.ustring.toNFD
local type = type
local ufind = mw.ustring.find
local ugmatch = mw.ustring.gmatch
local ugsub = mw.ustring.gsub
local umatch = mw.ustring.match
--[==[
Loaders for functions in other modules, which overwrite themselves with the target function when called. This ensures modules are only loaded when needed, retains the speed/convenience of locally-declared pre-loaded functions, and has no overhead after the first call, since the target functions are called directly in any subsequent calls.]==]
local function debug_track(...)
debug_track = require(debug_track_module)
return debug_track(...)
end
local function encode_entities(...)
encode_entities = require(string_utilities_module).encode_entities
return encode_entities(...)
end
local function extend(...)
extend = require(table_module).extend
return extend(...)
end
local function find_best_script_without_lang(...)
find_best_script_without_lang = require(scripts_module).findBestScriptWithoutLang
return find_best_script_without_lang(...)
end
local function format_categories(...)
format_categories = require(utilities_module).format_categories
return format_categories(...)
end
local function format_genders(...)
format_genders = require(gender_and_number_module).format_genders
return format_genders(...)
end
local function format_pron_qualifiers(...)
format_pron_qualifiers = require(pron_qualifier_module).format_qualifiers
return format_pron_qualifiers(...)
end
local function full_link(...)
full_link = require(links_module).full_link
return full_link(...)
end
local function get_current_L2(...)
get_current_L2 = require(pages_module).get_current_L2
return get_current_L2(...)
end
local function get_link_page(...)
get_link_page = require(links_module).get_link_page
return get_link_page(...)
end
local function get_script(...)
get_script = require(scripts_module).getByCode
return get_script(...)
end
local function is_palindrome(...)
is_palindrome = require(palindromes_module).is_palindrome
return is_palindrome(...)
end
local function language_link(...)
language_link = require(links_module).language_link
return language_link(...)
end
local function load_data(...)
load_data = require(load_module).load_data
return load_data(...)
end
local function pattern_escape(...)
pattern_escape = require(string_utilities_module).pattern_escape
return pattern_escape(...)
end
local function pluralize(pos)
--pluralize = require(en_utilities_module).pluralize
pluralize = require("Module:ckb-nouns").declination
return pluralize(pos).defPl
end
local function process_page(...)
process_page = require(headword_page_module).process_page
return process_page(...)
end
local function remove_links(...)
remove_links = require(links_module).remove_links
return remove_links(...)
end
local function shallow_copy(...)
shallow_copy = require(table_module).shallowCopy
return shallow_copy(...)
end
local function tag_text(...)
tag_text = require(script_utilities_module).tag_text
return tag_text(...)
end
local function tag_transcription(...)
tag_transcription = require(script_utilities_module).tag_transcription
return tag_transcription(...)
end
local function tag_translit(...)
tag_translit = require(script_utilities_module).tag_translit
return tag_translit(...)
end
local function trim(...)
trim = require(string_utilities_module).trim
return trim(...)
end
local function ulen(...)
ulen = require(string_utilities_module).len
return ulen(...)
end
--[==[
Loaders for objects, which load data (or some other object) into some variable, which can then be accessed as "foo or get_foo()", where the function get_foo sets the object to "foo" and then returns it. This ensures they are only loaded when needed, and avoids the need to check for the existence of the object each time, since once "foo" has been set, "get_foo" will not be called again.]==]
local m_data
local function get_data()
m_data = load_data(headword_data_module)
return m_data
end
local script_data
local function get_script_data()
script_data = load_data(scripts_data_module)
return script_data
end
local script_utilities_data
local function get_script_utilities_data()
script_utilities_data = load_data(script_utilities_data_module)
return script_utilities_data
end
-- If set to true, categories always appear, even in non-mainspace pages
local test_force_categories = false
-- Add a tracking category to track entries with certain (unusually undesirable) properties. `track_id` is an identifier
-- for the particular property being tracked and goes into the tracking page. Specifically, this adds a link in the
-- page text to [[Wiktionary:Tracking/headword/TRACK_ID]], meaning you can find all entries with the `track_id` property
-- by visiting [[Special:WhatLinksHere/Wiktionary:Tracking/headword/TRACK_ID]].
--
-- If `lang` (a language object) is given, an additional tracking page [[Wiktionary:Tracking/headword/TRACK_ID/CODE]] is
-- linked to where CODE is the language code of `lang`, and you can find all entries in the combination of `track_id`
-- and `lang` by visiting [[Special:WhatLinksHere/Wiktionary:Tracking/headword/TRACK_ID/CODE]]. This makes it possible to
-- isolate only the entries with a specific tracking property that are in a given language. Note that if `lang`
-- references at etymology-only language, both that language's code and its full parent's code are tracked.
local function track(track_id, lang)
local tracking_page = "headword/" .. track_id
if lang and lang:hasType("etymology-only") then
debug_track{tracking_page, tracking_page .. "/" .. lang:getCode(),
tracking_page .. "/" .. lang:getFullCode()}
elseif lang then
debug_track{tracking_page, tracking_page .. "/" .. lang:getCode()}
else
debug_track(tracking_page)
end
return true
end
local function text_in_script(text, script_code)
local sc = get_script(script_code)
if not sc then
error("Internal error: Bad script code " .. script_code)
end
local characters = sc.characters
local out
if characters then
text = ugsub(text, "%W", "")
out = ufind(text, "[" .. characters .. "]")
end
if out then
return true
else
return false
end
end
local spacingPunctuation = "[%s%p]+"
--[[ List of punctuation or spacing characters that are found inside of words.
Used to exclude characters from the regex above. ]]
local wordPunc = "-#%%&@־׳״'.·*’་•:᠊"
local notWordPunc = "[^" .. wordPunc .. "]+"
-- Format a term (either a head term or an inflection term) along with any left or right qualifiers, labels, references
-- or customized separator: `part` is the object specifying the term (and `lang` the language of the term), which should
-- optionally contain:
-- * left qualifiers in `q`, an array of strings;
-- * right qualifiers in `qq`, an array of strings;
-- * left labels in `l`, an array of strings;
-- * right labels in `ll`, an array of strings;
-- * references in `refs`, an array either of strings (formatted reference text) or objects containing fields `text`
-- (formatted reference text) and optionally `name` and/or `group`;
-- * a separator in `separator`, defaulting to " <i>or</i> " if this is not the first term (j > 1), otherwise "".
-- `formatted` is the formatted version of the term itself, and `j` is the index of the term.
local function format_term_with_qualifiers_and_refs(lang, part, formatted, j)
local function part_non_empty(field)
local list = part[field]
if not list then
return nil
end
if type(list) ~= "table" then
error(("Internal error: Wrong type for `part.%s`=%s, should be \"table\""):format(field, mw.dumpObject(list)))
end
return list[1]
end
if part_non_empty("q") or part_non_empty("qq") or part_non_empty("l") or
part_non_empty("ll") or part_non_empty("refs") then
formatted = format_pron_qualifiers {
lang = lang,
text = formatted,
q = part.q,
qq = part.qq,
l = part.l,
ll = part.ll,
refs = part.refs,
}
end
local separator = part.separator or j > 1 and " <i>or</i> " -- use "" to request no separator
if separator then
formatted = separator .. formatted
end
return formatted
end
--[==[Return true if the given head is multiword according to the algorithm used in full_headword().]==]
function export.head_is_multiword(head)
for possibleWordBreak in ugmatch(head, spacingPunctuation) do
if umatch(possibleWordBreak, notWordPunc) then
return true
end
end
return false
end
do
local function workaround_to_exclude_chars(s)
return (ugsub(s, notWordPunc, "\2%1\1"))
end
--[==[Add links to a multiword head.]==]
function export.add_multiword_links(head, default)
head = "\1" .. ugsub(head, spacingPunctuation, workaround_to_exclude_chars) .. "\2"
if default then
head = head
:gsub("(\1[^\2]*)\\([:#][^\2]*\2)", "%1\\\\%2")
:gsub("(\1[^\2]*)([:#][^\2]*\2)", "%1\\%2")
end
--Escape any remaining square brackets to stop them breaking links (e.g. "[citation needed]").
head = encode_entities(head, "[]", true, true)
--[=[
use this when workaround is no longer needed:
head = "[[" .. ugsub(head, WORDBREAKCHARS, "]]%1[[") .. "]]"
Remove any empty links, which could have been created above
at the beginning or end of the string.
]=]
return (head
:gsub("\1\2", "")
:gsub("[\1\2]", {["\1"] = "[[", ["\2"] = "]]"}))
end
end
-- In ckb.wiktionary.org, we don't have these. So, we don't need them; disabled.
-- local function non_categorizable(full_raw_pagename)
-- return full_raw_pagename:find("^Appendix:Gestures/") or
-- -- Unsupported titles with descriptive names.
-- (full_raw_pagename:find("^Unsupported titles/") and not full_raw_pagename:find("`"))
-- end
local function tag_text_and_add_quals_and_refs(data, head, formatted, j)
-- Add language and script wrapper.
formatted = tag_text(formatted, data.lang, head.sc, "head", nil, j == 1 and data.id or nil)
-- Add qualifiers, labels, references and separator.
return format_term_with_qualifiers_and_refs(data.lang, head, formatted, j)
end
-- Format a headword with transliterations.
local function format_headword(data)
-- Are there non-empty transliterations?
local has_translits = false
local has_manual_translits = false
------ Format the headwords. ------
local head_parts = {}
local unique_head_parts = {}
local has_multiple_heads = not not data.heads[2]
for j, head in ipairs(data.heads) do
if head.tr or head.ts then
has_translits = true
end
if head.tr and head.tr_manual or head.ts then
has_manual_translits = true
end
local formatted
-- Apply processing to the headword, for formatting links and such.
if head.term:find("[[", nil, true) and head.sc:getCode() ~= "Image" then
formatted = language_link{term = head.term, lang = data.lang}
else
formatted = data.lang:makeDisplayText(head.term, head.sc, true)
end
local head_part = tag_text_and_add_quals_and_refs(data, head, formatted, j)
insert(head_parts, head_part)
-- If multiple heads, try to determine whether all heads display the same. To do this we need to effectively
-- rerun the text tagging and addition of qualifiers and references, using 1 for all indices.
if has_multiple_heads then
local unique_head_part
if j == 1 then
unique_head_part = head_part
else
unique_head_part = tag_text_and_add_quals_and_refs(data, head, formatted, 1)
end
unique_head_parts[unique_head_part] = true
end
end
local set_size = 0
if has_multiple_heads then
for _ in pairs(unique_head_parts) do
set_size = set_size + 1
end
end
if set_size == 1 then
head_parts = head_parts[1]
else
head_parts = concat(head_parts)
end
if has_manual_translits then
-- [[Special:WhatLinksHere/Wiktionary:Tracking/headword/manual-tr]]
-- [[Special:WhatLinksHere/Wiktionary:Tracking/headword/manual-tr/LANGCODE]]
track("manual-tr", data.lang)
end
------ Format the transliterations and transcriptions. ------
local translits_formatted
if has_translits then
local translit_parts = {}
for _, head in ipairs(data.heads) do
if head.tr or head.ts then
local this_parts = {}
if head.tr then
insert(this_parts, tag_translit(head.tr, data.lang:getCode(), "head", nil, head.tr_manual))
if head.ts then
insert(this_parts, " ")
end
end
if head.ts then
insert(this_parts, "/" .. tag_transcription(head.ts, data.lang:getCode(), "head") .. "/")
end
insert(translit_parts, concat(this_parts))
end
end
translits_formatted = " (" .. concat(translit_parts, " <i>or</i> ") .. ")"
local langname = data.lang:getCanonicalName()
local transliteration_page = new_title(langname .. " transliteration", "Wiktionary")
local saw_translit_page = false
if transliteration_page and transliteration_page.exists then
translits_formatted = " [[Wiktionary:" .. langname .. " transliteration|•]]" .. translits_formatted
saw_translit_page = true
end
-- If data.lang is an etymology-only language and we didn't find a translation page for it, fall back to the
-- full parent.
if not saw_translit_page and data.lang:hasType("etymology-only") then
langname = data.lang:getFullName()
transliteration_page = new_title(langname .. " transliteration", "Wiktionary")
if transliteration_page and transliteration_page.exists then
translits_formatted = " [[Wiktionary:" .. langname .. " transliteration|•]]" .. translits_formatted
end
end
else
translits_formatted = ""
end
------ Paste heads and transliterations/transcriptions. ------
local lemma_gloss
if data.gloss then
lemma_gloss = ' <span class="ib-content qualifier-content">' .. data.gloss .. '</span>'
else
lemma_gloss = ""
end
return head_parts .. translits_formatted .. lemma_gloss
end
local function format_headword_genders(data)
local retval = ""
if data.genders and data.genders[1] then
if data.gloss then
retval = ","
end
local pos_for_cat
if not data.nogendercat then
local no_gender_cat = (m_data or get_data()).no_gender_cat
if not (no_gender_cat[data.lang:getCode()] or no_gender_cat[data.lang:getFullCode()]) then
pos_for_cat = (m_data or get_data()).pos_for_gender_number_cat[data.pos_category:gsub("^reconstructed ", "")]
end
end
local text, cats = format_genders(data.genders, data.lang, pos_for_cat)
if cats then
extend(data.categories, cats)
end
retval = retval .. " " .. text
end
return retval
end
local function format_inflection_parts(data, parts)
local any_part_translit = false
for j, part in ipairs(parts) do
if type(part) ~= "table" then
part = {term = part}
end
local partaccel = part.accel
local face = part.face or "bold"
if face ~= "bold" and face ~= "plain" and face ~= "hypothetical" then
error("The face `" .. face .. "` " .. (
(script_utilities_data or get_script_utilities_data()).faces[face] and
"should not be used for non-headword terms on the headword line." or
"is invalid."
))
end
-- Here the final part 'or data.nolinkinfl' allows to have 'nolinkinfl=true'
-- right into the 'data' table to disable inflection links of the entire headword
-- when inflected forms aren't entry-worthy, e.g.: in Vulgar Latin
local nolinkinfl = part.face == "hypothetical" or (part.nolink and track("nolink") or part.nolinkinfl) or (
data.nolink and track("nolink") or data.nolinkinfl)
local formatted
if part.label then
-- FIXME: There should be a better way of italicizing a label. As is, this isn't customizable.
formatted = "<i>" .. part.label .. "</i>"
else
-- Convert the term into a full link. Don't show a transliteration here unless enable_auto_translit is
-- requested, either at the `parts` level (i.e. per inflection) or at the `data.inflections` level (i.e.
-- specified for all inflections). This is controllable in {{head}} using autotrinfl=1 for all inflections,
-- or fNautotr=1 for an individual inflection (remember that a single inflection may be associated with
-- multiple terms). The reason for doing this is to avoid clutter in headword lines by default in languages
-- where the script is relatively straightforward to read by learners (e.g. Greek, Russian), but allow it
-- to be enabled in languages with more complex scripts (e.g. Arabic).
local tr = part.translit or (not (parts.enable_auto_translit or data.inflections.enable_auto_translit) and "-" or nil)
if tr ~= "-" then
any_part_translit = true
end
formatted = full_link(
{
term = not nolinkinfl and part.term or nil,
alt = part.alt or (nolinkinfl and part.term or nil),
lang = part.lang or data.lang,
sc = part.sc or parts.sc or nil,
gloss = part.gloss,
pos = part.pos,
lit = part.lit,
id = part.id,
genders = part.genders,
tr = tr,
ts = part.transcription,
accel = partaccel or parts.accel,
},
face
)
end
parts[j] = format_term_with_qualifiers_and_refs(part.lang or data.lang, part,
formatted, j)
end
local parts_output
if parts[1] then
parts_output = (parts.label and " " or "") .. concat(parts)
elseif parts.request then
parts_output = " <small>[please provide]</small>"
insert(data.categories, "Requests for inflections in " .. data.lang:getFullName() .. " entries")
else
parts_output = ""
end
local parts_label = parts.label and ("<i>" .. parts.label .. "</i>") or ""
return format_term_with_qualifiers_and_refs(data.lang, parts, parts_label .. parts_output, 1), any_part_translit
end
-- Format the inflections following the headword.
local function format_inflections(data)
local any_part_translit = false
if data.inflections and data.inflections[1] then
-- Format each inflection individually.
for key, infl in ipairs(data.inflections) do
local this_any_part_translit
data.inflections[key], this_any_part_translit = format_inflection_parts(data, infl)
if this_any_part_translit then
any_part_translit = true
end
end
local concat_result = concat(data.inflections, ", ")
return " (" .. concat_result .. ")"
else
return ""
end
end
--[==[
Returns the plural form of `pos`, a raw part of speech input, which could be singular or
plural. Irregular plural POS are taken into account (e.g. "kanji" pluralizes to
"kanji").
]==]
function export.pluralize_pos(pos)
-- Make the plural form of the part of speech
return (m_data or get_data()).irregular_plurals[pos] or
mw.ustring.sub(pos, -3) == "کان" and pos or
pluralize(pos)
end
--[==[
Return "lemma" if the given POS is a lemma, "non-lemma form" if a non-lemma form, or nil
if unknown. The POS passed in must be in its plural form ("nouns", "prefixes", etc.).
If you have a POS in its singular form, call {export.pluralize_pos()} above to pluralize it
in a smart fashion that knows when to add "-s" and when to add "-es", and also takes
into account any irregular plurals.
If `best_guess` is given and the POS is in neither the lemma nor non-lemma list, guess
based on whether it ends in " forms"; otherwise, return nil.
]==]
function export.pos_lemma_or_nonlemma(plpos, best_guess)
local isLemma = (m_data or get_data()).lemmas
-- Is it a lemma category?
if isLemma[plpos] then
return "lemma"
end
local plpos_no_recon = plpos:gsub("^reconstructed ", "")
if isLemma[plpos_no_recon] then
return "lemma"
end
-- Is it a nonlemma category?
local isNonLemma = (m_data or get_data()).nonlemmas
if isNonLemma[plpos] or isNonLemma[plpos_no_recon] then
return "non-lemma form"
end
local plpos_no_mut = plpos:gsub("^mutated ", "")
if isLemma[plpos_no_mut] or isNonLemma[plpos_no_mut] then
return "non-lemma form"
elseif best_guess then
return plpos:find(" forms$") and "non-lemma form" or "lemma"
else
return nil
end
end
--[==[
Canonicalize a part of speech as specified in 2= in {{tl|head}}. This checks for POS aliases and non-lemma form
aliases ending in 'f', and then pluralizes if the POS term does not have an invariable plural.
]==]
function export.canonicalize_pos(pos)
-- FIXME: Temporary code to throw an error for alias 'pre' (= preposition) that will go away.
if pos == "pre" then
-- Don't throw error on 'pref' as it's an alias for "prefix".
error("POS 'pre' for 'preposition' no longer allowed as it's too ambiguous; use 'prep'")
end
-- Likewise for pro = pronoun.
if pos == "pro" or pos == "prof" then
error("POS 'pro' for 'pronoun' no longer allowed as it's too ambiguous; use 'pron'")
end
local data = m_data or get_data()
if data.pos_aliases[pos] then
pos = data.pos_aliases[pos]
elseif pos:sub(-1) == "f" then
pos = pos:sub(1, -2)
pos = (data.pos_aliases[pos] or pos) .. " forms"
end
return export.pluralize_pos(pos)
end
-- Find and return the maximum index in the array `data[element]` (which may have gaps in it), and initialize it to a
-- zero-length array if unspecified. Check to make sure all keys are numeric (other than "maxindex", which is set by
-- [[Module:parameters]] for list parameters), all values are strings, and unless `allow_blank_string` is given,
-- no blank (zero-length) strings are present.
local function init_and_find_maximum_index(data, element, allow_blank_string)
local maxind = 0
if not data[element] then
data[element] = {}
end
local typ = type(data[element])
if typ ~= "table" then
error(("In full_headword(), `data.%s` must be an array but is a %s"):format(element, typ))
end
for k, v in pairs(data[element]) do
if k ~= "maxindex" then
if type(k) ~= "number" then
error(("Unrecognized non-numeric key '%s' in `data.%s`"):format(k, element))
end
if k > maxind then
maxind = k
end
if v then
if type(v) ~= "string" then
error(("For key '%s' in `data.%s`, value should be a string but is a %s"):format(k, element, type(v)))
end
if not allow_blank_string and v == "" then
error(("For key '%s' in `data.%s`, blank string not allowed; use 'false' for the default"):format(k, element))
end
end
end
end
return maxind
end
--[==[
-- Add the page to various maintenance categories for the language and the
-- whole page. These are placed in the headword somewhat arbitrarily, but
-- mainly because headword templates are mandatory for entries (meaning that
-- in theory it provides full coverage).
--
-- This is provided as an external entry point so that modules which transclude
-- information from other entries (such as {{tl|ja-see}}) can take advantage
-- of this feature as well, because they are used in place of a conventional
-- headword template.]==]
do
-- Handle any manual sortkeys that have been specified in raw categories
-- by tracking if they are the same or different from the automatically-
-- generated sortkey, so that we can track them in maintenance
-- categories.
local function handle_raw_sortkeys(tbl, sortkey, page, lang, lang_cats)
sortkey = sortkey or lang:makeSortKey(page.pagename)
-- If there are raw categories with no sortkey, then they will be
-- sorted based on the default MediaWiki sortkey, so we check against
-- that.
if tbl == true then
if page.raw_defaultsort ~= sortkey then
insert(lang_cats, lang:getFullName() .. " terms with non-redundant non-automated sortkeys")
end
return
end
local redundant, different
for k in pairs(tbl) do
if k == sortkey then
redundant = true
else
different = true
end
end
if redundant then
insert(lang_cats, lang:getFullName() .. " terms with redundant sortkeys")
end
if different then
insert(lang_cats, lang:getFullName() .. " terms with non-redundant non-automated sortkeys")
end
return sortkey
end
function export.maintenance_cats(page, lang, lang_cats, page_cats)
extend(page_cats, page.cats)
lang = lang:getFull() -- since we are just generating categories
local canonical = lang:getCanonicalName()
local tbl, sortkey = page.wikitext_topic_cat[lang:getCode()]
if tbl then
sortkey = handle_raw_sortkeys(tbl, sortkey, page, lang, lang_cats)
insert(lang_cats, canonical .. " entries with topic categories using raw markup")
end
tbl = page.wikitext_langname_cat[canonical]
if tbl then
handle_raw_sortkeys(tbl, sortkey, page, lang, lang_cats)
insert(lang_cats, canonical .. " entries with language name categories using raw markup")
end
if get_current_L2() ~= canonical then
insert(lang_cats, canonical .. " entries with incorrect language header")
-- [[Special:WhatLinksHere/Wiktionary:Tracking/headword/incorrect language header]]
-- [[Special:WhatLinksHere/Wiktionary:Tracking/headword/incorrect language header/LANGCODE]]
track("incorrect language header", lang)
end
end
end
--[==[This is the primary external entry point.
{{lua|full_headword(data)}}
This is used by {{temp|head}} and various language-specific headword templates (e.g. {{temp|ru-adj}} for Russian adjectives, {{temp|de-noun}} for German nouns, etc.) to display an entire headword line.
See [[#Further explanations for full_headword()]]
]==]
function export.full_headword(data)
-- Prevent data from being destructively modified.
local data = shallow_copy(data)
------------ 1. Basic checks for old-style (multi-arg) calling convention. ------------
if data.getCanonicalName then
error("In full_headword(), the first argument `data` needs to be a Lua object (table) of properties, not a language object")
end
if not data.lang or type(data.lang) ~= "table" or not data.lang.getCode then
error("In full_headword(), the first argument `data` needs to be a Lua object (table) and `data.lang` must be a language object")
end
if data.id and type(data.id) ~= "string" then
error("The id in the data table should be a string.")
end
------------ 2. Initialize pagename etc. ------------
local langcode = data.lang:getCode()
local full_langcode = data.lang:getFullCode()
local langname = data.lang:getCanonicalName()
local full_langname = data.lang:getFullName()
local raw_pagename, page = data.pagename
if raw_pagename and raw_pagename ~= (m_data or get_data()).pagename then -- for testing, doc pages, etc.
page = process_page(raw_pagename)
else
page = (m_data or get_data()).page
end
-- Check the namespace against the language type.
local namespace = page.namespace
if namespace == "" then
if data.lang:hasType("reconstructed") then
error("Entries in " .. langname .. " must be placed in the Reconstruction: namespace")
elseif data.lang:hasType("appendix-constructed") then
error("Entries in " .. langname .. " must be placed in the Appendix: namespace")
end
elseif namespace == "Citations" or namespace == "Thesaurus" then
error("Headword templates should not be used in the " .. namespace .. ": namespace.")
end
------------ 3. Initialize `data.heads` table; if old-style, convert to new-style. ------------
if type(data.heads) == "table" and type(data.heads[1]) == "table" then
-- new-style
if data.translits or data.transcriptions then
error("In full_headword(), if `data.heads` is new-style (array of head objects), `data.translits` and `data.transcriptions` cannot be given")
end
else
-- convert old-style `heads`, `translits` and `transcriptions` to new-style
local maxind = max(
init_and_find_maximum_index(data, "heads"),
init_and_find_maximum_index(data, "translits", true),
init_and_find_maximum_index(data, "transcriptions", true)
)
for i = 1, maxind do
data.heads[i] = {
term = data.heads[i],
tr = data.translits[i],
ts = data.transcriptions[i],
}
end
end
-- Make sure there's at least one head.
if not data.heads[1] then
data.heads[1] = {}
end
------------ 4. Initialize and validate `data.categories` and `data.whole_page_categories`, and determine `pos_category` if not given, and add basic categories. ------------
-- EXPERIMENTAL: see [[Wiktionary:Beer parlour/2024/June#Decluttering the altform mess]]
if data.altform then
data.noposcat = true
end
init_and_find_maximum_index(data, "categories")
init_and_find_maximum_index(data, "whole_page_categories")
local pos_category_already_present = false
if data.categories[1] then
local escaped_langname = pattern_escape(full_langname)
local matches_lang_pattern = "^" .. escaped_langname .. " "
for _, cat in ipairs(data.categories) do
-- Does the category begin with the language name? If not, tag it with a tracking category.
if not cat:find(matches_lang_pattern) then
-- [[Special:WhatLinksHere/Wiktionary:Tracking/headword/no lang category]]
-- [[Special:WhatLinksHere/Wiktionary:Tracking/headword/no lang category/LANGCODE]]
track("no lang category", data.lang)
end
end
-- If `pos_category` not given, try to infer it from the first specified category. If this doesn't work, we
-- throw an error below.
if not data.pos_category and data.categories[1]:find(matches_lang_pattern) then
data.pos_category = data.categories[1]:gsub(matches_lang_pattern, "")
-- Optimization to avoid inserting category already present.
pos_category_already_present = true
end
end
if not data.pos_category then
error("`data.pos_category` not specified and could not be inferred from the categories given in "
.. "`data.categories`. Either specify the plural part of speech in `data.pos_category` "
.. "(e.g. \"proper nouns\") or ensure that the first category in `data.categories` is formed from the "
.. "language's canonical name plus the plural part of speech (e.g. \"Norwegian Bokmål proper nouns\")."
)
end
-- Insert a category at the beginning for the part of speech unless it's already present or `data.noposcat` given.
if not pos_category_already_present and not data.noposcat then
local pos_category = data.pos_category .. " بە " .. full_langname
-- FIXME: [[User:Theknightwho]] Why is this special case here? Please add an explanatory comment.
if pos_category ~= "Translingual Han characters" then
insert(data.categories, 1, pos_category)
end
end
-- Try to determine whether the part of speech refers to a lemma or a non-lemma form; if we can figure this out,
-- add an appropriate category.
local postype = export.pos_lemma_or_nonlemma(data.pos_category)
if not postype then
-- We don't know what this category is, so tag it with a tracking category.
-- [[Special:WhatLinksHere/Wiktionary:Tracking/headword/unrecognized pos]]
-- [[Special:WhatLinksHere/Wiktionary:Tracking/headword/unrecognized pos/LANGCODE]]
track("unrecognized pos", data.lang)
-- [[Special:WhatLinksHere/Wiktionary:Tracking/headword/unrecognized pos/POS]]
-- [[Special:WhatLinksHere/Wiktionary:Tracking/headword/unrecognized pos/POS/LANGCODE]]
track("unrecognized pos/pos/" .. data.pos_category, data.lang)
elseif not data.noposcat then
insert(data.categories, 1, postype .. " بە " .. full_langname)
end
-- EXPERIMENTAL: see [[Wiktionary:Beer parlour/2024/June#Decluttering the altform mess]]
if data.altform then
insert(data.categories, 1, full_langname .. " alternative forms")
end
------------ 5. Create a default headword, and add links to multiword page names. ------------
-- Determine if term is reconstructed
local is_reconstructed = namespace == "Reconstruction" or data.lang:hasType("reconstructed")
-- Create a default headword based on the pagename, which is determined in
-- advance by the data module so that it only needs to be done once.
local default_head = page.pagename
-- Add links to multi-word page names when appropriate
if not (is_reconstructed or data.nolinkhead) then
local no_links = (m_data or get_data()).no_multiword_links
if not (no_links[langcode] or no_links[full_langcode]) and export.head_is_multiword(default_head) then
default_head = export.add_multiword_links(default_head, true)
end
end
if is_reconstructed then
default_head = "*" .. default_head
end
------------ 6. Fill in missing values in `data.heads`. ------------
-- True if any script among the headword scripts has spaces in it.
local any_script_has_spaces = false
-- True if any term has a redundant head= param.
local has_redundant_head_param = false
for _, head in ipairs(data.heads) do
------ 6a. If missing head, replace with default head.
if not head.term then
head.term = default_head
elseif head.term == default_head then
has_redundant_head_param = true
elseif head.term:find("^[!?]$") then
-- If explicit head= just consists of ! or ?, add it to the end of the default head.
head.term = default_head .. head.term
end
if is_reconstructed then
local head_term = head.term
if head_term:find("%[%[") then
head_term = remove_links(head_term)
end
if head_term:sub(1, 1) ~= "*" then
error("The headword '" .. head_term .. "' must begin with '*' to indicate that it is reconstructed.")
end
end
------ 6b. Try to detect the script(s) if not provided. If a per-head script is provided, that takes precedence,
------ otherwise fall back to the overall script if given. If neither given, autodetect the script.
local auto_sc = data.lang:findBestScript(head.term)
if (
auto_sc:getCode() == "None" and
find_best_script_without_lang(head.term):getCode() ~= "None"
) then
insert(data.categories, full_langname .. " terms in nonstandard scripts")
end
if not (head.sc or data.sc) then -- No script code given, so use autodetected script.
head.sc = auto_sc
else
if not head.sc then -- Overall script code given.
head.sc = data.sc
end
-- Track uses of sc parameter.
if head.sc:getCode() == auto_sc:getCode() then
insert(data.categories, full_langname .. " terms with redundant script codes")
else
insert(data.categories, full_langname .. " terms with non-redundant manual script codes")
end
end
-- If using a discouraged character sequence, add to maintenance category.
if head.sc:hasNormalizationFixes() == true then
local composed_head = toNFC(head.term)
if head.sc:fixDiscouragedSequences(composed_head) ~= composed_head then
insert(data.whole_page_categories, "Pages using discouraged character sequences")
end
end
any_script_has_spaces = any_script_has_spaces or head.sc:hasSpaces()
------ 6c. Create automatic transliterations for any non-Latin headwords without manual translit given
------ (provided automatic translit is available, e.g. not in Persian or Hebrew).
-- Make transliterations
head.tr_manual = nil
-- Try to generate a transliteration if necessary
if head.tr == "-" then
head.tr = nil
else
local notranslit = (m_data or get_data()).notranslit
if not (notranslit[langcode] or notranslit[full_langcode]) and head.sc:isTransliterated() then
head.tr_manual = not not head.tr
local text = head.term
if not data.lang:link_tr(head.sc) then
text = remove_links(text)
end
local automated_tr, tr_categories
automated_tr, head.tr_fail, tr_categories = data.lang:transliterate(text, head.sc)
if automated_tr or head.tr_fail then
local manual_tr = head.tr
if manual_tr then
if (remove_links(manual_tr) == remove_links(automated_tr)) and (not head.tr_fail) then
insert(data.categories, full_langname .. " terms with redundant transliterations")
elseif not head.tr_fail then
insert(data.categories, full_langname .. " terms with non-redundant manual transliterations")
end
end
if not manual_tr then
head.tr = automated_tr
extend(data.categories, tr_categories)
end
end
-- There is still no transliteration?
-- Add the entry to a cleanup category.
if not head.tr then
head.tr = "<small>transliteration needed</small>"
-- FIXME: No current support for 'Request for transliteration of Classical Persian terms' or similar.
-- Consider adding this support in [[Module:category tree/poscatboiler/data/entry maintenance]].
insert(data.categories, "Requests for transliteration of " .. full_langname .. " terms")
else
-- Otherwise, trim it.
head.tr = trim(head.tr)
end
end
end
-- Link to the transliteration entry for languages that require this.
if head.tr and data.lang:link_tr(head.sc) then
head.tr = full_link{
term = head.tr,
lang = data.lang,
sc = get_script("Latn"),
tr = "-"
}
end
end
------------ 7. Maybe tag the title with the appropriate script code, using the `display_title` mechanism. ------------
-- Assumes that the scripts in "toBeTagged" will never occur in the Reconstruction namespace.
-- (FIXME: Don't make assumptions like this, and if you need to do so, throw an error if the assumption is violated.)
-- Avoid tagging ASCII as Hani even when it is tagged as Hani in the headword, as in [[check]]. The check for ASCII
-- might need to be expanded to a check for any Latin characters and whitespace or punctuation.
local display_title
-- Where there are multiple headwords, use the script for the first. This assumes the first headword is similar to
-- the pagename, and that headwords that are in different scripts from the pagename aren't first. This seems to be
-- about the best we can do (alternatively we could potentially do script detection on the pagename).
local dt_script = data.heads[1].sc
local dt_script_code = dt_script:getCode()
local page_non_ascii = namespace == "" and not page.pagename:find("^[%z\1-\127]+$")
local unsupported_pagename, unsupported = page.full_raw_pagename:gsub("^Unsupported titles/", "")
if unsupported == 1 and page.unsupported_titles[unsupported_pagename] then
display_title = 'Unsupported titles/<span class="' .. dt_script_code .. '">' .. page.unsupported_titles[unsupported_pagename] .. '</span>'
elseif page_non_ascii and (m_data or get_data()).toBeTagged[dt_script_code]
or (dt_script_code == "Jpan" and (text_in_script(page.pagename, "Hira") or text_in_script(page.pagename, "Kana")))
or (dt_script_code == "Kore" and text_in_script(page.pagename, "Hang")) then
display_title = '<span class="' .. dt_script_code .. '">' .. page.full_raw_pagename .. '</span>'
-- Keep Han entries region-neutral in the display title.
elseif page_non_ascii and (dt_script_code == "Hant" or dt_script_code == "Hans") then
display_title = '<span class="Hani">' .. page.full_raw_pagename .. '</span>'
elseif namespace == "Reconstruction" then
local matched
display_title, matched = ugsub(
page.full_raw_pagename,
"^(Reconstruction:[^/]+/)(.+)$",
function(before, term)
return before .. tag_text(term, data.lang, dt_script)
end
)
if matched == 0 then
display_title = nil
end
end
-- FIXME: Generalize this.
-- If the current language uses ur-Arab (for Urdu, etc.), ku-Arab (Central Kurdish) or pa-Arab
-- (Shahmukhi, for Punjabi) and there's more than one language on the page, don't set the display title
-- because these three scripts display in Nastaliq and we don't want this for terms that also exist in other
-- languages that don't display in Nastaliq (e.g. Arabic or Persian) to display in Nastaliq. Because the word
-- "Urdu" occurs near the end of the alphabet, Urdu fonts tend to override the fonts of other languages.
-- FIXME: This is checking for more than one language on the page but instead needs to check if there are any
-- languages using scripts other than the ones just mentioned.
if (dt_script_code == "ur-Arab" or dt_script_code == "ku-Arab" or dt_script_code == "pa-Arab") and page.L2_list.n > 1 then
display_title = nil
end
if display_title then
mw.getCurrentFrame():callParserFunction(
"DISPLAYTITLE",
display_title
)
end
------------ 8. Insert additional categories. ------------
if data.force_cat_output then
-- [[Special:WhatLinksHere/Wiktionary:Tracking/headword/force cat output]]
track("force cat output")
end
if has_redundant_head_param then
if not data.no_redundant_head_cat then
insert(data.categories, full_langname .. " terms with redundant head parameter")
end
end
-- If the first head is multiword (after removing links), maybe insert into "LANG multiword terms".
if not data.nomultiwordcat and any_script_has_spaces and postype == "lemma" then
local no_multiword_cat = (m_data or get_data()).no_multiword_cat
if not (no_multiword_cat[langcode] or no_multiword_cat[full_langcode]) then
-- Check for spaces or hyphens, but exclude prefixes and suffixes.
-- Use the pagename, not the head= value, because the latter may have extra
-- junk in it, e.g. superscripted text that throws off the algorithm.
local no_hyphen = (m_data or get_data()).hyphen_not_multiword_sep
-- Exclude hyphens if the data module states that they should for this language.
local checkpattern = (no_hyphen[langcode] or no_hyphen[full_langcode]) and ".[%s፡]." or ".[%s%-፡]."
if umatch(page.pagename, checkpattern) then
insert(data.categories, full_langname .. " multiword terms")
end
end
end
if data.sccat then
for _, head in ipairs(data.heads) do
insert(data.categories, full_langname .. " " .. data.pos_category .. " in " ..
head.sc:getDisplayForm())
end
end
-- Reconstructed terms often use weird combinations of scripts and realistically aren't spelled so much as notated.
if namespace ~= "Reconstruction" then
-- Map from languages to a string containing the characters to ignore when considering whether a term has
-- multiple written scripts in it. Typically these are Greek or Cyrillic letters used for their phonetic
-- values.
local characters_to_ignore = {
["aaq"] = "α", -- Penobscot
["acy"] = "δθ", -- Cypriot Arabic
["anc"] = "γ", -- Ngas
["aou"] = "χ", -- A'ou
["awg"] = "β", -- Anguthimri
["bhp"] = "β", -- Bima
["byk"] = "θ", -- Biao
["cdy"] = "θ", -- Chadong
["clm"] = "χ", -- Klallam
["col"] = "χ", -- Colombia-Wenatchi
["coo"] = "χ", -- Comox; FIXME: others? E.g. Greek theta (θ)?
["ets"] = "θ", -- Yekhee
["gmw-gts"] = "χ", -- Gottscheerish
["hur"] = "θ", -- Halkomelem
["izh"] = "ь", -- Ingrian
["kic"] = "θ", -- Kickapoo
["lil"] = "χ", -- Lillooet
["mhz"] = "β", -- Mor (Austronesian)
["neg"]= "ӡ", -- Negidal (normally in Cyrillic)
["oui"] = "γβ", -- Old Uyghur: FIXME: others? E.g. Greek delta (δ)?
["pox"] = "χ", -- Polabian
["rom"] = "Θθ", -- Romani: International Standard; two different thetas???
["sah"] = "ь", -- Yakut (1929 - 1939 Latin spelling)
["sjw"] = "θ", -- Shawnee
["squ"] = "χ", -- Squamish
["str"] = "χθ", -- Saanich; uses two Greek letters
["twa"] = "χ", -- Twana
["yha"] = "θ", -- Baha
["za"] = "зч", -- Zhuang; 1957-1982 alphabet used two Cyrillic letters (as well as some others like
-- ƃ, ƅ, ƨ, ɯ and ɵ that look like Cyrillic or Greek but are actually Latin)
["zlw-slv"] = "χђћ", -- Slovincian; FIXME: χ is Greek, the other two are Cyrillic, but I'm not sure
-- the currect characters are being chosen in the entry names
["zng"] = "θ", -- Mang
}
-- Determine how many real scripts are found in the pagename, where we exclude symbols and such. We exclude
-- scripts whose `character_category` is false as well as Zmth (mathematical notation symbols), which has a
-- category of "Mathematical notation symbols". When counting scripts, we need to elide language-specific
-- variants because e.g. Beng and as-Beng have slightly different characters but we don't want to consider them
-- two different scripts (e.g. [[এৰ]] has two characters which are detected respectively as Beng and as-Beng).
local seen_scripts = {}
local num_seen_scripts = 0
local num_loops = 0
local canon_pagename = page.pagename
local ch_to_ignore = characters_to_ignore[full_langcode]
if ch_to_ignore then
canon_pagename = ugsub(canon_pagename, "[" .. ch_to_ignore .. "]", "")
end
while true do
if canon_pagename == "" or num_seen_scripts >= 2 or num_loops >= 10 then
break
end
-- Make sure we don't get into a loop checking the same script over and over again; happens with e.g. [[ᠪᡳ]]
num_loops = num_loops + 1
local pagename_script = find_best_script_without_lang(canon_pagename, "None only as last resort")
local script_chars = pagename_script.characters
if not script_chars then
-- we are stuck; this happens with None
break
end
local script_code = pagename_script:getCode()
local replaced
canon_pagename, replaced = ugsub(canon_pagename, "[" .. script_chars .. "]", "")
if (
replaced and
script_code ~= "Zmth" and
(script_data or get_script_data())[script_code] and
script_data[script_code].character_category ~= false
) then
script_code = script_code:gsub("^.-%-", "")
if not seen_scripts[script_code] then
seen_scripts[script_code] = true
num_seen_scripts = num_seen_scripts + 1
end
end
end
if num_seen_scripts > 1 then
insert(data.categories, full_langname .. " terms written in multiple scripts")
end
end
-- Categorise for unusual characters. Takes into account combining characters, so that we can categorise for characters with diacritics that aren't encoded as atomic characters (e.g. U̠). These can be in two formats: single combining characters (i.e. character + diacritic(s)) or double combining characters (i.e. character + diacritic(s) + character). Each can have any number of diacritics.
local standard = data.lang:getStandardCharacters()
if standard then
local function char_category(char)
local specials = {
["#"] = "number sign",
["("] = "parentheses",
[")"] = "parentheses",
["<"] = "angle brackets",
[">"] = "angle brackets",
["["] = "square brackets",
["]"] = "square brackets",
["_"] = "underscore",
["{"] = "braces",
["|"] = "vertical line",
["}"] = "braces",
["ß"] = "ẞ",
["\205\133"] = "", -- this is UTF-8 for U+0345 ( ͅ)
["\239\191\189"] = "replacement character",
}
char = toNFD(char)
:gsub(".[\128-\191]*", function(m)
local new_m = specials[m]
new_m = new_m or m:uupper()
return new_m
end)
return toNFC(char)
end
if full_langcode ~= "hi" and full_langcode ~= "lo" then
local standard_chars_scripts = {}
for _, head in ipairs(data.heads) do
standard_chars_scripts[head.sc:getCode()] = true
end
-- Iterate over the scripts, in case there is more than one (as they can have different sets of standard characters).
for code in pairs(standard_chars_scripts) do
local sc_standard = data.lang:getStandardCharacters(code)
if sc_standard then
if page.pagename_len > 1 then
local explode_standard = {}
local function explode(char)
explode_standard[char] = true
return ""
end
local sc_standard = ugsub(sc_standard, page.comb_chars.combined_double, explode)
sc_standard = ugsub(sc_standard,page.comb_chars.combined_single, explode)
:gsub(".[\128-\191]*", explode)
local num_cat_inserted
for char in pairs(page.explode_pagename) do
if not explode_standard[char] then
if char:find("[0-9]") then
if not num_cat_inserted then
insert(data.categories, full_langname .. " terms spelled with numbers")
num_cat_inserted = true
end
elseif ufind(char, page.emoji_pattern) then
insert(data.categories, full_langname .. " terms spelled with emoji")
else
local upper = char_category(char)
if not explode_standard[upper] then
char = upper
end
insert(data.categories, full_langname .. " terms spelled with " .. char)
end
end
end
end
-- If a diacritic doesn't appear in any of the standard characters, also categorise for it generally.
sc_standard = toNFD(sc_standard)
for diacritic in ugmatch(page.decompose_pagename, page.comb_chars.diacritics_single) do
if not umatch(sc_standard, diacritic) then
insert(data.categories, full_langname .. " terms spelled with ◌" .. diacritic)
end
end
for diacritic in ugmatch(page.decompose_pagename, page.comb_chars.diacritics_double) do
if not umatch(sc_standard, diacritic) then
insert(data.categories, full_langname .. " terms spelled with ◌" .. diacritic .. "◌")
end
end
end
end
-- Ancient Greek, Hindi and Lao handled the old way for now, as their standard chars still need to be converted to the new format (because there are a lot of them).
elseif ulen(page.pagename) ~= 1 then
for character in ugmatch(page.pagename, "([^" .. standard .. "])") do
local upper = char_category(character)
if not umatch(upper, "[" .. standard .. "]") then
character = upper
end
insert(data.categories, full_langname .. " terms spelled with " .. character)
end
end
end
if data.heads[1].sc:isSystem("alphabet") then
local pagename, i = page.pagename:ulower(), 2
while umatch(pagename, "(%a)" .. ("%1"):rep(i)) do
i = i + 1
insert(data.categories, full_langname .. " terms with " .. i .. " consecutive instances of the same letter")
end
end
-- Categorise for palindromes
if not data.nopalindromecat and namespace ~= "Reconstruction" and ulen(page.pagename) > 2
-- FIXME: Use of first script here seems hacky. What is the clean way of doing this in the presence of
-- multiple scripts?
and is_palindrome(page.pagename, data.lang, data.heads[1].sc) then
insert(data.categories, full_langname .. " palindromes")
end
if namespace == "" and not data.lang:hasType("reconstructed") then
for _, head in ipairs(data.heads) do
if page.full_raw_pagename ~= get_link_page(remove_links(head.term), data.lang, head.sc) then
-- [[Special:WhatLinksHere/Wiktionary:Tracking/headword/pagename spelling mismatch]]
-- [[Special:WhatLinksHere/Wiktionary:Tracking/headword/pagename spelling mismatch/LANGCODE]]
track("pagename spelling mismatch", data.lang)
break
end
end
end
-- Add to various maintenance categories.
export.maintenance_cats(page, data.lang, data.categories, data.whole_page_categories)
------------ 9. Format and return headwords, genders, inflections and categories. ------------
-- Format and return all the gathered information. This may add more categories (e.g. gender/number categories),
-- so make sure we do it before evaluating `data.categories`.
local text = '<span class="headword-line">' ..
format_headword(data) ..
format_headword_genders(data) ..
format_inflections(data) .. '</span>'
-- Language-specific categories.
local cats = format_categories(
data.categories, data.lang, data.sort_key, page.encoded_pagename,
data.force_cat_output or test_force_categories, data.heads[1].sc
)
-- Language-agnostic categories.
local whole_page_cats = format_categories(
data.whole_page_categories, nil, "-"
)
return text .. cats .. whole_page_cats
end
return export
fd062grgau1587nsbik8i2h5y0glc2y
داڕێژە:ckb-noun
10
120
36862
36851
2026-08-16T13:48:10Z
Ghybu
12
36862
wikitext
text/x-wiki
{{#invoke:checkparams|error}}<!-- Validate template parameters
-->{{head|ckb|ناو|sc=ku-Arab|sort={{{sort|}}}|head={{{head|}}}|tr={{{tr|}}}}}{{#if:{{{pl|}}}| (''plural'' '''{{l-self|ckb|accel=plural-form-of|{{{pl}}}|sc=ku-Arab}}''')}}<!--
--><noinclude>{{documentation}}</noinclude>
8meboehyh5tdhz34pswa6le5ia7k9xe
36875
36862
2026-08-16T23:37:12Z
Ghybu
12
36875
wikitext
text/x-wiki
{{#invoke:checkparams|error}}<!-- Validate template parameters
-->{{head|ckb|ناوەکان|sc=ku-Arab|sort={{{sort|}}}|head={{{head|}}}|tr={{{tr|}}}}}{{#if:{{{pl|}}}| (''plural'' '''{{l-self|ckb|accel=plural-form-of|{{{pl}}}|sc=ku-Arab}}''')}}<!--
--><noinclude>{{documentation}}</noinclude>
7akzecdf9d9x119kdp6vob19xlmqnxz
36887
36875
2026-08-17T00:24:36Z
Ghybu
12
pl
36887
wikitext
text/x-wiki
{{#invoke:checkparams|error}}<!-- Validate template parameters
-->{{head|ckb|ناوەکان|sc=ku-Arab|sort={{{sort|}}}|head={{{head|}}}|tr={{{tr|}}}<!--
-->|{{#if:{{{pl|}}}|  ''plural'' {{l-self|ckb|{{{pl}}}|sc=ku-Arab}}}}<!--
-->}}<!--
--><noinclude>{{documentation}}</noinclude>
d31o2a6t6hl1u31bczbm166p47xw4w0
36888
36887
2026-08-17T00:32:56Z
Ghybu
12
کۆ
36888
wikitext
text/x-wiki
{{#invoke:checkparams|error}}<!-- Validate template parameters
-->{{head|ckb|ناوەکان|sc=ku-Arab|sort={{{sort|}}}|head={{{head|}}}|tr={{{tr|}}}<!--
-->|{{#if:{{{pl|}}}|  ''کۆ'' {{l-self|ckb|{{{pl}}}|sc=ku-Arab}}}}<!--
-->}}<!--
--><noinclude>{{documentation}}</noinclude>
lfslc9pzpr7q97t9j2qqunbiue50zau
36889
36888
2026-08-17T00:35:16Z
Ghybu
12
36889
wikitext
text/x-wiki
{{#invoke:checkparams|error}}<!-- Validate template parameters
-->{{head|ckb|ناوەکان|sc=ku-Arab|sort={{{sort|}}}|head={{{head|}}}|tr={{{tr|}}}<!--
-->|{{#if:{{{pl|}}}|  ''کۆ'' {{l-self|ckb|{{{pl}}}|sc=ku-Arab}}}}<!--
-->}}<!--
--><noinclude>{{documentation}}</noinclude>
k77abcp38x4dzsgga4j3w7a8uxxwlnf
36892
36889
2026-08-17T01:48:38Z
Ghybu
12
36892
wikitext
text/x-wiki
{{#invoke:checkparams|error}}<!-- Validate template parameters
-->{{head|ckb|ناوەکان|head={{{head|}}}<!--
-->|tr={{{tr|}}}<!--
-->|{{#if:{{{pl|}}}|کۆ}}<!--
-->|{{{pl|}}}<!--
-->}}<!--
--><noinclude>{{documentation}}</noinclude>
38pjhfmhcaaoi8dx8tzicdvpamthtgy
36893
36892
2026-08-17T01:52:14Z
Ghybu
12
36893
wikitext
text/x-wiki
{{head|ckb|ناوەکان|head={{{head|}}}<!--
-->|tr={{{tr|}}}<!--
-->|{{#if:{{{pl|}}}|کۆ}}<!--
-->|{{{pl|}}}<!--
-->}}<!--
--><noinclude>{{documentation}}</noinclude>
lyuwcdbtp8exaczmwo8e2q63ndcybj9
تفاحة
0
4524
36898
36517
2026-08-17T01:56:53Z
Ghybu
12
/* عەرەبی */
36898
wikitext
text/x-wiki
== عەرەبی ==
=== وشەڕەتناسی ===
{{واڵا}}
===ناو===
{{head|ar|ناو|head=تُفَّاحَة|g=f}}
# [[سێو]]
oyxj75nmpajpxtkrn6gf7hutikhx9s5
سێو
0
5476
36891
36612
2026-08-17T01:32:37Z
Ghybu
12
/* ناو */کۆ
36891
wikitext
text/x-wiki
== کوردیی ناوەندی ==
[[File:Fresh ripe apples on a tree (54015357813).jpg|thumb|'''سێوی''' سوور]]
=== وشەڕەتناسی ===
{{واڵا}}
=== گۆکردن ===
* {{ckb-IPA}}
=== ناو ===
{{ckb-noun|pl=سێوەکان}}
#جۆرێکی [[میوە|میوەیە]]. سێ ڕەنگی هەیە: [[سەوز]] و [[سوور]] و [[زەرد]].
==== وەرگێڕانەکان ====
{{وەرگێڕان-سەر|میوەیە}}
* ئینگلیزی: {{t+|en|apple}}
* فەڕەنسی: {{t+|fr|pomme|f}}
* تورکی: {{t+|tr|elma}}
* عەرەبی: {{t+|ar|تُفَّاحَة}}
* فارسی: {{t+|fa|سیب|tr=sēb}}
{{وەرگێڕان-بن}}
kkv8obb9yogabot20bi5opu6ni5g42e
داڕێژە:ckb-adj
10
8905
36874
36735
2026-08-16T23:34:48Z
Ghybu
12
36874
wikitext
text/x-wiki
{{head|ckb|ھاوەڵناوەکان|head={{{head|}}}|tr={{{tr|}}}}}<!--
--><noinclude>{{documentation}}</noinclude>
7tknrygr4chtx5ok0834cgy87b7btp2
داڕێژە:ckb-verb
10
9170
36876
36729
2026-08-16T23:37:49Z
Ghybu
12
36876
wikitext
text/x-wiki
{{head|ckb|کارەکان|sort={{{sort|}}}|head={{{head|}}}<!--
-->|{{#if:{{{tr|}}}|{{#switch: {{{tr|}}}|trans = transitive|intrans = intransitive|both = transitive and intransitive}} }}<!--
-->|<!--
-->|{{#if:{{{prstem|}}}|present stem}}<!--
-->|{{{prstem|}}}<!--
-->|f2alt={{{prstem|}}}-<!--
-->|f2tr={{xlit|ckb|{{{prstem|}}}}}-<!--
-->|{{#if:{{{prstem2|}}}|or}}<!--
-->|{{{prstem2|}}}<!--
-->|f3alt={{{prstem|}}}-<!--
-->|f3tr={{xlit|ckb|{{{prstem2|}}}}}-<!--
-->}}<!--
--><noinclude>{{documentation}}</noinclude>
0v75ct49e4jz6q1kqun4s2ddv3pbf2a
مۆدیوول:kmr-headword
828
9176
36870
36732
2026-08-16T23:25:49Z
Ghybu
12
plural
36870
Scribunto
text/plain
local export = {}
local pos_functions = {}
local m_scripts = require("Module:scripts")
local en_utilities_module = "Module:en-utilities"
local lang = require("Module:languages").getByCode("kmr")
local langname = lang:getCanonicalName()
local ku_sc = m_scripts.getByCode("ku-Arab")
local PAGENAME = mw.loadData("Module:headword/data").pagename
local boolean = {type = "boolean"}
local list = {list = true}
local suffix_categories = {
["adjectives"] = true,
["adverbs"] = true,
["nouns"] = true,
["verbs"] = true,
}
local function track(page)
require("Module:debug").track("kmr-headword/" .. page)
return true
end
local function glossary_link(entry, text)
text = text or entry
return "[[Appendix:Glossary#" .. entry .. "|" .. text .. "]]"
end
local function process_arabic_spellings(spellings)
local inflection = {}
for _, spelling in ipairs(spellings) do
table.insert(inflection, {term = spelling, sc = ku_sc})
end
inflection.label = "Arabic spelling"
return inflection
end
-- The main entry point.
-- This is the only function that can be invoked from a template.
function export.show(frame)
local tracking_categories = {}
local poscat = frame.args[1]
or error("Plural part of speech e.g. 'nouns' has not been specified. Please pass parameter 1 to the module invocation.")
local params = {
["head"] = list,
["tr"] = {list = true, allow_holes = true},
["id"] = {},
["ar"] = list,
["suff"] = boolean,
["c"] = boolean,
}
if pos_functions[poscat] then
for key, val in pairs(pos_functions[poscat].params) do
params[key] = val
end
end
local parargs = frame:getParent().args
local args = require("Module:parameters").process(parargs, params)
local data = {
lang = lang,
pos_category = poscat,
categories = {},
heads = args["head"],
translits = args["tr"],
genders = {},
inflections = {},
id = args["id"],
categories = {}
}
if args["suff"] then
data.pos_category = "suffixes"
if suffix_categories[poscat] then
local singular_poscat = poscat:gsub("s$", "")
table.insert(data.categories, langname .. " " .. singular_poscat .. "-forming suffixes")
else
error("No category exists for suffixes forming " .. poscat .. ".")
end
end
if pos_functions[poscat] then
pos_functions[poscat].func(args, data, tracking_categories)
end
if #args["ar"] > 0 then
table.insert(data.inflections, process_arabic_spellings(args["ar"]))
end
return require("Module:headword").full_headword(data)
.. require("Module:utilities").format_categories(tracking_categories, lang)
end
pos_functions["کارەکان"] = {
params = {
[1] = {},
[2] = {},
},
func = function(args, data, tracking_categories)
if args[1] then
table.insert(data.inflections, {label = "third-person singular simple present", args[1], accel = {form = '3|s|pres'}})
end
if args[2] then
table.insert(data.inflections, {label = "past tense", args[2], accel = {form = 'past'}})
end
end
}
pos_functions["ھاوەڵناوەکان"] = {
params = {
[1] = {alias_of = "comparative", list = false},
["comparative"] = list, --comparative(s)
[2] = {alias_of = "superlative", list = false},
["superlative"] = list, --superlative(s)
["c"] = boolean,
},
func = function(args, data, tracking_categories)
if args.comparative[1] == "-" then
table.insert(data.inflections, {label = "not comparable"})
table.insert(data.categories, langname .. " uncomparable adjectives")
else
if #args.comparative > 0 then
args.comparative.label = glossary_link("comparative")
args.comparative.accel = {form = "comparative"}
table.insert(data.inflections, args.comparative)
else
if args["c"] then
table.insert(data.inflections, {label = glossary_link("comparative"), accel = {form = 'comparative'}, {term = PAGENAME ..'tir'}})
end
end
if #args.superlative > 0 then
args.superlative.label = glossary_link("superlative")
args.superlative.accel = {form = "superlative"}
table.insert(data.inflections, args.superlative)
else
if args["c"] then
table.insert(data.inflections, {label = glossary_link("superlative"), accel = {form = 'superlative'}, {term = '[[herî]] ' .. PAGENAME}, {term = '[[tewrî]] ' .. PAGENAME}})
end
end
end
end
}
local noun_params = {
[1] = {alias_of = "g", list = false},
["g"] = list, --gender(s)
[2] = {alias_of = "pl", list = false},
["pl"] = list, --plural(s)
["f"] = list, --feminine form(s)
["m"] = list, --masculine form(s)
}
local allowed_genders = {
["m"] = true,
["f"] = true,
["n"] = true,
["mf"] = true,
["mfbysense"] = true,
["m-p"] = true,
["f-p"] = true,
["n-p"] = true,
}
local function do_nouns(pos, args, data, tracking_categories)
local genders = {}
for _, g in ipairs(args.g) do
if not allowed_genders[g] then
error("Unrecognized gender: " .. g)
end
if g == "mf" then
table.insert(genders, "m")
table.insert(genders, "f")
else
table.insert(genders, g)
end
end
if #genders > 0 then
data.genders = genders
else
data.genders = {"?"}
end
local plpos = require(en_utilities_module).pluralize(pos)
-- Check for special plural signals
local mode = nil
if args.pl[1] == "?" or args.pl[1] == "!" or args.pl[1] == "-" or args.pl[1] == "-~" or args.pl[1] == "~" or args.pl[1] == "#" then
mode = args.pl[1]
table.remove(args.pl, 1) -- Remove the mode parameter
end
if mode == "?" then
-- Plural is unknown
table.insert(data.categories, langname .. " " .. plpos .. " with unknown or uncertain plurals")
elseif mode == "!" then
-- Plural is not attested
table.insert(data.inflections, {label = "plural not attested"})
table.insert(data.categories, langname .. " " .. plpos .. " with unattested plurals")
return
elseif mode == "-" then
-- Uncountable noun; may occasionally have a plural
table.insert(data.categories, langname .. " uncountable " .. plpos)
table.insert(data.inflections, {label = glossary_link("uncountable")})
elseif mode == "-~" then
table.insert(data.categories, langname .. " uncountable " .. plpos)
table.insert(data.inflections, {label = "usually " .. glossary_link("uncountable")})
elseif mode == "~" then
-- Mixed countable/uncountable noun, always has a plural
table.insert(data.inflections, {label = glossary_link("countable") .. " and " .. glossary_link("uncountable")})
table.insert(data.categories, langname .. " uncountable " .. plpos)
table.insert(data.categories, langname .. " countable " .. plpos)
elseif mode == "#" or pos == "noun" then
-- Countable nouns; the default for regular nouns but not proper nouns
if mode == "#" then
table.insert(data.inflections, {label = glossary_link("countable")})
end
-- Not enough nouns properly use - in the second argument
-- table.insert(data.categories, langname .. " countable " .. plpos)
end
if #args.pl > 0 then
args.pl.label = "plural"
args.pl.accel = {form = "p"}
table.insert(data.inflections, args.pl)
end
if #args.f > 0 then
args.f.label = "feminine"
table.insert(data.inflections, args.f)
end
if #args.m > 0 then
args.m.label = "masculine"
table.insert(data.inflections, args.m)
end
end
pos_functions["ناوەکان"] = {
params = noun_params,
func = function(args, data, tracking_categories)
return do_nouns("noun", args, data, tracking_categories)
end,
}
pos_functions["proper nouns"] = {
params = noun_params,
func = function(args, data, tracking_categories)
return do_nouns("proper noun", args, data, tracking_categories)
end,
}
return export
kgomcyn4d9z07jdqugjkxnrbkitqzm3
داڕێژە:kmr-noun
10
9178
36871
36722
2026-08-16T23:29:17Z
Ghybu
12
36871
wikitext
text/x-wiki
{{#invoke:kmr-headword|show|ناوەکان}}<!--
--><noinclude>{{documentation}}</noinclude>
ctjylaxfrwxeas47xybs08ztzyxxl4s
داڕێژە:kmr-adj
10
9179
36873
36731
2026-08-16T23:32:08Z
Ghybu
12
36873
wikitext
text/x-wiki
{{#invoke:kmr-headword|show|ھاوەڵناوەکان}}<!--
--><noinclude>{{documentation}}</noinclude>
s4trvcs3609yaco63orss4xpsqujq1b
داڕێژە:kmr-verb
10
9183
36872
36726
2026-08-16T23:31:00Z
Ghybu
12
36872
wikitext
text/x-wiki
{{#invoke:kmr-headword|show|کارەکان}}<!--
--><noinclude>{{documentation}}</noinclude>
44zerz6ryerazfk7vzbjmli48o6zf7z
داڕێژە:en-adj
10
10126
36884
36792
2026-08-16T23:50:34Z
Ghybu
12
36884
wikitext
text/x-wiki
{{#invoke:en-headword|show|ھاوەڵناوەکان}}<!--
--><noinclude>{{documentation}}</noinclude>
f9exzdjv1yaiapm5vcatv4bmduhftqx
داڕێژە:en-noun
10
10135
36880
36761
2026-08-16T23:46:17Z
Ghybu
12
36880
wikitext
text/x-wiki
{{#invoke:en-headword|show|ناوەکان}}<noinclude>{{documentation}}</noinclude>
mk17urhverw6b9bjkn4i1u4l1rfyx6w
داڕێژە:ar-noun
10
10547
36867
36774
2026-08-16T23:16:03Z
Ghybu
12
36867
wikitext
text/x-wiki
{{#invoke:ar-headword|show|ناوەکان}}<!--
--><noinclude>{{documentation}}</noinclude>
ol454gwhxtemf2wbjv2lc3g11a2juim
داڕێژە:ar-adj
10
10548
36868
30444
2026-08-16T23:19:41Z
Ghybu
12
ھاوەڵناوەکان
36868
wikitext
text/x-wiki
{{#invoke:ar-headword|show|ھاوەڵناوەکان}}<!--
--><noinclude>{{documentation}}</noinclude>
7h7flm55hrilxfz91e75hib4mrnl22r
مۆدیوول:ar-headword
828
10557
36866
36775
2026-08-16T23:14:33Z
Ghybu
12
36866
Scribunto
text/plain
-- Author: Benwing2; based on an early version by Rua
local ar_translit = require("Module:ar-translit")
local m_str_utils = require("Module:string utilities")
local list_to_set = require("Module:table").listToSet
local rfind = m_str_utils.find
local rsubn = m_str_utils.gsub
local u = m_str_utils.char
local lang = require("Module:languages").getByCode("ar")
local export = {}
local pos_functions = {}
-- diacritics
local A = u(0x064E) -- fatḥa
local AN = u(0x064B) -- fatḥatān (fatḥa tanwīn)
local U = u(0x064F) -- ḍamma
local UN = u(0x064C) -- ḍammatān (ḍamma tanwīn)
local I = u(0x0650) -- kasra
local IN = u(0x064D) -- kasratān (kasra tanwīn)
local SK = u(0x0652) -- sukūn = no vowel
local SH = u(0x0651) -- šadda = gemination of consonants
local DAGGER_ALIF = u(0x0670)
local DIACRITIC_ANY_BUT_SH = "[" .. A .. I .. U .. AN .. IN .. UN .. SK .. DAGGER_ALIF .. "]"
-- various letters and signs
local HAMZA = u(0x0621) -- hamza on the line (stand-alone hamza) = ء
local ALIF = u(0x0627) -- ʾalif = ا
local AMAQ = u(0x0649) -- ʾalif maqṣūra = ى
local TAM = u(0x0629) -- tāʾ marbūṭa = ة
-- common combinations
local UNU = "[" .. UN .. U .. "]"
-----------------------
-- Utility functions --
-----------------------
-- If Not Empty
local function ine(arg)
if arg == "" then
return nil
else
return arg
end
end
-- version of mw.ustring.gsub() that discards all but the first return value
local function rsub(term, foo, bar)
local retval = rsubn(term, foo, bar)
return retval
end
local function remove_links(text)
text = rsub(text, "%[%[[^|%]]*|", "")
text = rsub(text, "%[%[", "")
text = rsub(text, "%]%]", "")
return text
end
local function reorder_shadda(text)
-- shadda+short-vowel (including tanwīn vowels, i.e. -an -in -un) gets
-- replaced with short-vowel+shadda during NFC normalisation, which
-- MediaWiki does for all Unicode strings; however, it makes the
-- detection process inconvenient, so undo it. (For example, the tracking
-- code below would fail to detect the -un in سِتٌّ because the shadda
-- would come after the -un.)
text = rsub(text, "(" .. DIACRITIC_ANY_BUT_SH .. ")" .. SH, SH .. "%1")
return text
end
-- Tracking functions
local trackfn = require("Module:debug/track")
local function track(page)
trackfn("ar-headword/" .. page)
return true
end
--[==[
Examples of what you can find by looking at what links to the given
pages:
[[Special:WhatLinksHere/Wiktionary:Tracking/ar-headword/unvocalized]]
all unvocalized pages
[[Special:WhatLinksHere/Wiktionary:Tracking/ar-headword/unvocalized/pl]]
all unvocalized pages where the plural is unvocalized,
whether specified using pl=, pl2=, etc.
[[Special:WhatLinksHere/Wiktionary:Tracking/ar-headword/unvocalized/head]]
all unvocalized pages where the head is unvocalized
[[Special:WhatLinksHere/Wiktionary:Tracking/ar-headword/unvocalized/head/nouns]]
all nouns excluding proper nouns, collective nouns,
singulative nouns where the head is unvocalized
[[Special:WhatLinksHere/Wiktionary:Tracking/ar-headword/unvocalized/head/proper]]
nouns all proper nouns where the head is unvocalized
[[Special:WhatLinksHere/Wiktionary:Tracking/ar-headword/unvocalized/head/not]]
proper nouns all words that are not proper nouns
where the head is unvocalized
[[Special:WhatLinksHere/Wiktionary:Tracking/ar-headword/unvocalized/adjectives]]
all adjectives where any parameter is unvocalized;
currently only works for heads,
so equivalent to .../unvocalized/head/adjectives
[[Special:WhatLinksHere/Wiktionary:Tracking/ar-headword/unvocalized-empty-head]]
all pages with an empty head
[[Special:WhatLinksHere/Wiktionary:Tracking/ar-headword/unvocalized-manual-translit]]
all unvocalized pages with manual translit
[[Special:WhatLinksHere/Wiktionary:Tracking/ar-headword/unvocalized-manual-translit/head/nouns]]
all nouns where the head is unvocalized but has manual translit
[[Special:WhatLinksHere/Wiktionary:Tracking/ar-headword/unvocalized-no-translit]]
all unvocalized pages without manual translit
[[Special:WhatLinksHere/Wiktionary:Tracking/ar-headword/i3rab]]
all pages with any parameter containing i3rab
of either -un, -u, -a or -i
[[Special:WhatLinksHere/Wiktionary:Tracking/ar-headword/i3rab-un]]
all pages with any parameter containing an -un i3rab ending
[[Special:WhatLinksHere/Wiktionary:Tracking/ar-headword/i3rab-un/pl]]
all pages where a form specified using pl=, pl2=, etc.
contains an -un i3rab ending
[[Special:WhatLinksHere/Wiktionary:Tracking/ar-headword/i3rab-u/head]]
all pages with a head containing an -u i3rab ending
[[Special:WhatLinksHere/Wiktionary:Tracking/ar-headword/i3rab/head/proper]]
nouns (all proper nouns with a head containing i3rab
of either -un, -u, -a or -i)
In general, the format is one of the following:
Wiktionary:Tracking/ar-headword/FIRSTLEVEL
Wiktionary:Tracking/ar-headword/FIRSTLEVEL/ARGNAME
Wiktionary:Tracking/ar-headword/FIRSTLEVEL/POS
Wiktionary:Tracking/ar-headword/FIRSTLEVEL/ARGNAME/POS
FIRSTLEVEL can be one of "unvocalized", "unvocalized-empty-head" or its
opposite "unvocalized-specified", "unvocalized-manual-translit" or its
opposite "unvocalized-no-translit", "i3rab", "i3rab-un", "i3rab-u",
"i3rab-a", or "i3rab-i".
ARGNAME is either "head" or an argument such as "pl", "f", "cons", etc.
This automatically includes arguments specified as head2=, pl3=, etc.
POS is a part of speech, lowercase and pluralized, e.g. "nouns",
"adjectives", "proper nouns", "collective nouns", etc. or
"not proper nouns", which includes all parts of speech but proper nouns.
]==]
local function track_form(argname, form, translit, pos)
form = reorder_shadda(remove_links(form))
function dotrack(page)
track(page)
track(page .. "/" .. argname)
if pos then
track(page .. "/" .. pos)
track(page .. "/" .. argname .. "/" .. pos)
if pos ~= "proper nouns" then
track(page .. "/not proper nouns")
track(page .. "/" .. argname .. "/not proper nouns")
end
end
end
function track_i3rab(arabic, tr)
if rfind(form, arabic .. "$") then
dotrack("i3rab")
dotrack("i3rab-" .. tr)
end
end
track_i3rab(UN, "un")
track_i3rab(U, "u")
track_i3rab(A, "a")
track_i3rab(I, "i")
if form == "" or not (lang:transliterate(form)) then
dotrack("unvocalized")
if form == "" then
dotrack("unvocalized-empty-head")
else
dotrack("unvocalized-specified")
end
if translit then
dotrack("unvocalized-manual-translit")
else
dotrack("unvocalized-no-translit")
end
end
end
-- The main entry point.
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 = {
[1] = {list = "head", disallow_holes = true},
["tr"] = {list = true, allow_holes = true},
["id"] = {},
["nolinkhead"] = {type = "boolean"},
["json"] = {type = "boolean"},
["pagename"] = {}, -- 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 data = {
lang = lang,
pos_category = poscat,
categories = {},
heads = args[1],
translits = args.tr,
genders = {},
inflections = {enable_auto_translit = true},
pagename = pagename,
id = args.id,
sort_key = args.sort,
force_cat_output = force_cat,
}
local irreg_translit = false
for i = 1, #args[1] do
if ar_translit.irregular_translit(args[1][i], args.tr[i]) then
irreg_translit = true
break
end
end
if irreg_translit then
table.insert(data.categories, lang:getCanonicalName() .. " terms with irregular pronunciations")
end
if pos_functions[poscat] then
pos_functions[poscat].func(args, data)
end
if args.json then
return require("Module:JSON").toJSON(data)
end
return require("Module:headword").full_headword(data)
end
-- Get a list of inflections. See handle_infl() for meaning of ARGS and ARGPREF.
local function getargs(args, argpref)
local forms = {}
for i, form in ipairs(args[argpref]) do
local translit = args[argpref .. "tr"][i]
local gender = args[argpref .. "g"][i]
local gender2 = args[argpref .. "g2"][i]
local genderlist = (gender or gender2) and { gender, gender2 } or nil
-- FIXME, do we need this?
track_form(argpref, form, translit)
table.insert(forms, { term = form, translit = translit, genders = genderlist })
end
return forms
end
local function add_infl_params(params, argpref, defgender)
params[argpref] = {list = true, disallow_holes = true}
params[argpref .. "\1tr"] = {list = true, allow_holes = true}
params[argpref .. "\1g"] = {list = true, default = defgender}
params[argpref .. "\1g2"] = {list = true}
end
-- Get a list of inflections from the arguments in ARGS based on argument
-- prefix ARGPREF (e.g. "pl" to snarf arguments called "pl", "pl2", etc.,
-- along with "pltr", "pl2tr", etc. and optional gender(s) "plg", "plg2",
-- "pl2g", "pl2g2", "pl3g", "pl3g2", etc.). Label with LABEL (e.g. "plural"),
-- which will appear in the headword. Insert into inflections list
-- INFLS. Optional DEFGENDER is default gender to insert if gender
-- isn't given; otherwise, no gender is inserted. (This is used for
-- singulative forms of collective nouns, and collective forms of singulative
-- nouns, which have different gender from the base form(s).)
local function handle_infl(args, data, argpref, label, generate_default)
local newinfls = getargs(args, argpref)
if #newinfls == 0 and generate_default then
newinfls = {{term = "+"}}
end
if generate_default then
local saw_plus = false
for _, newinfl in ipairs(newinfls) do
if newinfl.term == "+" then
saw_plus = true
break
end
end
if saw_plus then
local newnewinfls = {}
for _, newinfl in ipairs(newinfls) do
if newinfl.term == "+" then
local definfls = generate_default(args, data)
for _, definfl in ipairs(definfls) do
table.insert(newnewinfls, definfl)
end
else
table.insert(newnewinfls, newinfl)
end
end
newinfls = newnewinfls
end
end
if #newinfls > 0 then
newinfls.label = label
table.insert(data.inflections, newinfls)
end
end
local function add_all_infl_params(params, argpref)
if argpref ~= "" then
add_infl_params(params, argpref)
end
add_infl_params(params, argpref .. "cons")
add_infl_params(params, argpref .. "def")
add_infl_params(params, argpref .. "obl")
add_infl_params(params, argpref .. "inf")
end
-- Handle a basic inflection (e.g. plural, feminine) along with the construct,
-- definite and oblique variants of this inflection. Can also handle the base
-- construct/definite/oblique variants if both ARGPREF and LABEL are given
-- as blank strings. If ARGPREF is blank, skip the base inflection.
local function handle_all_infl(args, data, argpref, label, generate_default)
if argpref ~= "" then
handle_infl(args, data, argpref, label, generate_default)
end
local labelsp = label == "" and "" or label .. " "
handle_infl(args, data, argpref .. "cons", labelsp .. "construct state")
handle_infl(args, data, argpref .. "def", labelsp .. "definite state")
handle_infl(args, data, argpref .. "obl", labelsp .. "oblique")
handle_infl(args, data, argpref .. "inf", labelsp .. "informal")
end
-- Handle the case where pl=-, indicating an uncountable noun.
local function handle_noun_plural(args, data)
if args.pl[1] == "-" then
table.insert(data.inflections, { label = "usually [[Appendix:Glossary#uncountable|uncountable]]" })
table.insert(data.categories, lang:getCanonicalName() .. " uncountable nouns")
if args.pauc and #args.pauc > 0 then
error("Can't specify paucals when pl=-")
end
else
handle_all_infl(args, data, "pl", "plural")
end
end
local valid_bare_genders = {false, "m", "f", "mfbysense", "mfequiv"}
local valid_bare_numbers = {false, "d", "p"}
local valid_bare_animacies = {false, "pr", "np"}
local valid_genders = {}
for _, gender in ipairs(valid_bare_genders) do
for _, number in ipairs(valid_bare_numbers) do
for _, animacy in ipairs(valid_bare_animacies) do
local parts = {}
local function ins_part(part)
if part then
table.insert(parts, part)
end
end
ins_part(gender)
ins_part(number)
ins_part(animacy)
local full_gender = table.concat(parts, "-")
valid_genders[full_gender == "" and "?" or full_gender] = true
end
end
end
local function is_masc_sg(g)
return g == "m" or g == "m-pr" or g == "m-np"
end
local function is_fem_sg(g)
return g == "f" or g == "f-pr" or g == "f-np"
end
local function add_gender_params(params, default)
params[2] = {list = "g", default = default or "?"}
end
-- Handle gender in params 2=, g2=, etc., inserting into `data.genders`. Also, if a lemma, insert categories into
-- `data.categories` if the gender is unexpected for the form of the noun. (Note: If there are multiple genders,
-- [[Module:gender and number]] will automatically insert 'Arabic POS with multiple genders'.)
local function handle_gender(args, data, nonlemma)
for _, g in ipairs(args[2]) do
if valid_genders[g] then
table.insert(data.genders, g)
else
error("Unrecognized gender: " .. g)
end
end
if nonlemma then
return
end
if #args[2] == 1 then
local g = args[2][1]
if is_masc_sg(g) or is_fem_sg(g) then
local head = args.head
if head then
head = rsub(reorder_shadda(remove_links(head)), UNU .. "?$", "")
local ends_with_tam = rfind(head, "^[^ ]*" .. TAM .. "$") or
rfind(head, "^[^ ]*" .. TAM .. " ")
if is_masc_sg(g) and ends_with_tam then
table.insert(data.categories, lang:getCanonicalName() .. " masculine terms with feminine ending")
elseif is_fem_sg(g) and not ends_with_tam and
not rfind(head, "[" .. ALIF .. AMAQ .. "]$") and
not rfind(head, ALIF .. HAMZA .. "$") then
table.insert(data.categories, lang:getCanonicalName() .. " feminine terms lacking feminine ending")
end
end
end
end
end
-- Part-of-speech functions
local adj_inflections = {
{pref = "", label = ""}, -- handle cons, def, obl, inf
{pref = "f", label = "feminine"},
{pref = "d", label = "masculine dual"},
{pref = "fd", label = "feminine dual"},
{pref = "cpl", label = "common plural"},
{pref = "pl", label = "masculine plural"},
{pref = "fpl", label = "feminine plural"},
}
local function create_infl_list_params(infl_list)
params = {}
for _, infl in ipairs(infl_list) do
if infl.basic then
add_infl_params(params, infl.pref)
else
add_all_infl_params(params, infl.pref)
end
end
return params
end
local function handle_infl_list_args(args, data, infl_list)
for _, infl in ipairs(infl_list) do
if infl.handle then
infl.handle(args, data)
elseif infl.basic then
handle_infl(args, data, infl.pref, infl.label, infl.generate_default)
else
handle_all_infl(args, data, infl.pref, infl.label, infl.generate_default)
end
end
end
pos_functions["adjectives"] = {
params = (function()
local params = create_infl_list_params(adj_inflections)
add_infl_params(params, "el")
return params
end)(),
func = function(args, data)
handle_infl_list_args(args, data, adj_inflections)
handle_infl(args, data, "el", "elative")
end
}
local function make_nisba_default(ending, endingtr)
return function(args, data)
local heads = data.heads
if #heads == 0 then
heads = {data.pagename}
end
local forms = {}
for i = 1, #heads do
local tr = data.translits[i]
table.insert(forms, {term = heads[i] .. ending, translit = tr and tr .. endingtr or nil})
end
return forms
end
end
local nisba_adj_inflections = {
{pref = "", label = ""}, -- handle cons, def, obl, inf
{pref = "f", label = "feminine", generate_default = make_nisba_default(A .. "ة", "a")},
{pref = "d", label = "masculine dual"},
{pref = "fd", label = "feminine dual"},
{pref = "cpl", label = "common plural"},
{pref = "pl", label = "masculine plural", generate_default = make_nisba_default(U .. "ونَ", "ūna")},
{pref = "fpl", label = "feminine plural", generate_default = make_nisba_default(A .. "ات", "āt")},
}
pos_functions["nisba adjectives"] = {
params = (function()
return create_infl_list_params(nisba_adj_inflections)
end)(),
func = function(args, data)
data.pos_category = "adjectives"
handle_infl_list_args(args, data, nisba_adj_inflections)
end
}
local sing_coll_noun_inflections = {
{pref = "", label = ""}, -- handle cons, def, obl, inf
{pref = "d", label = "dual"},
{pref = "pl", label = "plural", handle = handle_noun_plural},
{pref = "pauc", label = "paucal"},
}
local function handle_sing_coll_noun_infls(args, data, otherinfl, otherlabel)
handle_gender(args, data)
-- Handle sing= (corresponding singulative noun) or coll= (corresponding collective noun) and their gender
handle_infl(args, data, otherinfl, otherlabel)
handle_infl_list_args(args, data, sing_coll_noun_inflections)
end
local function get_sing_coll_noun_params(defgender, otherinfl, othergender)
local params = create_infl_list_params(sing_coll_noun_inflections)
add_gender_params(params, defgender)
add_infl_params(params, otherinfl, othergender)
return params
end
pos_functions["collective nouns"] = {
params = get_sing_coll_noun_params("m", "sing", "f"),
func = function(args, data)
data.pos_category = "nouns"
table.insert(data.categories, lang:getCanonicalName() .. " collective nouns")
table.insert(data.inflections, { label = "collective" })
handle_sing_coll_noun_infls(args, data, "sing", "singulative")
end
}
pos_functions["singulative nouns"] = {
params = get_sing_coll_noun_params("f", "coll", "m"),
func = function(args, data)
data.pos_category = "nouns"
table.insert(data.categories, lang:getCanonicalName() .. " singulative nouns")
table.insert(data.inflections, { label = "singulative" })
handle_sing_coll_noun_infls(args, data, "coll", "collective")
end
}
local noun_inflections = {
{pref = "", label = ""}, -- handle cons, def, obl, inf
{pref = "d", label = "dual"},
{pref = "pl", label = "plural", handle = handle_noun_plural},
{pref = "pauc", label = "paucal"},
{pref = "f", label = "feminine"},
{pref = "m", label = "masculine"},
}
local function get_noun_params()
local params = create_infl_list_params(noun_inflections)
add_gender_params(params)
return params
end
local function handle_noun_infls(args, data)
handle_gender(args, data)
handle_infl_list_args(args, data, noun_inflections)
end
pos_functions["ناوەکان"] = {
params = get_noun_params(),
func = handle_noun_infls,
}
-- FIXME: Do numerals really behave almost as nouns? They vary by masc/fem.
pos_functions["numerals"] = {
params = get_noun_params(),
func = function(args, data)
table.insert(data.categories, lang:getCanonicalName() .. " cardinal numbers")
handle_noun_infls(args, data)
end
}
pos_functions["proper nouns"] = {
params = get_noun_params(),
func = handle_noun_infls,
}
local pronoun_inflections = {
{pref = "", label = ""}, -- handle cons, def, obl, inf
{pref = "d", label = "dual"},
{pref = "pl", label = "plural", handle = handle_noun_plural},
{pref = "f", label = "feminine"},
}
local function get_pronoun_params()
local params = create_infl_list_params(pronoun_inflections)
add_gender_params(params)
return params
end
pos_functions["pronouns"] = {
params = get_pronoun_params(),
func = function(args, data)
handle_gender(args, data)
handle_infl_list_args(args, data, pronoun_inflections)
end
}
local function get_gender_only_params(default)
local params = {}
add_gender_params(params, default)
return params
end
pos_functions["noun plural forms"] = {
params = (function()
local params = {}
add_gender_params(params, "p")
add_infl_params(params, "cons")
return params
end)(),
func = function(args, data)
data.pos_category = "noun forms"
handle_gender(args, data, "nonlemma")
handle_infl(args, data, "cons", "construct state")
end
}
pos_functions["adjective feminine forms"] = {
params = get_gender_only_params("f"),
func = function(args, data)
data.pos_category = "adjective feminine forms"
handle_gender(args, data, "nonlemma")
end
}
pos_functions["noun dual forms"] = {
params = get_gender_only_params("m-d"),
func = function(args, data)
data.pos_category = "noun forms"
handle_gender(args, data, "nonlemma")
end
}
pos_functions["adjective plural forms"] = {
params = get_gender_only_params("m-p"),
func = function(args, data)
data.pos_category = "adjective forms"
handle_gender(args, data, "nonlemma")
end
}
pos_functions["adjective dual forms"] = {
params = get_gender_only_params("m-p"),
func = function(args, data)
data.pos_category = "adjective forms"
handle_gender(args, data, "m-d", "nonlemma")
end
}
pos_functions["noun forms"] = {
params = get_gender_only_params(),
func = function(args, data)
handle_gender(args, data, nil, "nonlemma")
end
}
local valid_forms = list_to_set(
{ "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X", "XI", "XII",
"XIII", "XIV", "XV", "Iq", "IIq", "IIIq", "IVq" })
local function handle_conj_form(args, data)
local form = args[2]
if form then
if not valid_forms[form] then
error("Invalid verb conjugation form " .. form)
end
table.insert(data.inflections, { label = '[[Appendix:Arabic verbs#Form ' .. form .. '|form ' .. form .. ']]' })
end
end
pos_functions["verb forms"] = {
params = {
[2] = {},
},
func = function(args, data)
handle_conj_form(args, data)
end
}
local function get_participle_params()
local params = create_infl_list_params(adj_inflections)
params[2] = {}
return params
end
pos_functions["active participles"] = {
params = get_participle_params(),
func = function(args, data)
data.pos_category = "participles"
table.insert(data.categories, lang:getCanonicalName() .. " active participles")
handle_conj_form(args, data)
handle_infl_list_args(args, data, adj_inflections)
end
}
pos_functions["passive participles"] = {
params = get_participle_params(),
func = function(args, data)
data.pos_category = "participles"
table.insert(data.categories, lang:getCanonicalName() .. " passive participles")
handle_conj_form(args, data)
handle_infl_list_args(args, data, adj_inflections)
end
}
return export
qav5kpwibj0iu974o5h7kna6volpl1j
36869
36866
2026-08-16T23:20:27Z
Ghybu
12
ھاوەڵناوەکان (adjectives)
36869
Scribunto
text/plain
-- Author: Benwing2; based on an early version by Rua
local ar_translit = require("Module:ar-translit")
local m_str_utils = require("Module:string utilities")
local list_to_set = require("Module:table").listToSet
local rfind = m_str_utils.find
local rsubn = m_str_utils.gsub
local u = m_str_utils.char
local lang = require("Module:languages").getByCode("ar")
local export = {}
local pos_functions = {}
-- diacritics
local A = u(0x064E) -- fatḥa
local AN = u(0x064B) -- fatḥatān (fatḥa tanwīn)
local U = u(0x064F) -- ḍamma
local UN = u(0x064C) -- ḍammatān (ḍamma tanwīn)
local I = u(0x0650) -- kasra
local IN = u(0x064D) -- kasratān (kasra tanwīn)
local SK = u(0x0652) -- sukūn = no vowel
local SH = u(0x0651) -- šadda = gemination of consonants
local DAGGER_ALIF = u(0x0670)
local DIACRITIC_ANY_BUT_SH = "[" .. A .. I .. U .. AN .. IN .. UN .. SK .. DAGGER_ALIF .. "]"
-- various letters and signs
local HAMZA = u(0x0621) -- hamza on the line (stand-alone hamza) = ء
local ALIF = u(0x0627) -- ʾalif = ا
local AMAQ = u(0x0649) -- ʾalif maqṣūra = ى
local TAM = u(0x0629) -- tāʾ marbūṭa = ة
-- common combinations
local UNU = "[" .. UN .. U .. "]"
-----------------------
-- Utility functions --
-----------------------
-- If Not Empty
local function ine(arg)
if arg == "" then
return nil
else
return arg
end
end
-- version of mw.ustring.gsub() that discards all but the first return value
local function rsub(term, foo, bar)
local retval = rsubn(term, foo, bar)
return retval
end
local function remove_links(text)
text = rsub(text, "%[%[[^|%]]*|", "")
text = rsub(text, "%[%[", "")
text = rsub(text, "%]%]", "")
return text
end
local function reorder_shadda(text)
-- shadda+short-vowel (including tanwīn vowels, i.e. -an -in -un) gets
-- replaced with short-vowel+shadda during NFC normalisation, which
-- MediaWiki does for all Unicode strings; however, it makes the
-- detection process inconvenient, so undo it. (For example, the tracking
-- code below would fail to detect the -un in سِتٌّ because the shadda
-- would come after the -un.)
text = rsub(text, "(" .. DIACRITIC_ANY_BUT_SH .. ")" .. SH, SH .. "%1")
return text
end
-- Tracking functions
local trackfn = require("Module:debug/track")
local function track(page)
trackfn("ar-headword/" .. page)
return true
end
--[==[
Examples of what you can find by looking at what links to the given
pages:
[[Special:WhatLinksHere/Wiktionary:Tracking/ar-headword/unvocalized]]
all unvocalized pages
[[Special:WhatLinksHere/Wiktionary:Tracking/ar-headword/unvocalized/pl]]
all unvocalized pages where the plural is unvocalized,
whether specified using pl=, pl2=, etc.
[[Special:WhatLinksHere/Wiktionary:Tracking/ar-headword/unvocalized/head]]
all unvocalized pages where the head is unvocalized
[[Special:WhatLinksHere/Wiktionary:Tracking/ar-headword/unvocalized/head/nouns]]
all nouns excluding proper nouns, collective nouns,
singulative nouns where the head is unvocalized
[[Special:WhatLinksHere/Wiktionary:Tracking/ar-headword/unvocalized/head/proper]]
nouns all proper nouns where the head is unvocalized
[[Special:WhatLinksHere/Wiktionary:Tracking/ar-headword/unvocalized/head/not]]
proper nouns all words that are not proper nouns
where the head is unvocalized
[[Special:WhatLinksHere/Wiktionary:Tracking/ar-headword/unvocalized/adjectives]]
all adjectives where any parameter is unvocalized;
currently only works for heads,
so equivalent to .../unvocalized/head/adjectives
[[Special:WhatLinksHere/Wiktionary:Tracking/ar-headword/unvocalized-empty-head]]
all pages with an empty head
[[Special:WhatLinksHere/Wiktionary:Tracking/ar-headword/unvocalized-manual-translit]]
all unvocalized pages with manual translit
[[Special:WhatLinksHere/Wiktionary:Tracking/ar-headword/unvocalized-manual-translit/head/nouns]]
all nouns where the head is unvocalized but has manual translit
[[Special:WhatLinksHere/Wiktionary:Tracking/ar-headword/unvocalized-no-translit]]
all unvocalized pages without manual translit
[[Special:WhatLinksHere/Wiktionary:Tracking/ar-headword/i3rab]]
all pages with any parameter containing i3rab
of either -un, -u, -a or -i
[[Special:WhatLinksHere/Wiktionary:Tracking/ar-headword/i3rab-un]]
all pages with any parameter containing an -un i3rab ending
[[Special:WhatLinksHere/Wiktionary:Tracking/ar-headword/i3rab-un/pl]]
all pages where a form specified using pl=, pl2=, etc.
contains an -un i3rab ending
[[Special:WhatLinksHere/Wiktionary:Tracking/ar-headword/i3rab-u/head]]
all pages with a head containing an -u i3rab ending
[[Special:WhatLinksHere/Wiktionary:Tracking/ar-headword/i3rab/head/proper]]
nouns (all proper nouns with a head containing i3rab
of either -un, -u, -a or -i)
In general, the format is one of the following:
Wiktionary:Tracking/ar-headword/FIRSTLEVEL
Wiktionary:Tracking/ar-headword/FIRSTLEVEL/ARGNAME
Wiktionary:Tracking/ar-headword/FIRSTLEVEL/POS
Wiktionary:Tracking/ar-headword/FIRSTLEVEL/ARGNAME/POS
FIRSTLEVEL can be one of "unvocalized", "unvocalized-empty-head" or its
opposite "unvocalized-specified", "unvocalized-manual-translit" or its
opposite "unvocalized-no-translit", "i3rab", "i3rab-un", "i3rab-u",
"i3rab-a", or "i3rab-i".
ARGNAME is either "head" or an argument such as "pl", "f", "cons", etc.
This automatically includes arguments specified as head2=, pl3=, etc.
POS is a part of speech, lowercase and pluralized, e.g. "nouns",
"adjectives", "proper nouns", "collective nouns", etc. or
"not proper nouns", which includes all parts of speech but proper nouns.
]==]
local function track_form(argname, form, translit, pos)
form = reorder_shadda(remove_links(form))
function dotrack(page)
track(page)
track(page .. "/" .. argname)
if pos then
track(page .. "/" .. pos)
track(page .. "/" .. argname .. "/" .. pos)
if pos ~= "proper nouns" then
track(page .. "/not proper nouns")
track(page .. "/" .. argname .. "/not proper nouns")
end
end
end
function track_i3rab(arabic, tr)
if rfind(form, arabic .. "$") then
dotrack("i3rab")
dotrack("i3rab-" .. tr)
end
end
track_i3rab(UN, "un")
track_i3rab(U, "u")
track_i3rab(A, "a")
track_i3rab(I, "i")
if form == "" or not (lang:transliterate(form)) then
dotrack("unvocalized")
if form == "" then
dotrack("unvocalized-empty-head")
else
dotrack("unvocalized-specified")
end
if translit then
dotrack("unvocalized-manual-translit")
else
dotrack("unvocalized-no-translit")
end
end
end
-- The main entry point.
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 = {
[1] = {list = "head", disallow_holes = true},
["tr"] = {list = true, allow_holes = true},
["id"] = {},
["nolinkhead"] = {type = "boolean"},
["json"] = {type = "boolean"},
["pagename"] = {}, -- 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 data = {
lang = lang,
pos_category = poscat,
categories = {},
heads = args[1],
translits = args.tr,
genders = {},
inflections = {enable_auto_translit = true},
pagename = pagename,
id = args.id,
sort_key = args.sort,
force_cat_output = force_cat,
}
local irreg_translit = false
for i = 1, #args[1] do
if ar_translit.irregular_translit(args[1][i], args.tr[i]) then
irreg_translit = true
break
end
end
if irreg_translit then
table.insert(data.categories, lang:getCanonicalName() .. " terms with irregular pronunciations")
end
if pos_functions[poscat] then
pos_functions[poscat].func(args, data)
end
if args.json then
return require("Module:JSON").toJSON(data)
end
return require("Module:headword").full_headword(data)
end
-- Get a list of inflections. See handle_infl() for meaning of ARGS and ARGPREF.
local function getargs(args, argpref)
local forms = {}
for i, form in ipairs(args[argpref]) do
local translit = args[argpref .. "tr"][i]
local gender = args[argpref .. "g"][i]
local gender2 = args[argpref .. "g2"][i]
local genderlist = (gender or gender2) and { gender, gender2 } or nil
-- FIXME, do we need this?
track_form(argpref, form, translit)
table.insert(forms, { term = form, translit = translit, genders = genderlist })
end
return forms
end
local function add_infl_params(params, argpref, defgender)
params[argpref] = {list = true, disallow_holes = true}
params[argpref .. "\1tr"] = {list = true, allow_holes = true}
params[argpref .. "\1g"] = {list = true, default = defgender}
params[argpref .. "\1g2"] = {list = true}
end
-- Get a list of inflections from the arguments in ARGS based on argument
-- prefix ARGPREF (e.g. "pl" to snarf arguments called "pl", "pl2", etc.,
-- along with "pltr", "pl2tr", etc. and optional gender(s) "plg", "plg2",
-- "pl2g", "pl2g2", "pl3g", "pl3g2", etc.). Label with LABEL (e.g. "plural"),
-- which will appear in the headword. Insert into inflections list
-- INFLS. Optional DEFGENDER is default gender to insert if gender
-- isn't given; otherwise, no gender is inserted. (This is used for
-- singulative forms of collective nouns, and collective forms of singulative
-- nouns, which have different gender from the base form(s).)
local function handle_infl(args, data, argpref, label, generate_default)
local newinfls = getargs(args, argpref)
if #newinfls == 0 and generate_default then
newinfls = {{term = "+"}}
end
if generate_default then
local saw_plus = false
for _, newinfl in ipairs(newinfls) do
if newinfl.term == "+" then
saw_plus = true
break
end
end
if saw_plus then
local newnewinfls = {}
for _, newinfl in ipairs(newinfls) do
if newinfl.term == "+" then
local definfls = generate_default(args, data)
for _, definfl in ipairs(definfls) do
table.insert(newnewinfls, definfl)
end
else
table.insert(newnewinfls, newinfl)
end
end
newinfls = newnewinfls
end
end
if #newinfls > 0 then
newinfls.label = label
table.insert(data.inflections, newinfls)
end
end
local function add_all_infl_params(params, argpref)
if argpref ~= "" then
add_infl_params(params, argpref)
end
add_infl_params(params, argpref .. "cons")
add_infl_params(params, argpref .. "def")
add_infl_params(params, argpref .. "obl")
add_infl_params(params, argpref .. "inf")
end
-- Handle a basic inflection (e.g. plural, feminine) along with the construct,
-- definite and oblique variants of this inflection. Can also handle the base
-- construct/definite/oblique variants if both ARGPREF and LABEL are given
-- as blank strings. If ARGPREF is blank, skip the base inflection.
local function handle_all_infl(args, data, argpref, label, generate_default)
if argpref ~= "" then
handle_infl(args, data, argpref, label, generate_default)
end
local labelsp = label == "" and "" or label .. " "
handle_infl(args, data, argpref .. "cons", labelsp .. "construct state")
handle_infl(args, data, argpref .. "def", labelsp .. "definite state")
handle_infl(args, data, argpref .. "obl", labelsp .. "oblique")
handle_infl(args, data, argpref .. "inf", labelsp .. "informal")
end
-- Handle the case where pl=-, indicating an uncountable noun.
local function handle_noun_plural(args, data)
if args.pl[1] == "-" then
table.insert(data.inflections, { label = "usually [[Appendix:Glossary#uncountable|uncountable]]" })
table.insert(data.categories, lang:getCanonicalName() .. " uncountable nouns")
if args.pauc and #args.pauc > 0 then
error("Can't specify paucals when pl=-")
end
else
handle_all_infl(args, data, "pl", "plural")
end
end
local valid_bare_genders = {false, "m", "f", "mfbysense", "mfequiv"}
local valid_bare_numbers = {false, "d", "p"}
local valid_bare_animacies = {false, "pr", "np"}
local valid_genders = {}
for _, gender in ipairs(valid_bare_genders) do
for _, number in ipairs(valid_bare_numbers) do
for _, animacy in ipairs(valid_bare_animacies) do
local parts = {}
local function ins_part(part)
if part then
table.insert(parts, part)
end
end
ins_part(gender)
ins_part(number)
ins_part(animacy)
local full_gender = table.concat(parts, "-")
valid_genders[full_gender == "" and "?" or full_gender] = true
end
end
end
local function is_masc_sg(g)
return g == "m" or g == "m-pr" or g == "m-np"
end
local function is_fem_sg(g)
return g == "f" or g == "f-pr" or g == "f-np"
end
local function add_gender_params(params, default)
params[2] = {list = "g", default = default or "?"}
end
-- Handle gender in params 2=, g2=, etc., inserting into `data.genders`. Also, if a lemma, insert categories into
-- `data.categories` if the gender is unexpected for the form of the noun. (Note: If there are multiple genders,
-- [[Module:gender and number]] will automatically insert 'Arabic POS with multiple genders'.)
local function handle_gender(args, data, nonlemma)
for _, g in ipairs(args[2]) do
if valid_genders[g] then
table.insert(data.genders, g)
else
error("Unrecognized gender: " .. g)
end
end
if nonlemma then
return
end
if #args[2] == 1 then
local g = args[2][1]
if is_masc_sg(g) or is_fem_sg(g) then
local head = args.head
if head then
head = rsub(reorder_shadda(remove_links(head)), UNU .. "?$", "")
local ends_with_tam = rfind(head, "^[^ ]*" .. TAM .. "$") or
rfind(head, "^[^ ]*" .. TAM .. " ")
if is_masc_sg(g) and ends_with_tam then
table.insert(data.categories, lang:getCanonicalName() .. " masculine terms with feminine ending")
elseif is_fem_sg(g) and not ends_with_tam and
not rfind(head, "[" .. ALIF .. AMAQ .. "]$") and
not rfind(head, ALIF .. HAMZA .. "$") then
table.insert(data.categories, lang:getCanonicalName() .. " feminine terms lacking feminine ending")
end
end
end
end
end
-- Part-of-speech functions
local adj_inflections = {
{pref = "", label = ""}, -- handle cons, def, obl, inf
{pref = "f", label = "feminine"},
{pref = "d", label = "masculine dual"},
{pref = "fd", label = "feminine dual"},
{pref = "cpl", label = "common plural"},
{pref = "pl", label = "masculine plural"},
{pref = "fpl", label = "feminine plural"},
}
local function create_infl_list_params(infl_list)
params = {}
for _, infl in ipairs(infl_list) do
if infl.basic then
add_infl_params(params, infl.pref)
else
add_all_infl_params(params, infl.pref)
end
end
return params
end
local function handle_infl_list_args(args, data, infl_list)
for _, infl in ipairs(infl_list) do
if infl.handle then
infl.handle(args, data)
elseif infl.basic then
handle_infl(args, data, infl.pref, infl.label, infl.generate_default)
else
handle_all_infl(args, data, infl.pref, infl.label, infl.generate_default)
end
end
end
pos_functions["ھاوەڵناوەکان"] = {
params = (function()
local params = create_infl_list_params(adj_inflections)
add_infl_params(params, "el")
return params
end)(),
func = function(args, data)
handle_infl_list_args(args, data, adj_inflections)
handle_infl(args, data, "el", "elative")
end
}
local function make_nisba_default(ending, endingtr)
return function(args, data)
local heads = data.heads
if #heads == 0 then
heads = {data.pagename}
end
local forms = {}
for i = 1, #heads do
local tr = data.translits[i]
table.insert(forms, {term = heads[i] .. ending, translit = tr and tr .. endingtr or nil})
end
return forms
end
end
local nisba_adj_inflections = {
{pref = "", label = ""}, -- handle cons, def, obl, inf
{pref = "f", label = "feminine", generate_default = make_nisba_default(A .. "ة", "a")},
{pref = "d", label = "masculine dual"},
{pref = "fd", label = "feminine dual"},
{pref = "cpl", label = "common plural"},
{pref = "pl", label = "masculine plural", generate_default = make_nisba_default(U .. "ونَ", "ūna")},
{pref = "fpl", label = "feminine plural", generate_default = make_nisba_default(A .. "ات", "āt")},
}
pos_functions["nisba adjectives"] = {
params = (function()
return create_infl_list_params(nisba_adj_inflections)
end)(),
func = function(args, data)
data.pos_category = "adjectives"
handle_infl_list_args(args, data, nisba_adj_inflections)
end
}
local sing_coll_noun_inflections = {
{pref = "", label = ""}, -- handle cons, def, obl, inf
{pref = "d", label = "dual"},
{pref = "pl", label = "plural", handle = handle_noun_plural},
{pref = "pauc", label = "paucal"},
}
local function handle_sing_coll_noun_infls(args, data, otherinfl, otherlabel)
handle_gender(args, data)
-- Handle sing= (corresponding singulative noun) or coll= (corresponding collective noun) and their gender
handle_infl(args, data, otherinfl, otherlabel)
handle_infl_list_args(args, data, sing_coll_noun_inflections)
end
local function get_sing_coll_noun_params(defgender, otherinfl, othergender)
local params = create_infl_list_params(sing_coll_noun_inflections)
add_gender_params(params, defgender)
add_infl_params(params, otherinfl, othergender)
return params
end
pos_functions["collective nouns"] = {
params = get_sing_coll_noun_params("m", "sing", "f"),
func = function(args, data)
data.pos_category = "nouns"
table.insert(data.categories, lang:getCanonicalName() .. " collective nouns")
table.insert(data.inflections, { label = "collective" })
handle_sing_coll_noun_infls(args, data, "sing", "singulative")
end
}
pos_functions["singulative nouns"] = {
params = get_sing_coll_noun_params("f", "coll", "m"),
func = function(args, data)
data.pos_category = "nouns"
table.insert(data.categories, lang:getCanonicalName() .. " singulative nouns")
table.insert(data.inflections, { label = "singulative" })
handle_sing_coll_noun_infls(args, data, "coll", "collective")
end
}
local noun_inflections = {
{pref = "", label = ""}, -- handle cons, def, obl, inf
{pref = "d", label = "dual"},
{pref = "pl", label = "plural", handle = handle_noun_plural},
{pref = "pauc", label = "paucal"},
{pref = "f", label = "feminine"},
{pref = "m", label = "masculine"},
}
local function get_noun_params()
local params = create_infl_list_params(noun_inflections)
add_gender_params(params)
return params
end
local function handle_noun_infls(args, data)
handle_gender(args, data)
handle_infl_list_args(args, data, noun_inflections)
end
pos_functions["ناوەکان"] = {
params = get_noun_params(),
func = handle_noun_infls,
}
-- FIXME: Do numerals really behave almost as nouns? They vary by masc/fem.
pos_functions["numerals"] = {
params = get_noun_params(),
func = function(args, data)
table.insert(data.categories, lang:getCanonicalName() .. " cardinal numbers")
handle_noun_infls(args, data)
end
}
pos_functions["proper nouns"] = {
params = get_noun_params(),
func = handle_noun_infls,
}
local pronoun_inflections = {
{pref = "", label = ""}, -- handle cons, def, obl, inf
{pref = "d", label = "dual"},
{pref = "pl", label = "plural", handle = handle_noun_plural},
{pref = "f", label = "feminine"},
}
local function get_pronoun_params()
local params = create_infl_list_params(pronoun_inflections)
add_gender_params(params)
return params
end
pos_functions["pronouns"] = {
params = get_pronoun_params(),
func = function(args, data)
handle_gender(args, data)
handle_infl_list_args(args, data, pronoun_inflections)
end
}
local function get_gender_only_params(default)
local params = {}
add_gender_params(params, default)
return params
end
pos_functions["noun plural forms"] = {
params = (function()
local params = {}
add_gender_params(params, "p")
add_infl_params(params, "cons")
return params
end)(),
func = function(args, data)
data.pos_category = "noun forms"
handle_gender(args, data, "nonlemma")
handle_infl(args, data, "cons", "construct state")
end
}
pos_functions["adjective feminine forms"] = {
params = get_gender_only_params("f"),
func = function(args, data)
data.pos_category = "adjective feminine forms"
handle_gender(args, data, "nonlemma")
end
}
pos_functions["noun dual forms"] = {
params = get_gender_only_params("m-d"),
func = function(args, data)
data.pos_category = "noun forms"
handle_gender(args, data, "nonlemma")
end
}
pos_functions["adjective plural forms"] = {
params = get_gender_only_params("m-p"),
func = function(args, data)
data.pos_category = "adjective forms"
handle_gender(args, data, "nonlemma")
end
}
pos_functions["adjective dual forms"] = {
params = get_gender_only_params("m-p"),
func = function(args, data)
data.pos_category = "adjective forms"
handle_gender(args, data, "m-d", "nonlemma")
end
}
pos_functions["noun forms"] = {
params = get_gender_only_params(),
func = function(args, data)
handle_gender(args, data, nil, "nonlemma")
end
}
local valid_forms = list_to_set(
{ "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X", "XI", "XII",
"XIII", "XIV", "XV", "Iq", "IIq", "IIIq", "IVq" })
local function handle_conj_form(args, data)
local form = args[2]
if form then
if not valid_forms[form] then
error("Invalid verb conjugation form " .. form)
end
table.insert(data.inflections, { label = '[[Appendix:Arabic verbs#Form ' .. form .. '|form ' .. form .. ']]' })
end
end
pos_functions["verb forms"] = {
params = {
[2] = {},
},
func = function(args, data)
handle_conj_form(args, data)
end
}
local function get_participle_params()
local params = create_infl_list_params(adj_inflections)
params[2] = {}
return params
end
pos_functions["active participles"] = {
params = get_participle_params(),
func = function(args, data)
data.pos_category = "participles"
table.insert(data.categories, lang:getCanonicalName() .. " active participles")
handle_conj_form(args, data)
handle_infl_list_args(args, data, adj_inflections)
end
}
pos_functions["passive participles"] = {
params = get_participle_params(),
func = function(args, data)
data.pos_category = "participles"
table.insert(data.categories, lang:getCanonicalName() .. " passive participles")
handle_conj_form(args, data)
handle_infl_list_args(args, data, adj_inflections)
end
}
return export
dy4fmxkuzi4jc7oyjxt5xhckoqttx3r
گفتار
0
10924
36886
36592
2026-08-17T00:04:05Z
Ghybu
12
/* کوردیی ناوەندی */
36886
wikitext
text/x-wiki
== کوردیی ناوەندی ==
=== وشەڕەتناسی ===
وشەیەکی خواستراوە لە زمانی [[فارسی]]یەوە: ''[[گفتار]]'' (goftâr).
=== گۆکردن ===
* {{IPA|ckb|/ɡʊfˈtɑːr/}}
=== ناو ===
{{ckb-noun|pl=گفتارەکان}}
# شێوازی قسەکردن؛ وتار یان ئاخاوتن.
#: ''نموونە: '''گفتار'''ەکەی زۆر کاریگەر بوو.''
==== هاوواتا ====
* [[وتار]]
* [[ئاخاوتن]]
* [[قسەکردن]]
* [[لێدوان]]
==== وەرگێڕانەکان ====
{{trans-top|شێوازی قسەکردن؛ وتار}}
* ئینگلیزی: {{t+|en|speech}}, {{t+|en|discourse}}
* عەرەبی: {{t+|ar|خِطَاب|m}}, {{t+|ar|كَلام|m}}
* فارسی: {{t+|fa|گفتار|tr=goftâr}}
{{trans-mid}}
* تورکی: {{t+|tr|söylev}}
* فەڕەنسی: {{t+|fr|discours|m}}
{{trans-bottom}}
qzgxwtakhd9ug77xe3woyzabz0jt2as
بەکارھێنەر:Ghybu
2
11461
36865
36853
2026-08-16T15:59:36Z
-Haryad
34
This page is personal to the user.
36865
wikitext
text/x-wiki
phoiac9h4m842xq45sp7s6u21eteeq1
مۆدیوول:ckb-nouns
828
11462
36863
36856
2026-08-16T13:51:04Z
Ghybu
12
36863
Scribunto
text/plain
local export = {}
local gsub = mw.ustring.gsub
local match = mw.ustring.match
--[[
Sorani vowels:
-- Short vowels
["ە"] -- e
["و"] -- u
-- Long vowels
["ا"] -- a
["ێ"] -- ê
["ی"] -- î
["ۆ"] -- o
["وو"] -- û
-- Half-vowels
["و"] -- w
["ی"] -- y
--]]
function export.declination(noun)
local decl = {}
if not noun or noun == "" then
return decl
end
local lastLetter = mw.ustring.sub(noun, -1)
local lastTwo = mw.ustring.sub(noun, -2)
-- 1. Nominal
decl.nomSg = noun
decl.nomPl = ""
-- 2. Indefinite
-- Singular
if lastTwo == "وو" or match(lastLetter, "[اەێیۆ]") then
decl.indefSg = noun .. "یەک"
else
decl.indefSg = noun .. "ێک"
end
-- Plural
if lastTwo == "وو" or match(lastLetter, "[اێۆ]") then
decl.indefPl = noun .. "یان"
elseif lastLetter == "ە" then
-- Remove the 'ە' and append 'ان'
local stem = gsub(noun, "ە$", "")
decl.indefPl = stem .. "ان"
else
decl.indefPl = noun .. "ان"
end
-- 3. Definite
-- Singular
if match(lastLetter, "[ەاۆ]") then
decl.defSg = noun .. "کە"
--elseif match(lastLetter, "[وێی]") then
-- decl.defPl = noun .. "ەکە"
else
decl.defSg = noun .. "ەکە"
end
-- Plural
if match(lastLetter, "[ەاۆ]") then
decl.defPl = noun .. "کان"
--elseif match(lastLetter, "[وێی]") then
-- decl.defPl = noun .. "ەکان"
else
decl.defPl = noun .. "ەکان"
end
-- 4. Demonstrative
-- Singular
if match(lastLetter, "[اەێۆ]") then
decl.demonsSg = "ئەم " .. noun .. "یە"
else
decl.demonsSg = "ئەم " .. noun .. "ە"
end
-- Plural
if lastLetter == "ا" then
decl.demonsPl = "ئەم " .. noun .. "یانە"
elseif lastLetter == "ە" then
-- Remove the 'ە' and append 'انە'
local stem = gsub(noun, "ە$", "")
decl.demonsPl = "ئەم " .. stem .. "انە"
else
decl.demonsPl = "ئەم " .. noun .. "انە"
end
return decl
end
return export
433vkfyuj5m2uzihoyl1oyisvfq6xkd
پۆل:ناوەکان بە کوردیی ناوەندی
14
11463
36859
2026-08-16T12:34:02Z
Ghybu
12
پەڕەی دروست کرد بە «{{auto cat}}»ەوە
36859
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
پۆل:کارەکان بە کوردیی ناوەندی
14
11464
36860
2026-08-16T12:35:54Z
Ghybu
12
پەڕەی دروست کرد بە «{{auto cat}}»ەوە
36860
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
پۆل:ھاوەڵناوەکان بە کوردیی ناوەندی
14
11465
36861
2026-08-16T12:39:27Z
Ghybu
12
پەڕەی دروست کرد بە «{{auto cat}}»ەوە
36861
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
لێدوانی بەکارھێنەر:Ghybu
3
11466
36864
2026-08-16T15:55:52Z
-Haryad
34
Bi xêr hatî
36864
wikitext
text/x-wiki
==بە خێر بێن ==
<div style="border: 1px solid #d2b48c; background-color: #fdfaf6; padding: 1em 1.5em; margin: 1em 0; border-radius: 10px; line-height: 1.6em;">
<div style="text-align:center; font-weight:bold; font-size:115%; margin-bottom: 1em;">
سڵاو {{ROOTPAGENAME}}، [[ویکیفەرھەنگ:بە خێر بێن، تازەکاران|بە خێر بێن بۆ ویکیفەرھەنگ]]، و سپاس بۆ ئەو بەشدارییانەی تا ئێستا کردووتن.
</div>
ئەگەر بە دەستکاریکردنی ویکی ئاشنا نیت، ئەوا سەیرێکی [[یارمەتی:چۆن دەستکاریی پەڕەیەک دەکرێت|چۆنێتیی دەستکاریکردنی پەڕە]] بکە. پەڕەکە پێڕستێکی پوختە لەسەر ڕێنمایییە تەکنیکییەکانی ئەو شێوازی ویکییەی ئێمە لێرە بە کاری دەھێنین: بۆ نموونە، چۆن دەقێک تۆخ بکەیت یان بەستەرەکان دروست بکەیت. ئاسوودە بە لە ڕاھێنانکردن لە [[ویکیفەرھەنگ:خۆڵەپەتانێ|خۆڵەپەتانێ]]. ئەگەر حەزت لە پێشەکییەکی ھێواشترە، ئەوا [[ویکیفەرھەنگ:فێرکاری]]یەکی کورتمان ھەیە.
لەوانەیە ئەم بەستەرانە وات لێ بکەن تا خۆت بە ویکیفەرھەنگ ئاشنا بکەیت:
* [[ویکیفەرھەنگ:ڕێکخستنی سەروشە|ڕێکخستنی سەروشە]] سیاسەتێکی وردە لەسەر شێوازی ڕێکخستنی پەڕەی ویکیفەرھەنگ؛ فەرزە ھەموو سەروشەکان پەیڕەویی لێ بکەن. ئاسانترین ڕێگا بۆ ئەوەی دەست پێ بکەیت، ئەوەیە کە ناوەڕۆکی پەڕەیەکی سەروشەی تری ھەمان زمان لەبەر بگریتەوە و، ئینجا لەگەڵ ئەو پەڕەیەی کە دروستی دەکەیت بیگونجێنیت.
* سەیری [[ویکیفەرھەنگ:ڕەچاوکردنی زمان|ڕەچاوکردنی زمان]] بکە تا زیاتر لەسەر چۆنێتیی دەستکاریکردنی زمانێکی دیاریکراو بزانیت.
* [[ویکیفەرھەنگ:پێوەرەکانی قبووڵکردن|پێوەری قبووڵکردنەکەمان]] بەتەواوی باسی ئەو وشانە دەکات کە دەکرێت بۆ ویکیفەرھەنگ زیاد بکرێ؛ گرنگترین شت ئەوەیە کە ویکیفەرھەنگ تەنیا ئەو وشانە قبووڵ دەکات کە بە لای کەمەوە بۆ ماوەی ساڵێک تا ڕادەیەک بە شێوەیەکی بەرفراوان بە کار ھێنراون، و کە گومان ھەبوو داوای ئەو سەرچاوانە بکرێت کە بەکارھێنانیان دەسەلمێنێ.
* ئەگەر پێشتر ئەزموونێکت لە دەستکاریکردنی پڕۆژەی [[w:دەستپێک|ویکیپیدیا]] ھەبووە، ئەوا لەوانەیە [[ویکیفەرھەنگ:ویکیفەرھەنگ بۆ ویکیپیدیاوانەکان|ڕێنمایی بۆ بەکارھێنەرانی ویکیپیدیا]] بە کەڵک بێت بۆت.
* ئەگەر ھەر پرسیارێکت ھەیە، تکایە بیانبە [[ویکیفەرھەنگ:مێزی زانیاری]] یانیش لە پەڕەی وتووێژەکەم لە منیان بپرسە.
* ھانت دەدەین تا [[:en:WT:Babel|سنووقی بابل]] بۆ پەڕەی بەکارھێنەریت زیاد بکەیت تا ئاستی زانینی زمانەکانت دیاری بکەیت.
<div style="text-align:center; font-weight:bold; margin-top: 1em;">چێژ لە مانەوەت لە ویکیفەرھەنگ ببینە!</div>
</div> [[User:-Haryad |<span style="color:black; font-size:16px;">'''ʜᴀʀʏᴀᴅ'''</span>]] <sub>[[User Talk: -Haryad |<span style="color:yellow; Background:black ">'''ᴛᴀʟᴋ'''</span>]]</sub> ١٨:٥٥، ١٦ی ئابی ٢٠٢٦ (+03)
l2zmbrtm6z1m5gdj8fgaldwq1to908n
پۆل:ناوەکان بە کوردیی باکووری
14
11467
36877
2026-08-16T23:43:32Z
Ghybu
12
پەڕەی دروست کرد بە «{{auto cat}}»ەوە
36877
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
پۆل:کارەکان بە کوردیی باکووری
14
11468
36878
2026-08-16T23:44:06Z
Ghybu
12
پەڕەی دروست کرد بە «{{auto cat}}»ەوە
36878
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
پۆل:ھاوەڵناوەکان بە کوردیی باکووری
14
11469
36879
2026-08-16T23:44:49Z
Ghybu
12
پەڕەی دروست کرد بە «{{auto cat}}»ەوە
36879
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
پۆل:ناوەکان بە ئینگلیزی
14
11470
36882
2026-08-16T23:49:13Z
Ghybu
12
پەڕەی دروست کرد بە «{{auto cat}}»ەوە
36882
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
پۆل:کارەکان بە ئینگلیزی
14
11471
36883
2026-08-16T23:49:42Z
Ghybu
12
پەڕەی دروست کرد بە «{{auto cat}}»ەوە
36883
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
پۆل:ھاوەڵناوەکان بە ئینگلیزی
14
11472
36885
2026-08-16T23:51:24Z
Ghybu
12
پەڕەی دروست کرد بە «{{auto cat}}»ەوە
36885
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
پۆل:ناوەکان بە فەڕەنسی
14
11473
36894
2026-08-17T01:54:33Z
Ghybu
12
پەڕەی دروست کرد بە «{{auto cat}}»ەوە
36894
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
پۆل:لێماکان بە فەڕەنسی
14
11474
36895
2026-08-17T01:54:48Z
Ghybu
12
پەڕەی دروست کرد بە «{{auto cat}}»ەوە
36895
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
پۆل:ناوەکان بە فارسی
14
11475
36896
2026-08-17T01:56:11Z
Ghybu
12
پەڕەی دروست کرد بە «{{auto cat}}»ەوە
36896
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
پۆل:لێماکان بە فارسی
14
11476
36897
2026-08-17T01:56:25Z
Ghybu
12
پەڕەی دروست کرد بە «{{auto cat}}»ەوە
36897
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
پۆل:ناوەکان بە عەرەبی
14
11477
36899
2026-08-17T01:57:08Z
Ghybu
12
پەڕەی دروست کرد بە «{{auto cat}}»ەوە
36899
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
پۆل:لێماکان بە عەرەبی
14
11478
36900
2026-08-17T01:57:29Z
Ghybu
12
پەڕەی دروست کرد بە «{{auto cat}}»ەوە
36900
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
پۆل:ناوەکان بە تورکی
14
11479
36901
2026-08-17T01:58:38Z
Ghybu
12
پەڕەی دروست کرد بە «{{auto cat}}»ەوە
36901
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
پۆل:لێماکان بە تورکی
14
11480
36902
2026-08-17T01:58:54Z
Ghybu
12
پەڕەی دروست کرد بە «{{auto cat}}»ەوە
36902
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
مۆدیوول:test
828
11481
36903
2026-08-17T05:30:16Z
Ghybu
12
test
36903
Scribunto
text/plain
local export = {}
local match = string.match
local format = string.format
local tonumber = tonumber
local unstrip_nowiki = mw.text.unstripNoWiki
function export.get_current_section()
local frame = mw.getCurrentFrame()
local extension_tag = frame.extensionTag
-- 1. Create a unique nowiki marker
local nowiki_marker = extension_tag(frame, "nowiki")
-- 2. Force a fake level-1 heading to get the heading counter
local preprocessed = frame:preprocess("=" .. nowiki_marker .. "=")
-- 3. Extract the heading strip marker number
local h = tonumber(match(
preprocessed,
"\127'\"`UNIQ%-%-h%-(%d+)%-%-QINU`\"'\127"
))
-- If no heading marker was found → return debug information
if not h then
return "ERROR: no heading marker found\n\n"
.. "nowiki = " .. mw.text.nowiki(tostring(nowiki_marker)) .. "\n\n"
.. "preprocessed = " .. mw.text.nowiki(tostring(preprocessed))
end
-- 4. Look for the offset in previous nowiki markers
local n = tonumber(match(
nowiki_marker,
"\127'\"`UNIQ%-%-nowiki%-([%dA-F]+)%-QINU`\"'\127"
), 16)
local offset
while not offset and n and n > 0 do
n = n - 1
local candidate = format("\127'\"`UNIQ--nowiki-%08X-QINU`\"'\127", n)
offset = match(
unstrip_nowiki(candidate),
"^HEADING\1(%d+)"
)
end
offset = offset and (offset + 1) or 0
-- 5. Store the offset for future calls
extension_tag(frame, "nowiki", "HEADING\1" .. offset)
local section = h - offset
return "Detected section: " .. section
.. "\n(h = " .. h .. ", offset = " .. offset .. ")"
end
return export
hd5x0z86503k6a91q5oq1djrs8c3nl5
36904
36903
2026-08-17T05:38:52Z
Ghybu
12
36904
Scribunto
text/plain
local export = {}
local match = string.match
local format = string.format
local tonumber = tonumber
local unstrip_nowiki = mw.text.unstripNoWiki
function export.get_current_section()
local frame = mw.getCurrentFrame()
local extension_tag = frame.extensionTag
local nowiki_marker = extension_tag(frame, "nowiki")
local preprocessed = frame:preprocess("=" .. nowiki_marker .. "=")
local h = tonumber(match(
preprocessed,
"\127'\"`UNIQ%-%-h%-(%d+)%-%-QINU`\"'\127"
)) or 0
local n = tonumber(match(
nowiki_marker,
"\127'\"`UNIQ%-%-nowiki%-([%dA-F]+)%-QINU`\"'\127"
), 16)
local offset
while not offset and n and n > 0 do
n = n - 1
local candidate = format("\127'\"`UNIQ--nowiki-%08X-QINU`\"'\127", n)
offset = match(unstrip_nowiki(candidate), "^HEADING\1(%d+)")
end
offset = offset and (offset + 1) or 0
extension_tag(frame, "nowiki", "HEADING\1" .. offset)
-- Correction pour ckb.wiktionary (les marqueurs commencent à 0)
local section = h - offset + 1
return "Detected section: " .. section
.. "\n(h = " .. h .. ", offset = " .. offset .. ")"
end
return export
gmvsg1w1n26i4zwd4y5sjqu1c88ob09
36905
36904
2026-08-17T05:46:10Z
Ghybu
12
36905
Scribunto
text/plain
local export = {}
local match = string.match
local tonumber = tonumber
function export.get_current_section()
local frame = mw.getCurrentFrame()
-- 1. Get the next heading number the parser would create
local nowiki = frame:extensionTag("nowiki")
local pre = frame:preprocess("=" .. nowiki .. "=")
local h = tonumber(match(pre, "\127'\"`UNIQ%-%-h%-(%d+)%-%-QINU`\"'\127")) or 0
-- 2. Count how many real level-2 headings (== ... ==) already exist in the page content
local content = mw.title.getCurrentTitle():getContent() or ""
local real_headings = 0
for line in mw.ustring.gmatch(content, "[^\r\n]+") do
-- Only exact level-2 headings
if mw.ustring.match(line, "^==[^=].*[^=]==%s*$") then
real_headings = real_headings + 1
end
end
-- 3. Calculate the current section
-- On ckb.wiktionary heading markers start at 0, so we add 1
local section = h + 1
-- Safety limits
if section > real_headings then
section = real_headings
end
if section < 1 then
section = 1
end
return "Detected section: " .. section
.. "\n(h = " .. h .. ", real_headings = " .. real_headings .. ")"
end
return export
euc9lmk70oa95twvsmo5zxweb2qkgh4
36907
36905
2026-08-17T06:05:53Z
Ghybu
12
36907
Scribunto
text/plain
local export = {}
local match = string.match
local format = string.format
local tonumber = tonumber
local unstrip_nowiki = mw.text.unstripNoWiki
function export.get_current_section()
local frame = mw.getCurrentFrame()
local extension_tag = frame.extensionTag
local nowiki_marker = extension_tag(frame, "nowiki")
local preprocessed = frame:preprocess("=" .. nowiki_marker .. "=")
local h = tonumber(match(
preprocessed,
"\127'\"`UNIQ%-%-h%-(%d+)%-%-QINU`\"'\127"
)) or 0
local n = tonumber(match(
nowiki_marker,
"\127'\"`UNIQ%-%-nowiki%-([%dA-F]+)%-QINU`\"'\127"
), 16)
local offset
while not offset and n and n > 0 do
n = n - 1
local candidate = format("\127'\"`UNIQ--nowiki-%08X-QINU`\"'\127", n)
offset = match(unstrip_nowiki(candidate), "^HEADING\1(%d+)")
end
offset = offset and (tonumber(offset) + 1) or 0
extension_tag(frame, "nowiki", "HEADING\1" .. offset)
local section = h - offset + 1
return "Detected section: " .. section
.. "\n(h = " .. h .. ", offset = " .. offset .. ")"
end
return export
ile1v7mvkog6dnvxd0tn6lgkgjjvw4w
36908
36907
2026-08-17T06:09:16Z
Ghybu
12
36908
Scribunto
text/plain
local export = {}
local match = string.match
local format = string.format
local tonumber = tonumber
local unstrip_nowiki = mw.text.unstripNoWiki
function export.get_current_section()
local frame = mw.getCurrentFrame()
local extension_tag = frame.extensionTag
local nowiki_marker = extension_tag(frame, "nowiki")
local preprocessed = frame:preprocess("=" .. nowiki_marker .. "=")
local h = tonumber(match(
preprocessed,
"\127'\"`UNIQ%-%-h%-(%d+)%-%-QINU`\"'\127"
)) or 0
local n = tonumber(match(
nowiki_marker,
"\127'\"`UNIQ%-%-nowiki%-([%dA-F]+)%-QINU`\"'\127"
), 16)
local offset
while not offset and n and n > 0 do
n = n - 1
local candidate = format("\127'\"`UNIQ--nowiki-%08X-QINU`\"'\127", n)
-- Unique prefix to avoid collisions
offset = match(unstrip_nowiki(candidate), "^CKBSEC\1(%d+)")
end
offset = offset and (tonumber(offset) + 1) or 0
extension_tag(frame, "nowiki", "CKBSEC\1" .. offset)
local section = h - offset + 1
return "Detected section: " .. section
.. "\n(h = " .. h .. ", offset = " .. offset .. ")"
end
return export
kd3di5bia8e7nmee1b0wipaabk65qo5
36909
36908
2026-08-17T06:10:59Z
Ghybu
12
36909
Scribunto
text/plain
local export = {}
local match = string.match
local format = string.format
local tonumber = tonumber
local unstrip_nowiki = mw.text.unstripNoWiki
function export.get_current_section()
local frame = mw.getCurrentFrame()
local extension_tag = frame.extensionTag
local nowiki_marker = extension_tag(frame, "nowiki")
local preprocessed = frame:preprocess("=" .. nowiki_marker .. "=")
local h = tonumber(match(
preprocessed,
"\127'\"`UNIQ%-%-h%-(%d+)%-%-QINU`\"'\127"
)) or 0
local n = tonumber(match(
nowiki_marker,
"\127'\"`UNIQ%-%-nowiki%-([%dA-F]+)%-QINU`\"'\127"
), 16)
local offset
while not offset and n and n > 0 do
n = n - 1
local candidate = format("\127'\"`UNIQ--nowiki-%08X-QINU`\"'\127", n)
offset = match(unstrip_nowiki(candidate), "^CKBSEC\1(%d+)")
end
offset = offset and (tonumber(offset) + 1) or 0
extension_tag(frame, "nowiki", "CKBSEC\1" .. offset)
local section = h - offset
return "Detected section: " .. section
.. "\n(h = " .. h .. ", offset = " .. offset .. ")"
end
return export
lf6l7dtp2oami119u57i3um96vlxppv
بەکارھێنەر:Ghybu/test
2
11482
36906
2026-08-17T06:01:46Z
Ghybu
12
test
36906
wikitext
text/x-wiki
{{ھەروەھا|Kürdistan}}
{{#invoke:test|get_current_section}}
== ئینگلیزی ==
=== گۆکردن ===
* {{IPA|en|/ˈkəɹdɪˌstæn/}}
* {{دەنگ|kmr|LL-Q1860 (eng)-Vealhurl-Kurdistan.wav|}}
=== ناو ===
{{en-proper noun}}
# [[کوردستان]]
{{#invoke:test|get_current_section}}
== کرمانجی ==
=== گۆکردن ===
* {{IPA|kmr|/kʰʊɾdɘsˈtɑːn/}}
* {{دەنگ|kmr|Ku-Kurdistan.oga|}}
* {{دەنگ|kmr|LL-Q36163 (kmr)-Ebulfîda-Kurdistan.wav}}
=== ناو ===
{{kmr-proper noun|f}}
# [[کوردستان]]
{{#invoke:test|get_current_section}}
p8qbksf3v9hl8r19y1ehyjgtyamq51c
36910
36906
2026-08-17T06:23:09Z
Ghybu
12
36910
wikitext
text/x-wiki
{{#invoke:test|get_current_section}}
== ئینگلیزی ==
=== گۆکردن ===
* {{IPA|en|/ˈkəɹdɪˌstæn/}}
* {{دەنگ|kmr|LL-Q1860 (eng)-Vealhurl-Kurdistan.wav|}}
=== ناو ===
{{en-proper noun}}
# [[کوردستان]]
{{#invoke:test|get_current_section}}
== کرمانجی ==
=== گۆکردن ===
* {{IPA|kmr|/kʰʊɾdɘsˈtɑːn/}}
* {{دەنگ|kmr|Ku-Kurdistan.oga|}}
* {{دەنگ|kmr|LL-Q36163 (kmr)-Ebulfîda-Kurdistan.wav}}
=== ناو ===
{{kmr-proper noun|f}}
# [[کوردستان]]
{{#invoke:test|get_current_section}}
1jofdz6cotj8lvm617kijvzm4pb584v
36911
36910
2026-08-17T06:30:34Z
Ghybu
12
36911
wikitext
text/x-wiki
{{#invoke:test|get_current_section}}
== ئینگلیزی ==
{{#invoke:test|get_current_section}}
== کرمانجی ==
=== ناو ===
{{kmr-proper noun|f}}
# [[کوردستان]]
{{#invoke:test|get_current_section}}
== کرمانجی ==
{{#invoke:test|get_current_section}}
duyd3rr3w00vs885eaivl410n3h5el0
36912
36911
2026-08-17T06:32:14Z
Ghybu
12
36912
wikitext
text/x-wiki
{{#invoke:test|get_current_section}}
== فەڕەنسی ==
{{#invoke:test|get_current_section}}
== کرمانجی ==
=== ناو ===
{{kmr-proper noun|f}}
# [[کوردستان]]
{{#invoke:test|get_current_section}}
== فارسی ==
{{#invoke:test|get_current_section}}
slhdkfimirkv7pscu9jyp0v2442ufbk