ویکیفەرھەنگ 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 .. "&nbsp;" .. 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 .. "&nbsp;" .. 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|}}}|&#32;(''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|}}}|&#32;(''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|}}}|&#32; ''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|}}}|&#32; ''کۆ'' {{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|}}}|&#32; ''کۆ'' {{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