ویکیفەرھەنگ 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.14 case-sensitive میدیا تایبەت وتووێژ بەکارھێنەر لێدوانی بەکارھێنەر ویکیفەرھەنگ لێدوانی ویکیفەرھەنگ پەڕگە وتووێژی پەڕگە میدیاویکی وتووێژی میدیاویکی داڕێژە وتووێژی داڕێژە یارمەتی وتووێژی یارمەتی پۆل وتووێژی پۆل TimedText TimedText talk مۆدیوول لێدوانی مۆدیوول Event Event talk مۆدیوول:headword 828 35 36500 34463 2026-08-09T17:16:44Z Ghybu 12 update 36500 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 dump = mw.dumpObject 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(...) pluralize = require(en_utilities_module).pluralize return pluralize(...) 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, dump(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 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:getContent() 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:getContent() 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 -- Forward reference local format_inflections local function format_inflection_parts(data, parts) 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). -- -- FIXME: With nested inflections, should we also respect `enable_auto_translit` at the top level of the -- nested inflections structure? local tr = part.tr or not (parts.enable_auto_translit or data.inflections.enable_auto_translit) and "-" or nil -- FIXME: Temporary errors added 2025-10-03. Remove after a month or so. if part.translit then error("Internal error: Use field `tr` not `translit` for specifying an inflection part translit") end if part.transcription then error("Internal error: Use field `ts` not `transcription` for specifying an inflection part transcription") end local postprocess_annotations if part.inflections then postprocess_annotations = function(infldata) insert(infldata.annotations, format_inflections(data, part.inflections)) end 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.ts, accel = partaccel or parts.accel, postprocess_annotations = postprocess_annotations, }, 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) end -- Format the inflections following the headword or nested after a given inflection. Declared local above. function format_inflections(data, inflections) if inflections and inflections[1] then -- Format each inflection individually. for key, infl in ipairs(inflections) do inflections[key] = format_inflection_parts(data, infl) end return concat(inflections, ", ") else return "" end end -- Format the top-level inflections following the headword. Currently this just adds parens around the -- formatted comma-separated inflections in `data.inflections`. local function format_top_level_inflections(data) local result = format_inflections(data, data.inflections) if result ~= "" then return " (" .. result .. ")" else return result end end -- Forward reference local check_red_link_inflections -- Check a single inflection (which consists of a label and zero or more terms, each possibly with nested inflections) -- for red links. If so, insert a red-link category based on `plpos` (the plural part of speech to insert in the -- category), stop further processing, and return true. If no red links found, return false. local function check_red_link_inflection_parts(data, parts, plpos) for _, part in ipairs(parts) do if type(part) ~= "table" then part = {term = part} end local term = part.term if term and not term:find("%[%[") then local stripped_physical_term = get_link_page(term, data.lang, part.sc or parts.sc or nil) if stripped_physical_term then local title = mw.title.new(stripped_physical_term) if title and not title:getContent() then insert(data.categories, data.lang:getFullName() .. " " .. plpos .. " with red links in their headword lines") return true end end end if part.inflections then if check_red_link_inflections(data, part.inflections, plpos) then return true end end end return false end -- Check a set of inflections (each of which describes a single inflection of the term, such as feminine or plural, and -- consists of a label and zero or more terms, each possibly with nested inflections) for red links. If so, insert a -- red-link category based on `plpos` (the plural part of speech to insert in the category), stop further processing, -- and return true. If no red links found, return false. function check_red_link_inflections(data, inflections, plpos) if inflections and inflections[1] then -- Check each inflection individually. for key, infl in ipairs(inflections) do if check_red_link_inflection_parts(data, infl, plpos) then return true end end end return false end -- Check the top-level inflections in `data.inflections`, along with any nested inflections, for red links. If so, -- insert a red-link category based on `plpos` (the plural part of speech to insert in the category), stop further -- processing, and return true. If no red links found, return false. local function check_red_link_inflections_top_level(data, plpos) return check_red_link_inflections(data, data.inflections, plpos) 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(-1) == "s" 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 m_headword_data = m_data or get_data() local isLemma = m_headword_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_headword_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 m_headword_data = m_data or get_data() if m_headword_data.pos_aliases[pos] then pos = m_headword_data.pos_aliases[pos] elseif pos:sub(-1) == "f" then pos = pos:sub(1, -2) pos = (m_headword_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(("Internal 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(("Internal 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(("Internal 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(("Internal 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("Internal 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("Internal 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("Internal 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 = data.pagename local page local m_headword_data = m_data or get_data() if raw_pagename and raw_pagename ~= m_headword_data.pagename then -- for testing, doc pages, etc. -- data.pagename is often set on documentation and test pages through the pagename= parameter of various -- templates, to emulate running on that page. Having a large number of such test templates on a single -- page often leads to timeouts, because we fetch and parse the contents of each page in turn. However, -- we don't really need to do that and can function fine without fetching and parsing the contents of a -- given page, so turn off content fetching/parsing (and also setting the DEFAULTSORT key through a parser -- function, which is *slooooow*) in certain namespaces where test and documentation templates are likely to -- be found and where actual content does not live (User, Template, Module). local actual_namespace = m_headword_data.page.namespace local no_fetch_content = actual_namespace == "User" or actual_namespace == "Template" or actual_namespace == "Module" page = process_page(raw_pagename, no_fetch_content) else page = m_headword_data.page end local namespace = page.namespace ------------ 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("Internal 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("Internal 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 = full_langname .. " " .. data.pos_category -- 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, full_langname .. " " .. postype .. "s") 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 this is an "anti-asterisk" term, i.e. an attested term in a language that must normally be -- reconstructed. local is_anti_asterisk = data.heads[1].term and data.heads[1].term:find("^!!") local lang_reconstructed = data.lang:hasType("reconstructed") if is_anti_asterisk then if not lang_reconstructed then error("Anti-asterisk feature (head= beginning with !!) can only be used with reconstructed languages") end lang_reconstructed = false end -- Determine if term is reconstructed local is_reconstructed = namespace == "Reconstruction" or lang_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_headword_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. Check the namespace against the language type. ------------ if namespace == "" then if lang_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 ------------ 7. 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 ------ 7a. 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 is_anti_asterisk and head.term == "!!" then -- If explicit head=!! is given, it's an anti-asterisk term and we fill in the default head. head.term = "!!" .. default_head 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 head.term_no_initial_bang_bang = is_anti_asterisk and head.term:sub(3) or head.term 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 ------ 7b. 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 track("redundant script code", data.lang) if not data.no_script_code_cat then insert(data.categories, full_langname .. " terms with redundant script codes") end else track("non-redundant manual script code", data.lang) if not data.no_script_code_cat then insert(data.categories, full_langname .. " terms with non-redundant manual script codes") end 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() ------ 7c. 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_headword_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_no_initial_bang_bang if not data.lang:link_tr(head.sc) then text = remove_links(text) end local automated_tr = data.lang:transliterate(text, head.sc) if automated_tr then local manual_tr = head.tr if manual_tr then if remove_links(manual_tr) == remove_links(automated_tr) then insert(data.categories, full_langname .. " terms with redundant transliterations") else insert(data.categories, full_langname .. " terms with non-redundant manual transliterations") end end if not manual_tr then head.tr = automated_tr 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 ------------ 8. 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_headword_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 ------------ 9. 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 -- This is not the right way to go about this; too many exceptions and problems due to language-specific headword -- handling customization. If we want this, it should be opt-in by a given language passing in the default headword. -- 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_headword_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_headword_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%-፡]." local is_multiword = umatch(page.pagename, checkpattern) if is_multiword and not non_categorizable(page.full_raw_pagename) then insert(data.categories, full_langname .. " multiword terms") elseif not is_multiword then local long_word_threshold = m_headword_data.long_word_thresholds[langcode] or m_headword_data.long_word_thresholds[full_langcode] if long_word_threshold and ulen(page.pagename) >= long_word_threshold then insert(data.categories, "Long " .. full_langname .. " words") end end end end local default_sccat = m_headword_data.default_sccat if data.sccat or data.sccat == nil and (default_sccat[langcode] or default_sccat[full_langcode]) 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 (Algonquian) ["acy"] = "δθ", -- Cypriot Arabic ["aez"] = "β", -- Aeka (Trans-New Guinea) ["anc"] = "γ", -- Ngas (Chadic/Afroasiatic) ["aou"] = "χ", -- A'ou (Kra-Dai) ["art-blk"] = "ч", -- Bolak (conlang) ["awg"] = "β", -- Anguthimri (Pama-Nyungan) ["az"] = "ь", -- Azerbaijani (Turkic; Yañalif Latin spelling, c. 1928 - 1938) ["ba"] = "ь", -- Bashkir (Turkic; Yañalif Latin spelling, c. 1928 - 1938) ["bhp"] = "β", -- Bima (Austronesian) ["bjz"] = "β", -- Baruga (Trans-New Guinea) ["byk"] = "θ", -- Biao (Kra-Dai) ["cdy"] = "θ", -- Chadong (Kra-Dai) ["chp"] = "θ", -- Chipewyan (Athabaskan) ["cjh"] = "χ", -- Upper Chehalis (Salishan) ["clm"] = "χ", -- Klallam (Salishan) ["col"] = "χ", -- Colombia-Wenatchi (Salishan) ["coo"] = "χθ", -- Comox (Salishan) ["crx"] = "θ", -- Carrier (Athabaskan) ["ets"] = "θ", -- Yekhee (Edoid/Niger-Congo) ["ett"] = "χ", -- Etruscan (isolate; in romanizations) ["fla"] = "χ", -- Montana Salish (Salishan) ["grt"] = "་", -- Garo (South Asian Sino-Tibetan) ["gmw-gts"] = "χ", -- Gottscheerish (Bavarian variant spoken in Slovenia) ["hur"] = "χθ", -- Halkomelem (Salishan) ["itc-psa"] = "f", -- Pre-Samnite (Italic; normally written in Greek) ["izh"] = "ь", -- Ingrian (Finnic) ["kic"] = "θ", -- Kickapoo (Algonquian) ["kk"] = "ь", -- Kazakh (Turkic; Yañalif Latin spelling, c. 1928 - 1938) ["ky"] = "ь", -- Kyrgyz (Turkic; Yañalif Latin spelling, c. 1928 - 1938) ["lil"] = "χ", -- Lillooet (Salishan) ["lsi"] = "ꓹ", -- Lashi (Lolo-Burmese/Sino-Tibetan; represents a glottal stop) ["mhz"] = "β", -- Mor (Austronesian) ["mqn"] = "β", -- Moronene (Austronesian) ["neg"]= "ӡā", -- Negidal (Tungusic; normally in Cyrillic) ["oka"] = "χ", -- Okanagan (Salishan) ["ole"] = "θ", -- Olekha (Sino-Tibetan) ["oui"] = "γβ", -- Old Uyghur (Turkic; FIXME: others? E.g. Greek delta (δ)?) ["pox"] = "χ", -- Polabian (West Slavic) ["rif"] = "ε", -- Tarifit (Berber) ["rom"] = "Θθ", -- Romani (Indic: International Standard; two different thetas???) ["rpn"] = "β", -- Repanbitip (Austronesian) ["sah"] = "ь", -- Yakut (Turkic; 1929 - 1939 Latin spelling) ["sit-jap"] = "χ", -- Japhug (Sino-Tibetan) ["sjw"] = "θ", -- Shawnee (Algonquian) ["squ"] = "χ", -- Squamish (Salishan) ["str"] = "χθ", -- Saanich (Salishan) ["teh"] = "χ", -- Tehuelche (Chonan; spoken in Argentina) ["tep"] = "η", -- Tepecano (Uto-Aztecan) ["thp"] = "χ", -- Thompson (Salishan) ["tk"] = "ь", -- Turkmen (Turkic; Yañalif Latin spelling, c. 1928 - 1938) ["tt"] = "ь", -- Kazakh (Turkic; Yañalif Latin spelling, c. 1928 - 1938) ["twa"] = "χ", -- Twana (Salishan) ["wbl"] = "ы", -- Wakhi (Iranian) ["xbc"] = "ϸ", -- Bactrian (Iranian; represents š; normally written in Greek) ["yha"] = "θ", -- Baha (Kra-Dai) ["za"] = "зч", -- Zhuang (Tai/Kra-Dai); 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 (West Slavic; 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 (Mon-Khmer) ["ztp"] = "θ", -- Loxicha Zapotec (Zapotecan) } -- 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 and not non_categorizable(page.full_raw_pagename) 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 lang_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 red link category if called for and we're not a "large" page, where such checks are disabled. if data.checkredlinks and not m_headword_data.large_pages[m_headword_data.pagename] then local plposcat = type(data.checkredlinks) == "string" and data.checkredlinks or data.pos_category check_red_link_inflections_top_level(data, plposcat) end -- Add to various maintenance categories. export.maintenance_cats(page, data.lang, data.categories, data.whole_page_categories) ------------ 10. 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_top_level_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 hiwez7r1jj6upn4uih38t4pu6mn3yjb 36502 36500 2026-08-09T17:20:03Z Ghybu 12 پووچەڵکردنەوەی پێداچوونەوەی [[Special:Diff/36500|36500]] لە لایەن [[Special:Contributions/Ghybu|Ghybu]] ([[User talk:Ghybu|لێدوان]]) 36502 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(...) pluralize = require(en_utilities_module).pluralize return pluralize(...) 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(-1) == "s" 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 = full_langname .. " " .. data.pos_category -- 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, full_langname .. " " .. postype .. "s") 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 izs3qnzaommzdxi1hhxl44kecep78rq مۆدیوول:headword/templates 828 37 36497 33970 2026-08-09T17:07:29Z Ghybu 12 rojanekirin 36497 Scribunto text/plain local export = {} local debug_track_module = "Module:debug/track" local headword_module = "Module:headword" local headword_data_module = "Module:headword/data" local parameters_module = "Module:parameters" local yesno_module = "Module:yesno" local insert = table.insert local require = require local function debug_track(...) debug_track = require(debug_track_module) return debug_track(...) end local function process_params(...) process_params = require(parameters_module).process return process_params(...) end local function track(page) debug_track("headword/templates/" .. page) return true end local function get_args(frame) local boolean = {type = "boolean"} local boolean_list_allow_holes = {type = "boolean", list = true, allow_holes = true} local list_allow_holes = {list = true, allow_holes = true} return process_params(frame:getParent().args, { [1] = {required = true, type = "language", template_default = "und"}, sc = {type = "script"}, sort = true, [2] = {required = true, template_default = "nouns"}, sccat = boolean, noposcat = boolean, nomultiwordcat = boolean, nogendercat = boolean, nopalindromecat = boolean, nolinkhead = boolean, -- Don't track uses of this template for the purpose of finding direct calls to {{head}}. -- Useful for templates that wrap {{tl|head}}. notrackuses = boolean, autotrinfl = boolean, altform = boolean, -- EXPERIMENTAL: see [[Wiktionary:Beer parlour/2024/June#Decluttering the altform mess]] checkredlinks = true, cat2 = true, cat3 = true, cat4 = true, pagename = true, head = list_allow_holes, image = true, id = true, tr = list_allow_holes, ts = list_allow_holes, gloss = true, g = {list = true, type = "genders", flatten = true}, ["g\1qual"] = {list = true, allow_holes = true, replaced_by = false, instead = "use <q:...> or <l:...> inline modifier on a gender"}, [3] = list_allow_holes, ["f\1accel-form"] = list_allow_holes, ["f\1accel-translit"] = list_allow_holes, ["f\1accel-lemma"] = list_allow_holes, ["f\1accel-lemma-translit"] = list_allow_holes, ["f\1accel-gender"] = list_allow_holes, ["f\1accel-nostore"] = boolean_list_allow_holes, ["f\1request"] = list_allow_holes, ["f\1alt"] = list_allow_holes, ["f\1lang"] = {list = true, allow_holes = true, type = "language"}, ["f\1sc"] = {list = true, allow_holes = true, type = "script"}, ["f\1id"] = list_allow_holes, ["f\1tr"] = list_allow_holes, ["f\1ts"] = list_allow_holes, ["f\1t"] = list_allow_holes, ["f\1lit"] = list_allow_holes, ["f\1pos"] = list_allow_holes, ["f\1ng"] = list_allow_holes, ["f\1g"] = {list = true, allow_holes = true, type = "genders"}, ["f\1q"] = {list = true, allow_holes = true, type = "qualifier"}, ["f\1qq"] = {list = true, allow_holes = true, type = "qualifier"}, ["f\1qual"] = {list = true, allow_holes = true, replaced_by = false, instead = "use fNq= or fNl="}, ["f\1l"] = {list = true, allow_holes = true, type = "labels"}, ["f\1ll"] = {list = true, allow_holes = true, type = "labels"}, ["f\1ref"] = {list = true, allow_holes = true, type = "references"}, ["f\1autotr"] = boolean_list_allow_holes, ["f\1nolink"] = boolean_list_allow_holes, }) end function export.head_t(frame) local m_headword = require(headword_module) local args = get_args(frame) -- Get language and script information local data = {} data.lang = args[1] data.sc = args.sc data.sccat = args.sccat data.sort_key = args.sort data.heads = args.head data.image = args.image data.pagename = args.pagename data.id = args.id data.translits = args.tr data.transcriptions = args.ts data.gloss = args.gloss data.genders = args.g -- TODO should throw an error if data.heads gets overwritten if data.image then data.heads = {"[[File:" .. data.image .. "|class=skin-invert-image]]"} end -- This shouldn't really happen. for i = 1, args.head.maxindex do if not args.head[i] then track("head-with-holes") end end -- EXPERIMENTAL: see [[Wiktionary:Beer parlour/2024/June#Decluttering the altform mess]] data.altform = args.altform -- Part-of-speech category local pos_category = args[2] data.noposcat = args.noposcat -- Check for headword aliases and then pluralize if the POS term does not have an invariable plural. data.pos_category = m_headword.canonicalize_pos(pos_category) -- Track all direct uses of {{head}} for certain languages that have a {{XX-head}} template that should be -- used instead. Examples: {{en-head}}, {{sa-head}}, {{hi-head}}, {{tl-head}}, etc. if not args.notrackuses then local langcode = data.lang:getCode() local langfullcode = data.lang:getFullCode() if mw.loadData(headword_data_module).track_head_template[langfullcode] then track(langcode) track(langcode .. "/" .. data.pos_category) if langcode ~= langfullcode then track(langfullcode) track(langfullcode .. "/" .. data.pos_category) end end end -- Additional categories. local categories = {} data.whole_page_categories = {} data.nomultiwordcat = args.nomultiwordcat data.nogendercat = args.nogendercat data.nopalindromecat = args.nopalindromecat -- FIXME: add a minimum_index spec to [[Module:parameters]] list specs, so -- that `cat` can be changed to a list parameter starting at index 2. if args.cat2 then insert(categories, data.lang:getFullName() .. " " .. args.cat2) end if args.cat3 then insert(categories, data.lang:getFullName() .. " " .. args.cat3) end if args.cat4 then insert(categories, data.lang:getFullName() .. " " .. args.cat4) end data.categories = categories if args.checkredlinks then data.checkredlinks = require(yesno_module)(args.checkredlinks, args.checkredlinks) end -- Headword linking data.nolinkhead = args.nolinkhead -- Inflected forms data.inflections = {enable_auto_translit = args.autotrinfl} local forms = args[3] local n = forms.maxindex / 2 for i = 1, n + n % 1 do local infl_part = { label = forms[i * 2 - 1], accel = args["faccel-form"][i] and { form = args["faccel-form"][i], translit = args["faccel-translit"][i], lemma = args["faccel-lemma"][i], lemma_translit = args["faccel-lemma-translit"][i], gender = args["faccel-gender"][i], nostore = args["faccel-nostore"][i], } or nil, request = args.frequest[i], enable_auto_translit = args.fautotr[i], } local form = { term = forms[i * 2], alt = args.falt[i], genders = args.fg[i], id = args.fid[i], lang = args.flang[i], nolinkinfl = args.fnolink[i], q = args.fq[i], qq = args.fqq[i], l = args.fl[i], ll = args.fll[i], refs = args.fref[i], sc = args.fsc[i], tr = args.ftr[i], ts = args.fts[i], gloss = args.ft[i], lit = args.flit[i], pos = args.fpos[i], ng = args.fng[i], } -- If no term or alt is given, then the label is shown alone. if form.term or form.alt then insert(infl_part, form) end if infl_part.label == "or" then -- Append to the previous inflection part, if one exists if #infl_part > 0 and data.inflections[1] then insert(data.inflections[#data.inflections], form) end elseif infl_part.label then -- Add a new inflection part insert(data.inflections, infl_part) end end return m_headword.full_headword(data) end function export.canonicalize_pos(frame) local iargs = process_params(frame.args, { [1] = {required = true}, }) return require(headword_module).canonicalize_pos(iargs[1]) end return export 3twb2qfj835l1a6p7yn0ursy7wvv42z 36503 36497 2026-08-09T17:20:36Z Ghybu 12 پووچەڵکردنەوەی پێداچوونەوەی [[Special:Diff/36497|36497]] لە لایەن [[Special:Contributions/Ghybu|Ghybu]] ([[User talk:Ghybu|لێدوان]]) 36503 Scribunto text/plain insert = table.insert local process_params = require("Module:parameters").process local export = {} local function get_args(frame) local boolean = {type = "boolean"} local boolean_list_allow_holes = {type = "boolean", list = true, allow_holes = true} local list_allow_holes = {list = true, allow_holes = true} return process_params(frame:getParent().args, { [1] = {required = true, type = "language", default = "und"}, ["sc"] = {type = "script"}, ["sort"] = true, [2] = {required = true, default = "nouns"}, ["sccat"] = boolean, ["noposcat"] = boolean, ["nomultiwordcat"] = boolean, ["nogendercat"] = boolean, ["nopalindromecat"] = boolean, ["nolinkhead"] = boolean, ["autotrinfl"] = boolean, ["altform"] = boolean, -- EXPERIMENTAL: see [[Wiktionary:Beer parlour/2024/June#Decluttering the altform mess]] ["cat2"] = true, ["cat3"] = true, ["cat4"] = true, ["head"] = list_allow_holes, ["id"] = true, ["tr"] = list_allow_holes, ["ts"] = list_allow_holes, ["gloss"] = true, ["g"] = {list = true}, ["g\1qual"] = list_allow_holes, [3] = list_allow_holes, ["f\1accel-form"] = list_allow_holes, ["f\1accel-translit"] = list_allow_holes, ["f\1accel-lemma"] = list_allow_holes, ["f\1accel-lemma-translit"] = list_allow_holes, ["f\1accel-gender"] = list_allow_holes, ["f\1accel-nostore"] = boolean_list_allow_holes, ["f\1request"] = list_allow_holes, ["f\1alt"] = list_allow_holes, ["f\1lang"] = {list = true, allow_holes = true, type = "language"}, ["f\1sc"] = {list = true, allow_holes = true, type = "script"}, ["f\1id"] = list_allow_holes, ["f\1tr"] = list_allow_holes, ["f\1ts"] = list_allow_holes, ["f\1g"] = list_allow_holes, ["f\1qual"] = list_allow_holes, ["f\1autotr"] = boolean_list_allow_holes, ["f\1nolink"] = boolean_list_allow_holes, }) end function export.head_t(frame) local m_headword = require("Module:headword") local function track(page) require("Module:debug/track")("headword/templates/" .. page) return true end local args = get_args(frame) -- Get language and script information local data = {} data.lang = args[1] data.sc = args["sc"] data.sccat = args["sccat"] data.sort_key = args["sort"] data.heads = args["head"] data.id = args["id"] data.translits = args["tr"] data.transcriptions = args["ts"] data.gloss = args["gloss"] data.genders = args["g"] -- This shouldn't really happen. for i = 1,args["head"].maxindex do if not args["head"][i] then track("head-with-holes") end end for k, v in pairs(args["gqual"]) do if k ~= "maxindex" then if data.genders[k] then data.genders[k] = {spec = data.genders[k], qualifiers = {v}} else k = k == 1 and "" or tostring(k) error(("g%squal= specified without g%s="):format(k, k)) end end end -- EXPERIMENTAL: see [[Wiktionary:Beer parlour/2024/June#Decluttering the altform mess]] data.altform = args["altform"] -- Part-of-speech category local pos_category = args[2] data.noposcat = args["noposcat"] -- Check for headword aliases and then pluralize if the POS term does not have an invariable plural. data.pos_category = m_headword.canonicalize_pos(pos_category) -- Additional categories. data.categories = {} data.whole_page_categories = {} data.nomultiwordcat = args["nomultiwordcat"] data.nogendercat = args["nogendercat"] data.nopalindromecat = args["nopalindromecat"] if args["cat2"] then insert(data.categories, data.lang:getFullName() .. " " .. args["cat2"]) end if args["cat3"] then insert(data.categories, data.lang:getFullName() .. " " .. args["cat3"]) end if args["cat4"] then insert(data.categories, data.lang:getFullName() .. " " .. args["cat4"]) end -- Headword linking data.nolinkhead = args["nolinkhead"] -- Inflected forms data.inflections = {enable_auto_translit = args["autotrinfl"]} for i = 1, math.ceil(args[3].maxindex / 2) do local infl_part = { label = args[3][i * 2 - 1], accel = args["faccel-form"][i] and { form = args["faccel-form"][i], translit = args["faccel-translit"][i], lemma = args["faccel-lemma"][i], lemma_translit = args["faccel-lemma-translit"][i], gender = args["faccel-gender"][i], nostore = args["faccel-nostore"][i], } or nil, request = args["frequest"][i], enable_auto_translit = args["fautotr"][i], } local form = { term = args[3][i * 2], alt = args["falt"][i], genders = args["fg"][i] and mw.text.split(args["fg"][i], ",") or {}, id = args["fid"][i], lang = args["flang"][i], nolinkinfl = args["fnolink"][i], q = {args["fqual"][i]}, sc = args["fsc"][i], translit = args["ftr"][i], transcription = args["fts"][i], } -- If no term or alt is given, then the label is shown alone. if form.term or form.alt then insert(infl_part, form) end if infl_part.label == "or" then -- Append to the previous inflection part, if one exists if #infl_part > 0 and data.inflections[1] then insert(data.inflections[#data.inflections], form) end elseif infl_part.label then -- Add a new inflection part insert(data.inflections, infl_part) end end return m_headword.full_headword(data) end return export tuwt4ezlwuig2z0c1blbuzfb2pef0t3 مۆدیوول:parameters 828 65 36498 35255 2026-08-09T17:09:00Z Ghybu 12 rojanekirin 36498 Scribunto text/plain --[==[TODO: * Change certain flag names, as some are misnomers: * Change `allow_holes` to `keep_holes`, because it's not the inverse of `disallow_holes`. * Change `allow_empty` to `keep_empty`, as it causes them to be kept as "" instead of deleted. * Sort out all the internal error calls. Manual error(format()) calls are used when certain parameters shouldn't be dumped, so find a way to avoid that. ]==] local export = {} local collation_module = "Module:collation" local families_module = "Module:families" local functions_module = "Module:fun" local gender_and_number_utilities_module = "Module:gender and number utilities" local labels_module = "Module:labels" local languages_module = "Module:languages" local math_module = "Module:math" local pages_module = "Module:pages" local parameters_finalize_set_module = "Module:parameters/finalizeSet" local parameters_track_module = "Module:parameters/track" local parse_utilities_module = "Module:parse utilities" local references_module = "Module:references" local scribunto_module = "Module:Scribunto" local scripts_module = "Module:scripts" local string_utilities_module = "Module:string utilities" local table_module = "Module:table" local wikimedia_languages_module = "Module:wikimedia languages" local yesno_module = "Module:yesno" local mw = mw local mw_title = mw.title local string = string local table = table local dump = mw.dumpObject local find = string.find local format = string.format local gsub = string.gsub local insert = table.insert local ipairs = ipairs local list_to_text = mw.text.listToText local make_title = mw_title.makeTitle local match = string.match local max = math.max local new_title = mw_title.new local next = next local pairs = pairs local pcall = pcall local require = require local sub = string.sub local tonumber = tonumber local type = type local unpack = unpack or table.unpack -- Lua 5.2 compatibility local current_title_text, current_namespace, sets -- Defined when needed. local namespaces = mw.site.namespaces --[==[ 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 decode_entities(...) decode_entities = require(string_utilities_module).decode_entities return decode_entities(...) end local function extend(...) extend = require(table_module).extend return extend(...) end local function finalize_set(...) finalize_set = require(parameters_finalize_set_module) return finalize_set(...) end local function get_family_by_code(...) get_family_by_code = require(families_module).getByCode return get_family_by_code(...) end local function get_family_by_name(...) get_family_by_name = require(families_module).getByCanonicalName return get_family_by_name(...) end local function get_language_by_code(...) get_language_by_code = require(languages_module).getByCode return get_language_by_code(...) end local function get_language_by_name(...) get_language_by_name = require(languages_module).getByCanonicalName return get_language_by_name(...) end local function get_script_by_code(...) get_script_by_code = require(scripts_module).getByCode return get_script_by_code(...) end local function get_script_by_name(...) get_script_by_name = require(scripts_module).getByCanonicalName return get_script_by_name(...) end local function get_wm_lang_by_code(...) get_wm_lang_by_code = require(wikimedia_languages_module).getByCode return get_wm_lang_by_code(...) end local function get_wm_lang_by_code_with_fallback(...) get_wm_lang_by_code_with_fallback = require(wikimedia_languages_module).getByCodeWithFallback return get_wm_lang_by_code_with_fallback(...) end local function gsplit(...) gsplit = require(string_utilities_module).gsplit return gsplit(...) end local function is_callable(...) is_callable = require(functions_module).is_callable return is_callable(...) end local function is_integer(...) is_integer = require(math_module).is_integer return is_integer(...) end local function is_internal_title(...) is_internal_title = require(pages_module).is_internal_title return is_internal_title(...) end local function is_positive_integer(...) is_positive_integer = require(math_module).is_positive_integer return is_positive_integer(...) end local function iterate_list(...) iterate_list = require(table_module).iterateList return iterate_list(...) end local function num_keys(...) num_keys = require(table_module).numKeys return num_keys(...) end local function parse_gender_and_number_spec(...) parse_gender_and_number_spec = require(gender_and_number_utilities_module).parse_gender_and_number_spec return parse_gender_and_number_spec(...) end local function parse_references(...) parse_references = require(references_module).parse_references return parse_references(...) end local function pattern_escape(...) pattern_escape = require(string_utilities_module).pattern_escape return pattern_escape(...) end local function php_trim(...) php_trim = require(scribunto_module).php_trim return php_trim(...) end local function scribunto_parameter_key(...) scribunto_parameter_key = require(scribunto_module).scribunto_parameter_key return scribunto_parameter_key(...) end local function sort(...) sort = require(collation_module).sort return sort(...) end local function sorted_pairs(...) sorted_pairs = require(table_module).sortedPairs return sorted_pairs(...) end local function split(...) split = require(string_utilities_module).split return split(...) end local function split_labels_on_comma(...) split_labels_on_comma = require(labels_module).split_labels_on_comma return split_labels_on_comma(...) end local function split_on_comma(...) split_on_comma = require(parse_utilities_module).split_on_comma return split_on_comma(...) end local function tonumber_extended(...) tonumber_extended = require(math_module).tonumber_extended return tonumber_extended(...) end local function track(...) track = require(parameters_track_module) return track(...) end local function yesno(...) yesno = require(yesno_module) return yesno(...) end --[==[ intro: This module is used to standardize template argument processing and checking. A typical workflow is as follows (based on [[Module:translations]]): { ... local parent_args = frame:getParent().args local params = { [1] = {required = true, type = "language", default = "und"}, [2] = true, [3] = {list = true}, ["alt"] = true, ["id"] = true, ["sc"] = {type = "script"}, ["tr"] = true, ["ts"] = true, ["lit"] = true, } local args = require("Module:parameters").process(parent_args, params) -- Do further processing of the parsed arguments in `args`. ... } The `params` table should have the parameter names as the keys, and a (possibly empty) table of parameter tags as the value. An empty table as the value merely states that the parameter exists, but should not receive any special treatment; if desired, empty tables can be replaced with the value `true` as a perforamnce optimization. Possible parameter tags are listed below: ; {required = true} : The parameter is required; an error is shown if it is not present. The template's page itself is an exception; no error is shown there. ; {default =} : Specifies a default input value for the parameter, if it is absent or empty. This will be processed as though it were the input instead, so (for example) {default = "und"} with the type {"language"} will return a language object for [[:Category:Undetermined language|Undetermined language]] if no language code is provided. When used on list parameters, this specifies a default value for the first item in the list only. Note that it is not possible to generate a default that depends on the value of other parameters. If used together with {required = true}, the default applies only to template pages (see the following entry), as a side effect of the fact that "required" parameters aren't actually required on template pages. This can be used to show an example of the template in action when the template page is visited; however, it is preferred to use `template_default` for this purpose, for clarity. ; {template_default =} : Specifies a default input value for absent or empty parameters only on the template demo invocation (the invocation of the template that is displayed when the template page that implements the template is viewed). Template pages are pages in template space that invoke (through {{tl|#invoke:}}) the module that implements the template and calls [[Module:parameters]]. For example, the page [[Template:en-noun]] implements the {{tl|en-noun}} template, which in turn invokes [[Module:en-headword]], and is a template page for [[Module:en-headword]]. When the template page [[Template:en-noun]] is visited, the {{tl|#invoke:}} of the template's module is expanded as if the template were called without arguments, and the output is inserted at that point into the processed page. This output serves as a sort of demo of the template's functionality. `template_default` can be used to supply default values for use only in this demo. Since the template page may also contain other invocations of the same template (e.g. on the template's documentation page, which is typically transcluded into the template page itself), `template_default` does not apply if there are any arguments passed to the template or if the template is invoked on any other page but its own template page (which is checked by comparing the name of the invoking template to the current pagename). Both `template_default` and `default` can be specified for the same parameter. If this is done, `template_default` applies for the argumentless template invocation on the template page, and `default` in all other circumstances As an example, {{tl|cs-IPA}} uses the equivalent of {[1] = {default = "+", template_default = "příklad"}} to supply a default of {"+"} for mainspace and documentation pages (which tells the module to use the value of the {{para|pagename}} parameter, falling back to the actual pagename), but {"příklad"} (which means "example"), on [[Template:cs-IPA]]. ; {alias_of =} : Treat the parameter as an alias of another. When arguments are specified for this parameter, they will automatically be renamed and stored under the alias name. This allows for parameters with multiple alternative names, while still treating them as if they had only one name. The conversion-related properties of an aliased parameter (e.g. `type`, `set`, `convert`, `sublist`) are taken from the aliasee, and the corrresponding properties set on the alias itself are ignored; but other properties on the alias are taken from the alias's spec and not from the aliasee's spec. This means, for example, that if you create an alias of a list parameter, the alias must also specify the `list` property or it is not a list. (In such a case, a value specified for the alias goes into the first item of the aliasee's list. You cannot make a list alias of a non-list parameter; this causes an error to be thrown.) Similarly, if you specify `separate_no_index` on an aliasee but not on the alias, uses of the unindexed aliasee parameter are stored into the `.default` key, but uses of the unindexed alias are stored into the first numbered key of the aliasee's list. Aliases cannot be required, as this prevents the other name or names of the parameter from being used. Parameters that are aliases and required at the same time cause an error to be thrown. ; {allow_empty = true} : If the argument is an empty string value, it is not converted to {nil}, but kept as-is. The use of `allow_empty` is disallowed if a type has been specified, and causes an error to be thrown. ; {no_trim = true} : Spacing characters such as spaces and newlines at the beginning and end of a positional parameter are not removed. (MediaWiki itself automatically trims spaces and newlines at the edge of named parameters.) The use of `no_trim` is disallowed if a type has been specified, and causes an error to be thrown. ; {type =} : Specifies what value type to convert the argument into. The default is to leave it as a text string. Alternatives are: :; {type = "boolean"} :: The value is treated as a boolean value, either true or false. No value, the empty string, and the strings {"0"}, {"no"}, {"n"}, {"false"}, {"f"} and {"off"} are treated as {false}, all other values are considered {true}. :; {type = "number"} :: The value is converted into a number, and throws an error if the value is not parsable as a number. Input values may be signed (`+` or `-`), and may contain decimal points and leading zeroes. If {allow_hex = true}, then hexadecimal values in the form {"0x100"} may optionally be used instead, which otherwise have the same syntax restrictions (including signs, decimal digits, and leading zeroes after {"0x"}). Hexadecimal inputs are not case-sensitive. Lua's special number values (`inf` and `nan`) are not possible inputs. :; {type = "range"} :: The value is interpreted as a hyphen-separated range of two numbers (e.g. {"2-4"} is interpreted as the range from {2} to {4}). A number input without a hyphen is interpreted as a range from that number to itself (e.g. the input {"1"} is interpreted as the range from {1} to {1}). Any optional flags which are available for numbers will also work for ranges. :; {type = "language"} :: The value is interpreted as a full or [[Wiktionary:Languages#Etymology-only languages|etymology-only language]] code language code (or name, if {method = "name"}) and converted into the corresponding object (see [[Module:languages]]). If the code or name is invalid, then an error is thrown. The additional setting {family = true} can be given to allow [[Wiktionary:Language families|language family codes]] to be considered valid and the corresponding object returned. Note that to distinguish an etymology-only language object from a full language object, use {object:hasType("language", "etymology-only")}. :; {type = "full language"} :: The value is interpreted as a full language code (or name, if {method = "name"}) and converted into the corresponding object (see [[Module:languages]]). If the code or name is invalid, then an error is thrown. Etymology-only languages are not allowed. The additional setting {family = true} can be given to allow [[Wiktionary:Language families|language family codes]] to be considered valid and the corresponding object returned. :; {type = "Wikimedia language"} :: The value is interpreted as a code and converted into a Wikimedia language object. If the code is invalid, then an error is thrown. If {fallback = true} is specified, conventional language codes which are different from their Wikimedia equivalent will also be accepted as a fallback. :; {type = "family"} :: The value is interpreted as a language family code (or name, if {method = "name"}) and converted into the corresponding object (see [[Module:families]]). If the code or name is invalid, then an error is thrown. :; {type = "script"} :: The value is interpreted as a script code (or name, if {method = "name"}) and converted into the corresponding object (see [[Module:scripts]]). If the code or name is invalid, then an error is thrown. :; {type = "title"} :: The value is interpreted as a page title and converted into the corresponding object (see the [[mw:Extension:Scribunto/Lua_reference_manual#Title_library|Title library]]). If the page title is invalid, then an error is thrown; by default, external titles (i.e. those on other wikis) are not treated as valid. Options are: ::; {namespace = n} ::: The default namespace, where {n} is a namespace number; this is treated as {0} (the mainspace) if not specified. ::; {allow_external = true} ::: External titles are treated as valid. ::; {prefix = "namespace override"} (default) ::: The default namespace prefix will be prefixed to the value is already prefixed by a namespace prefix. For instance, the input {"Foo"} with namespace {10} returns {"Template:Foo"}, {"Wiktionary:Foo"} returns {"Wiktionary:Foo"}, and {"Template:Foo"} returns {"Template:Foo"}. Interwiki prefixes cannot act as overrides, however: the input {"fr:Foo"} returns {"Template:fr:Foo"}. ::; {prefix = "force"} ::: The default namespace prefix will be prefixed unconditionally, even if the value already appears to be prefixed. This is the way that {{tl|#invoke:}} works when calling modules from the module namespace ({828}): the input {"Foo"} returns {"Module:Foo"}, {"Wiktionary:Foo"} returns {"Module:Wiktionary:Foo"}, and {"Module:Foo"} returns {"Module:Module:Foo"}. ::; {prefix = "full override"} ::: The same as {prefix = "namespace override"}, except that interwiki prefixes can also act as overrides. For instance, {"el:All topics"} with namespace {14} returns {"el:Category:All topics"}. Due to the limitations of MediaWiki, only the first prefix in the value may act as an override, so the namespace cannot be overridden if the first prefix is an interwiki prefix: e.g. {"el:Template:All topics"} with namespace {14} returns {"el:Category:Template:All topics"}. :; {type = "parameter"} :: The value is interpreted as the name of a parameter, and will be normalized using the method that Scribunto uses when constructing a {frame.args} table of arguments. This means that integers will be converted to numbers, but all other arguments will remain as strings (e.g. {"1"} will be normalized to {1}, but {"foo"} and {"1.5"} will remain unchanged). Note that Scribunto also trims parameter names, following the same trimming method that this module applies by default to all parameter types. :: This type is useful when one set of input arguments is used to construct a {params} table for use in a subsequent {export.process()} call with another set of input arguments; for instance, the set of valid parameters for a template might be defined as {{tl|#invoke:[some module]|args=}} in the template, where {args} is a sublist of valid parameters for the template. :; {type = "qualifier"} :: The value is interpreted as a qualifier and converted into the correct format for passing into `format_qualifiers()` in [[Module:qualifier]] (which currently just means converting it to a one-item list). :; {type = "labels"} :: The value is interpreted as a comma-separated list of labels and converted into the correct format for passing into `show_labels()` in [[Module:labels]] (which is currently a list of strings). Splitting is done on commas not followed by whitespace, except that commas inside of double angle brackets do not count even if not followed by whitespace. This type should be used by for normal labels (typically specified using {{para|l}} or {{para|ll}}) and accent qualifiers (typically specified using {{para|a}} and {{para|aa}}). :; {type = "references"} :: The value is interpreted as one or more references, in the format prescribed by `parse_references()` in [[Module:references]], and converted into a list of objects of the form accepted by `format_references()` in the same module. If a syntax error is found in the reference format, an error is thrown. :; {type = "genders"} :: The value is interpreted as one or more comma-separated gender/number specs, in the format prescribed by [[Module:gender and number]]. Inline modifiers (`<q:...>`, `<qq:...>`, `<l:...>`, `<ll:...>` or `<ref:...>`) may be attached to a gender/number spec. :; {type = "form of tags"} :: The value is interpreted as an ampersand-separated list of grammar tags and converted into the correct format for passing as `tags` into `tagged_inflections()` in [[Module:form of]] (which is currently a list of strings). Splitting is always done by ampersands. This type should be used by for inflection qualifiers that act as grammar tags (typically specified using {{para|infl}}). :; {type = function(val) ... end} :: `type` may be set to a function (or callable table), which must take the argument value as its sole argument, and must output one of the other recognized types. This is particularly useful for lists (see below), where certain values need to be interpreted differently to others. ; {list =} : Treat the parameter as a list of values, each having its own parameter name, rather than a single value. The parameters will have a number at the end, except optionally for the first (but see also {require_index = true}). For example, {list = true} on a parameter named "head" will include the parameters {{para|head}} (or {{para|head1}}), {{para|head2}}, {{para|head3}} and so on. If the parameter name is a number, another number doesn't get appended, but the counting simply continues, e.g. for parameter {3} the sequence is {{para|3}}, {{para|4}}, {{para|5}} etc. List parameters are returned as numbered lists, so for a template that is given the parameters `|head=a|head2=b|head3=c`, the processed value of the parameter {"head"} will be { { "a", "b", "c" }}}. : The value for {list =} can also be a string. This tells the module that parameters other than the first should have a different name, which is useful when the first parameter in a list is a number, but the remainder is named. An example would be for genders: {list = "g"} on a parameter named {1} would have parameters {{para|1}}, {{para|g2}}, {{para|g3}} etc. : If the number is not located at the end, it can be specified by putting {"\1"} at the number position. For example, parameters {{para|f1accel}}, {{para|f2accel}}, ... can be captured by using the parameter name {"f\1accel"}, as is done in [[Module:headword/templates]]. ; {set =} : Require that the value of the parameter be one of the specified values (or omitted, if {required = true} isn't given). Two formats are allowed; either a list of possible values can be supplied, or a table can be supplied where the keys are allowed values and the values are either `true` or a string naming a value found elsewhere in the table as a key. In the latter case, the key is an alias and the value is the canonical value, and if the user uses the alias, it will automatically be mapped to the canonical value. In such a case, the canonical value cannot itself be an alias. The use of `set` is disallowed if {type = "boolean"} and causes an error to be thrown. ; {sublist =} : The value of the parameter is a delimiter-separated list of individual raw values. The resulting field in `args` will be a Lua list (i.e. a table with numeric indices) of the converted values. If {sublist = true} is given, the values will be split on commas (possibly with whitespace on one or both sides of the comma, which is ignored). If {sublist = "comma without whitespace"} is given, the values will be split on commas which are not followed by whitespace, and which aren't preceded by an escaping backslash. Otherwise, the value of `sublist` should be either a Lua pattern specifying the delimiter(s) to split on or a function (or callable table) to do the splitting, which is passed two values (the value to split and a function to signal an error) and should return a list of the split values. ; {convert =} : If given, this specifies a function (or callable table) to convert the raw parameter value into the Lua object used during further processing. The function is passed two arguments, the raw parameter value itself and a function used to signal an error during parsing or conversion, and should return one value, the converted parameter. The error-signaling function contains the name and raw value of the parameter embedded into the message it generates, so these do not need to specified in the message passed into it. If `type` is specified in conjunction with `convert`, the processing by `type` happens first. If `sublist` is given in conjunction with `convert`, the raw parameter value will be split appropriately and `convert` called on each resulting item. ; {allow_hex = true} : When used in conjunction with {type = "number"}, allows hexadecimal numbers as inputs, in the format {"0x100"} (which is not case-sensitive). ; {family = true} : When used in conjunction with {type = "language"}, allows [[Wiktionary:Language families|language family codes]] to be returned. To check if a given object refers to a language family, use {object:hasType("family")}. ; {method = "name"} : When used in conjunction with {type = "language"}, {type = "family"} or {type = "script"}, checks for and parses a language, family or script name instead of a code. ; {allow_holes = true} : This is used in conjunction with list-type parameters. By default, the values are tightly packed in the resulting list. This means that if, for example, an entry specified `head=a|head3=c` but not {{para|head2}}, the returned list will be { {"a", "c"}}}, with the values stored at the indices {1} and {2}, not {1} and {3}. If it is desirable to keep the numbering intact, for example if the numbers of several list parameters correlate with each other (like those of {{tl|affix}}), then this tag should be specified. : If {allow_holes = true} is given, there may be {nil} values in between two real values, which makes many of Lua's table processing functions no longer work, like {#} or {ipairs()}. To remedy this, the resulting table will contain an additional named value, `maxindex`, which tells you the highest numeric index that is present in the table. In the example above, the resulting table will now be { { "a", nil, "c", maxindex = 3}}}. That way, you can iterate over the values from {1} to `maxindex`, while skipping {nil} values in between. ; {disallow_holes = true} : This is used in conjunction with list-type parameters. As mentioned above, normally if there is a hole in the source arguments, e.g. `head=a|head3=c` but not {{para|head2}}, it will be removed in the returned list. If {disallow_holes = true} is specified, however, an error is thrown in such a case. This should be used whenever there are multiple list-type parameters that need to line up (e.g. both {{para|head}} and {{para|tr}} are available and {{para|head3}} lines up with {{para|tr3}}), unless {allow_holes = true} is given and you are prepared to handle the holes in the returned lists. ; {disallow_missing = true} : This is similar to {disallow_holes = true}, but an error will not be thrown if an argument is blank, rather than completely missing. This may be used to tolerate intermediate blank numerical parameters, which sometimes occur in list templates. For instance, `head=a|head2=|head3=c` will not throw an error, but `head=a|head3=c` will. ; {require_index = true} : This is used in conjunction with list-type parameters. By default, the first parameter can have its index omitted. For example, a list parameter named `head` can have its first parameter specified as either {{para|head}} or {{para|head1}}. If {require_index = true} is specified, however, only {{para|head1}} is recognized, and {{para|head}} will be treated as an unknown parameter. {{tl|affixusex}} (and variants {{tl|suffixusex}}, {{tl|prefixusex}}) use this, for example, on all list parameters. ; {separate_no_index = true} : This is used to distinguish between {{para|head}} and {{para|head1}} as different parameters. For example, in {{tl|affixusex}}, to distinguish between {{para|sc}} (a script code for all elements in the usex's language) and {{para|sc1}} (the script code of the first element, used when the first element is prefixed with a language code to indicate that it is in a different language). When this is used, the resulting table will contain an additional named value, `default`, which contains the value for the indexless argument. ; {flatten = true} : This is used in conjunction with list-type parameters when `sublist` or a list-generating type such as {"labels"} or {"genders"} is also specified, and causes the resulting list to be flattened. Not currently compatible with {allow_holes = true}. ; {replaced_by =} : Specifies that the parameter is no longer valid, and has been replaced by some other mechanism. If the value of `replaced_by` is a string, it is the name of the new parameter to use instead. Use the `reason` tag to specify the reason why this change has been made, e.g. {reason = "for consistency with the corresponding parameter in other Romance-language headword templates"}. If the value of `replaced_by` is {false}, there is no replacement parameter. In this case, `instead` should be supplied with a description of what to do instead, e.g. {instead = "use an inline modifier on |2= such as <q:...>, <qq:...>, <l:...> or <ll:...>"}. You can also supply a justification in `reason` if you feel it is appropriate or necessary to do so. ; {reason =} : When used in conjunction with `replaced_by`, specifies the reason for the parameter replacement. ; {instead =} : When used in conjunction with {replaced_by = false}, specifies what to do instead of using the removed parameter. ; {demo = true} : This is used as a way to ensure that the parameter is only enabled on the template's own page (and its documentation page), and in the User: namespace; otherwise, it will be treated as an unknown parameter. This should only be used if special settings are required to showcase a template in its documentation (e.g. adjusting the pagename or disabling categorization). In most cases, it should be possible to do this without using demo parameters, but they may be required if a template/documentation page also contains real uses of the same template as well (e.g. {{tl|shortcut}}), as a way to distinguish them. ; {deprecated = true} : This is for tracking the use of deprecated parameters, including any aliases that are being brought out of use. See [[Wiktionary:Tracking]] for more information. ]==] -- Returns true if the current page is a template or module containing the current {{#invoke}}. -- If the include_documentation argument is given, also returns true if the current page is either page's documentation page. local own_page, own_page_or_documentation local function is_own_page(include_documentation) if own_page == nil then if current_namespace == nil then local current_title = mw_title.getCurrentTitle() current_title_text, current_namespace = current_title.prefixedText, current_title.namespace end local frame = current_namespace == 828 and mw.getCurrentFrame() or current_namespace == 10 and mw.getCurrentFrame():getParent() if frame then local frame_title_text = frame:getTitle() own_page = current_title_text == frame_title_text own_page_or_documentation = own_page or current_title_text == frame_title_text .. "/documentation" else own_page, own_page_or_documentation = false, false end end return include_documentation and own_page_or_documentation or own_page end -------------------------------------- Some helper functions ----------------------------- -- Convert a list in `list` to a string, separating the final element from the preceding one(s) by `conjunction`. If -- `dump_vals` is given, pass all values in `list` through mw.dumpObject() (WARNING: this destructively modifies -- `list`). This is similar to serialCommaJoin() in [[Module:table]] when used with the `dontTag = true` option, but -- internally uses mw.text.listToText(). local function concat_list(list, conjunction, dump_vals) if dump_vals then for k, v in pairs(list) do list[k] = dump(v) end end return list_to_text(list, nil, conjunction) end -- A helper function for use with generating error-signaling functions in the presence of raw value conversion. Format a -- message `msg`, including the processed value `processed` if it is different from the raw value `rawval`; otherwise, -- just return `msg`. local function msg_with_processed(msg, rawval, processed) if rawval == processed then return msg end local processed_type = type(processed) return format("%s (processed value %s)", msg, (processed_type == "string" or processed_type == "number") and processed or dump(processed) ) end -- Separate form of tags with ampersand (&). local function split_tags_on_ampersand(tags) return split(tags, "&") end -------------------------------------- Error handling ----------------------------- local function process_error(fmt, ...) local args = {...} for i, val in ipairs(args) do args[i] = dump(val) end if type(fmt) == "table" then -- hacky signal that we're called from internal_process_error(), and not to omit stack frames return error(format(fmt[1], unpack(args))) end return error(format(fmt, unpack(args)), 3) end local function internal_process_error(fmt, ...) process_error({"Internal error in `params` table: " .. fmt}, ...) end -- Check that a parameter or argument is in the form form Scribunto normalizes input argument keys into (e.g. 1 not "1", "foo" not " foo "). Otherwise, it won't be possible to normalize inputs in the expected way. Unless is_argument is set, also check that the name only contains one placeholder at most, and that strings don't resolve to numeric keys once the placeholder has been substituted. local function validate_name(name, desc, extra_name, is_argument) local normalized = scribunto_parameter_key(name) if name and name == normalized then if is_argument or type(name) ~= "string" then return end local placeholder = find(name, "\1", nil, true) if not placeholder then return elseif find(name, "\1", placeholder + 1, true) then error(format( "Internal error: expected %s to only contain one placeholder, but saw %s", extra_name and (desc .. dump(extra_name)) or desc, dump(name) )) end local first_name = gsub(name, "\1", "1") normalized = scribunto_parameter_key(first_name) if first_name == normalized then return end error(format( "Internal error: %s cannot resolve to numeric parameters once any placeholder has been substituted, but %s resolves to %s", extra_name and (desc .. dump(extra_name)) or desc, dump(name), dump(normalized) )) elseif normalized == nil then error(format( "Internal error: expected %s to be of type string or number, but saw %s", extra_name and (desc .. dump(extra_name)) or desc, type(name) )) end error(format( "Internal error: expected %s to be Scribunto-compatible: %s (a %s) should be %s (a %s)", extra_name and (desc .. dump(extra_name)) or desc, dump(name), type(name), dump(normalized), type(normalized) )) end local function validate_alias_options(...) local invalid = { required = true, default = true, template_default = true, allow_holes = true, disallow_holes = true, disallow_missing = true, } function validate_alias_options(param, name, main_param, alias_of) for k in pairs(param) do if invalid[k] then track("bad alias option") -- internal_process_error( -- "parameter %s cannot have the option %s, as it is an alias of parameter %s.", -- name, option, alias_of -- ) end end -- Soon, aliases will inherit options from the main parameter via __index. Track cases where this would happen. if main_param ~= true then for k in pairs(main_param) do if param[k] == nil and not invalid[k] then if k == "list" then -- these need to be changed to list = false to retain current behaviour track("mismatched list alias option") elseif not (k == "type" or k == "set" or k == "sublist") then -- rarely specified on aliases, as they're effectively inherited already track("mismatched alias option") end end end end end validate_alias_options(...) end -- TODO: give ranges instead of long lists, if possible. --[==[ func: export.params_list_error(params, msg) Given a key-value table of raw parameters `params`, display an error message about all the parameters seen in the table. The parameter names are displayed in sorted order. `msg` should be e.g. {"required"} or {"not used by this template"}. This is used internally to display error messages about required or invalid parameters, and can be used for the same purpose by code that processes its own parameters (e.g. if the `return_unknown` flag is specified to `process`). ]==] local function params_list_error(params, msg) local list, n = {}, 0 for name in sorted_pairs(params) do n = n + 1 list[n] = name end error(format( "Parameter%s %s.", format(n == 1 and " %s is" or "s %s are", concat_list(list, " and ", true)), msg ), 3) end export.params_list_error = params_list_error -- Helper function for use with convert_val_error(). Format a list of possible choices using `concat_list` and -- conjunction "or", displaying "either " before the choices if there's more than one. local function format_choice_list(valid) return (#valid > 1 and "either " or "") .. concat_list(valid, " or ") end -- Signal an error for a value `val` that is not of the right type `valid` (which is either a string specifying a type, or -- a list of possible values, in the case where `set` was used). `name` is the name of the parameter and can be a -- function to signal an error (which is assumed to automatically display the parameter's name and value). `seetext` is -- an optional additional explanatory link to display (e.g. [[WT:LOL]], the list of possible languages and codes). local function convert_val_error(val, name, valid, seetext) if is_callable(name) then if type(valid) == "table" then valid = "choice, must be " .. format_choice_list(valid) end name(format("Invalid %s; the value %s is not valid%s", valid, val, seetext and "; see " .. seetext or "")) else if type(valid) == "table" then valid = format_choice_list(valid) else valid = "a valid " .. valid end error(format("Parameter %s must be %s; the value %s is not valid.%s", dump(name), valid, dump(val), seetext and " See " .. seetext .. "." or "")) end end -- Generate the appropriate error-signaling function given parameter value `val` and name `name`. If `name` is already -- a function, it is just returned; otherwise a function is generated and returned that displays the passed-in messaeg -- along with the parameter's name and value. local function make_parse_err(val, name) if is_callable(name) then return name end return function(msg) error(format("%s: parameter %s=%s", msg, name, val)) end end -------------------------------------- Value conversion ----------------------------- -- For a list parameter `name` and corresponding value `list_name` of the `list` field (which should have the same value -- as `name` if `list = true` was given), generate a pattern to match parameters of the list and store the pattern as a -- key in `patterns`, with corresponding value set to `name`. For example, if `list_name` is "tr", the pattern will -- match "tr" as well as "tr1", "tr2", ..., "tr10", "tr11", etc. If the `list_name` contains a \1 in it, the numeric -- portion goes in place of the \1. For example, if `list_name` is "f\1accel", the pattern will match "faccel", -- "f1accel", "f2accel", etc. Any \1 in `name` is removed before storing into `patterns`. local function save_pattern(name, list_name, patterns) name = type(name) == "string" and gsub(name, "\1", "") or name if find(list_name, "\1", nil, true) then patterns["^" .. gsub(pattern_escape(list_name), "\1", "([1-9]%%d*)") .. "$"] = name else patterns["^" .. pattern_escape(list_name) .. "([1-9]%d*)$"] = name list_name = list_name .. "\1" end validate_name(list_name, "the list field of parameter ", name) return patterns end -- A helper function for use with `sublist`. It is an iterator function for use in a for-loop that returns split -- elements of `val` using `sublist` (a Lua split pattern; boolean `true` to split on commas optionally surrounded by -- whitespace; "comma without whitespace" to split only on commas not followed by whitespace which have not been escaped -- by a backslash; or a function to do the splitting, which is passed two values, the value to split and a function to -- signal an error, and should return a list of the split elements). `name` is the parameter name or error-signaling -- function passed into convert_val(). local function split_sublist(val, name, sublist) if sublist == true then return gsplit(val, "%s*,%s*") -- Split an argument on comma, but not comma followed by whitespace. elseif sublist == "comma without whitespace" then -- If difficult cases, use split_on_comma. if find(val, "\\", nil, true) or match(val, ",%s") then return iterate_list(split_on_comma(val)) end -- Otherwise, use gsplit. return gsplit(val, ",") elseif type(sublist) == "string" then return gsplit(val, sublist) elseif not is_callable(sublist) then error(format('Internal error: expected `sublist` to be of type "string" or "function" or boolean `true`, but saw %s', dump(sublist))) end return iterate_list(sublist(val, make_parse_err(val, name))) end -- For parameter named `name` with value `val` and param spec `param`, if the `set` field is specified, verify that the -- value is one of the one specified in `set`, and throw an error otherwise. `name` is taken directly from the -- corresponding parameter passed into convert_val() and may be a function to signal an error. Optional `param_type` is -- a string specifying the conversion type of `val` and is used for special-casing: If `param_type` is "boolean", an -- internal error is thrown (since `set` cannot be used in conjunction with booleans) and if `param_type` is "number", -- no checking happens because in this case `set` contains numbers and is checked inside the number conversion function -- itself, after converting `val` to a number. Return the canonical value of `val` (which may be different from `val` -- if an alias map is given). local function check_set(val, name, param, param_type) if param_type == "boolean" then error(format('Internal error: cannot use `set` with `type = "%s"`', param_type)) -- Needs to be special cased because the check happens after conversion to numbers. elseif param_type == "number" then return val end local set, map = param.set if sets == nil then map = finalize_set(set, name) sets = {[set] = map} else map = sets[set] if map == nil then map = finalize_set(set, name) sets[set] = map end end local newval = map[val] if newval == true then return val elseif newval ~= nil then return newval end local list = {} for k, v in sorted_pairs(map) do if v == true then insert(list, dump(k)) else insert(list, ("%s (alias of %s)"):format(dump(k), dump(v))) end end -- If the parameter is not required then put "or empty" at the end of the list, to avoid implying the parameter is actually required. if not param.required then insert(list, "empty") end convert_val_error(val, name, list) end local function convert_language(val, name, param, allow_etym) local method, func = param.method if method == nil or method == "code" then func, method = get_language_by_code, "code" elseif method == "name" then func, method = get_language_by_name, "name" else error(format('Internal error: expected `method` for type `language` to be "code", "name" or undefined, but saw %s', dump(method))) end local lang = func(val, nil, allow_etym, param.family) if lang then return lang end local list, links = {"language"}, {"[[WT:LOL]]"} if allow_etym then insert(list, "etymology language") insert(links, "[[WT:LOL/E]]") end if param.family then insert(list, "family") insert(links, "[[WT:LOF]]") end convert_val_error(val, name, concat_list(list, " or ") .. " " .. (method == "name" and "name" or "code"), concat_list(links, " and ")) end local function convert_number(val, allow_hex) -- Call tonumber_extended with the `real_finite` flag, which filters out ±infinity and NaN. -- By default, specify base 10, which prevents 0x hex inputs from being converted. -- If `allow_hex` is set, then don't give a base, which means 0x hex inputs will work. local num = tonumber_extended(val, not allow_hex and 10 or nil, "finite_real") if not num then return num end if match(val, "[eEpP.]") then -- float track("number not an integer") end if find(val, "+", nil, true) then track("number with +") end -- Track various unusual number inputs to determine if it should be restricted to positive integers by default (possibly including 0). if not is_positive_integer(num) then track("number not a positive integer") if num == 0 then track("number is 0") elseif not is_integer(num) then track("number not an integer") end end return num end -- TODO: validate parameter specs separately, as it's making the handler code really messy at the moment. local type_handlers = setmetatable({ ["boolean"] = function(val) return yesno(val, true) end, ["family"] = function(val, name, param) local method, func = param.method if method == nil or method == "code" then func, method = get_family_by_code, "code" elseif method == "name" then func, method = get_family_by_name, "name" else error(format('Internal error: expected `method` for type `family` to be "code", "name" or undefined, but saw %s', dump(method))) end return func(val) or convert_val_error(val, name, "family " .. method, "[[WT:LOF]]") end, ["labels"] = function(val, name, param) -- FIXME: Should be able to pass in a parse_err function. return split_labels_on_comma(val) end, ["form of tags"] = function(val, name, param) return split_tags_on_ampersand(val) end, ["language"] = function(val, name, param) return convert_language(val, name, param, true) end, ["full language"] = convert_language, ["number"] = function(val, name, param) local allow_hex = param.allow_hex if allow_hex and allow_hex ~= true then error(format( 'Internal error: expected `allow_hex` for type `number` to be of type "boolean" or undefined, but saw %s', dump(allow_hex) )) end local num = convert_number(val, allow_hex) if param.set then -- Don't pass in "number" here; otherwise no checking will happen. num = check_set(num, name, param) end if num then return num end convert_val_error(val, name, (allow_hex and "decimal or hexadecimal " or "") .. "number") end, ["range"] = function(val, name, param) local allow_hex = param.allow_hex if allow_hex and allow_hex ~= true then error(format( 'Internal error: expected `allow_hex` for type `range` to be of type "boolean" or undefined, but saw %s', dump(allow_hex) )) end -- Pattern ensures leading minus signs are accounted for. local m1, m2 = match(val, "^(%s*%S.-)%-(%s*%S.*)") if m1 then m1 = convert_number(m1, allow_hex) if m1 then m2 = convert_number(m2, allow_hex) if m2 then return {m1, m2} end end end -- Try `val` if it couldn't be split into a range, and return a range of `val` to `val` if possible. local num = convert_number(val, allow_hex) if num then return {num, num} end convert_val_error(val, name, (allow_hex and "decimal or hexadecimal " or "") .. "number or a hyphen-separated range of two numbers") end, ["parameter"] = function(val, name, param) -- Use the `no_trim` option, as any trimming will have already been done. return scribunto_parameter_key(val, true) end, ["qualifier"] = function(val, name, param) return {val} end, ["references"] = function(val, name, param) return parse_references(val, make_parse_err(val, name)) end, ["genders"] = function(val, name, param) if not val:find("[,<]") then return {{spec = val}} end -- NOTE: We don't pass in allow_space_around_comma. Consistent with other comma-separated types, there shouldn't -- be spaces around the comma. return parse_gender_and_number_spec { spec = val, parse_err = make_parse_err(val, name), allow_multiple = true, } end, ["script"] = function(val, name, param) local method, func = param.method if method == nil or method == "code" then func, method = get_script_by_code, "code" elseif method == "name" then func, method = get_script_by_name, "name" else error(format('Internal error: expected `method` for type `script` to be "code", "name" or undefined, but saw %s', dump(method))) end return func(val) or convert_val_error(val, name, "script " .. method, "[[WT:LOS]]") end, ["string"] = function(val, name, param) -- To be removed as unnecessary. track("string") return val end, -- TODO: add support for resolving to unsupported titles. -- TODO: split this into "page name" (i.e. internal) and "link target" (i.e. external as well), which is more intuitive. ["title"] = function(val, name, param) local namespace = param.namespace if namespace == nil then namespace = 0 else local valid_type = type(namespace) ~= "number" and 'of type "number" or undefined' or not namespaces[namespace] and "a valid namespace number" or nil if valid_type then error(format('Internal error: expected `namespace` for type `title` to be %s, but saw %s', valid_type, dump(namespace))) end end -- Decode entities. WARNING: mw.title.makeTitle must be called with `decoded` (as it doesn't decode) and mw.title.new must be called with `val` (as it does decode, so double-decoding needs to be avoided). local decoded, prefix, title = decode_entities(val), param.prefix -- If the input is a fragment, treat the title as the current title with the input fragment. if sub(decoded, 1, 1) == "#" then -- If prefix is "force", only get the current title if it's in the specified namespace. current_title includes the namespace prefix. if current_namespace == nil then local current_title = mw_title.getCurrentTitle() current_title_text, current_namespace = current_title.prefixedText, current_title.namespace end if not (prefix == "force" and namespace ~= current_namespace) then title = new_title(current_title_text .. val) end elseif prefix == "force" then -- Unconditionally add the namespace prefix (mw.title.makeTitle). title = make_title(namespace, decoded) elseif prefix == "full override" then -- The first input prefix will be used as an override (mw.title.new). This can be a namespace or interwiki prefix. title = new_title(val, namespace) elseif prefix == nil or prefix == "namespace override" then -- Only allow namespace prefixes to override. Interwiki prefixes therefore need to be treated as plaintext (e.g. "el:All topics" with namespace 14 returns "el:Category:All topics", but we want "Category:el:All topics" instead; if the former is really needed, then the input ":el:Category:All topics" will work, as the initial colon overrides the namespace). mw.title.new can take namespace names as well as numbers in the second argument, and will throw an error if the input isn't a valid namespace, so this can be used to determine if a prefix is for a namespace, since mw.title.new will return successfully only if there's either no prefix or the prefix is for a valid namespace (in which case we want the override). local success success, title = pcall(new_title, val, match(decoded, "^.-%f[:]") or namespace) -- Otherwise, get the title with mw.title.makeTitle, which unconditionally adds the namespace prefix, but behaves like mw.title.new if the namespace is 0. if not success then title = make_title(namespace, decoded) end else error(format('Internal error: expected `prefix` for type `title` to be "force", "full override", "namespace override" or undefined, but saw %s', dump(prefix))) end local allow_external = param.allow_external if allow_external == true then return title or convert_val_error(val, name, "Wiktionary or external page title") elseif not allow_external then return title and is_internal_title(title) and title or convert_val_error(val, name, "Wiktionary page title") end error(format('Internal error: expected `allow_external` for type `title` to be of type "boolean" or undefined, but saw %s', dump(allow_external))) end, ["Wikimedia language"] = function(val, name, param) local fallback = param.fallback if fallback == true then return get_wm_lang_by_code_with_fallback(val) or convert_val_error(val, name, "Wikimedia language or language code") elseif not fallback then return get_wm_lang_by_code(val) or convert_val_error(val, name, "Wikimedia language code") end error(format('Internal error: expected `fallback` for type `Wikimedia language` to be of type "boolean" or undefined, but saw %s', dump(fallback))) end, }, { -- TODO: decode HTML entities in all input values. Non-trivial to implement, because we need to avoid any downstream functions decoding the output from this module, which would be double-decoding. Note that "title" has this implemented already, and it needs to have both the raw input and the decoded input to avoid double-decoding by me.title.new, so any implementation can't be as simple as decoding in __call then passing the result to the handler. __call = function(self, val, name, param, param_type, default) local val_type = type(val) -- TODO: check this for all possible parameter types. if val_type == param_type then return val elseif val_type ~= "string" then local expected = "string" if default and (param_type == "boolean" or param_type == "number") then expected = param_type .. " or " .. expected end error(format( "Internal error: %sargument %s has the type %s; expected a %s.", default and (default .. " for ") or "", name, dump(val_type), expected )) end local func = self[param_type] if func == nil then error(format("Internal error: %s is not a recognized parameter type.", dump(param_type))) end return func(val, name, param) end }) --[==[ func: export.convert_val(val, name, param) Convert a parameter value according to the associated specs listed in the `params` table passed to [[Module:parameters]]. `val` is the value to convert for a parameter whose name is `name` (used only in error messages). `param` is the spec (the value part of the `params` table for the parameter). In place of passing in the parameter name, `name` can be a function that throws an error, displaying the specified message along with the parameter name and value. This function processes all the conversion-related fields in `param`, including `type`, `set`, `sublist`, `convert`, etc. It returns the converted value. ]==] local function convert_val(val, name, param, default) local param_type = param.type or "string" -- If param.type is a function, resolve it to a recognized type. if is_callable(param_type) then param_type = param_type(val) end local convert, sublist = param.convert, param.sublist -- `val` might not be a string if it's the default value. if sublist and type(val) == "string" then local retlist, set = {}, param.set if convert then local thisindex, thisval, insval, parse_err = 0 if is_callable(name) then -- We assume the passed-in error function in `name` already shows the parameter name and raw value. function parse_err(msg) name(format("%s: item #%s=%s", msg_with_processed(msg, thisval, insval), thisindex, thisval) ) end else function parse_err(msg) error(format("%s: item #%s=%s of parameter %s=%s", msg_with_processed(msg, thisval, insval), thisindex, thisval, name, val) ) end end for v in split_sublist(val, name, sublist) do thisindex, thisval = thisindex + 1, v if set then v = check_set(v, name, param, param_type) end insert(retlist, convert(type_handlers(v, name, param, param_type, default), parse_err)) end else for v in split_sublist(val, name, sublist) do if set then v = check_set(v, name, param, param_type) end insert(retlist, type_handlers(v, name, param, param_type, default)) end end return retlist elseif param.set then val = check_set(val, name, param, param_type) end local retval = type_handlers(val, name, param, param_type, default) if convert then local parse_err if is_callable(name) then -- We assume the passed-in error function in `name` already shows the parameter name and raw value. if retval == val then -- This is an optimization to avoid creating a closure. The second arm works correctly even -- when retval == val. parse_err = name else function parse_err(msg) name(msg_with_processed(msg, val, retval)) end end else function parse_err(msg) error(format("%s: parameter %s=%s", msg_with_processed(msg, val, retval), name, val)) end end retval = convert(retval, parse_err) end -- If `sublist` is set but the input wasn't a string, return `retval` as a one-item list. if sublist then retval = {retval} end return retval end export.convert_val = convert_val -- used by [[Module:parameter utilities]] local function unknown_param(name, val, args_unknown) track("unknown parameters") args_unknown[name] = val return args_unknown end local function check_string_param_modifier(param_type, name, tag) if param_type and not (param_type == "string" or param_type == "parameter" or is_callable(param_type)) then internal_process_error( "%s cannot be set unless %s is set to %s (the default), %s or a function: parameter %s has the type %s.", tag, "type", "string", "parameter", name, param_type ) end end local function hole_error(params, name, listname, this, nxt, extra) -- `process_error` calls `dump` on values to be inserted into -- error messages, but with numeric lists this causes "numeric" -- to look like the name of the list rather than a description, -- as `dump` adds quote marks. Insert it early to avoid this, -- but add another %s specifier in all other cases, so that -- actual list names will be displayed properly. local offset, specifier, starting_from = 0, "%s", "" local msg = "Item %%d in the list of %s parameters must be given if item %%d is given, because %sthere shouldn't be any gaps due to missing%s parameters." local specs = {} if type(listname) == "string" then specs[2] = listname elseif type(name) == "number" then offset = name - 1 -- To get the original parameter. specifier = "numeric" -- If the list doesn't start at parameter 1, avoid implying -- there can't be any gaps in the numeric parameters if -- some parameter with a lower key is optional. for j = name - 1, 1, -1 do local _param = params[j] if not (_param and _param.required) then starting_from = format("(starting from parameter %d) ", dump(j + 1)) break end end else specs[2] = name end specs[1] = this + offset -- Absolute index for this item. insert(specs, nxt + offset) -- Absolute index for the next item. process_error(format(msg, specifier, starting_from, extra or ""), unpack(specs)) end local function check_disallow_holes(params, val, name, listname, extra) for i = 1, val.maxindex do if val[i] == nil then hole_error(params, name, listname, i, num_keys(val)[i], extra) end end end local function handle_holes(params, val, name) local param = params[name] local disallow_holes = param.disallow_holes -- Iterate up the list, and throw an error if a hole is found. if disallow_holes then check_disallow_holes(params, val, name, param.list, " or empty") end -- Iterate up the list, and throw an error if a hole is found due to a -- missing parameter, treating empty parameters as part of the list. This -- applies beyond maxindex if blank arguments are supplied beyond it, so -- isn't mutually exclusive with `disallow_holes`. local empty = val.empty if param.disallow_missing then if empty then -- Remove `empty` from `val`, so it doesn't get returned. val.empty = nil for i = 1, max(val.maxindex, empty.maxindex) do if val[i] == nil and not empty[i] then local keys = extend(num_keys(val), num_keys(empty)) sort(keys) hole_error(params, name, param.list, i, keys[i]) end end -- If there's no table of empty parameters, the check is identical to -- `disallow_holes`, except that the error message only refers to -- missing parameters, not missing or empty ones. If `disallow_holes` is -- also set, there's no point checking again. elseif not disallow_holes then check_disallow_holes(params, val, name, param.list) end end -- If `allow_holes` is set, there's nothing left to do. if param.allow_holes then -- do nothing -- Otherwise, remove any holes: `pairs` won't work, as it's unsorted, and -- iterating from 1 to `maxindex` times out with inputs like |100000000000=, -- so use num_keys to get a list of numerical keys sorted from lowest to -- highest, then iterate up the list, moving each value in `val` to the -- lowest unused positive integer key. This also avoids the need to create a -- new table. If `disallow_holes` is specified, then there can't be any -- holes in the list, so there's no reason to check again; this doesn't -- apply to `disallow_missing`, however. else if not disallow_holes then local keys, i = num_keys(val), 0 while true do i = i + 1 local key = keys[i] if key == nil then break elseif i ~= key then track("holes compressed") val[i], val[key] = val[key], nil end end end -- Some code depends on only numeric params being present when no holes are -- allowed (e.g. by checking for the presence of arguments using next()), so -- remove `maxindex`. val.maxindex = nil end end local function maybe_flatten(params, val, name) local param = params[name] if param.flatten then if param.allow_holes then process_error("For parameter %s, can't set both `allow_holes` and `flatten`", name) end if not param.sublist and param.type ~= "genders" and param.type ~= "labels" and param.type ~= "references" and param.type ~= "qualifier" and param.type ~= "form of tags" then process_error("For parameter %s, can only set `flatten` along with `sublist` or a list-generating type", name) end -- Do the flattening ourselves rather than calling flatten() in [[Module:table]], which will attempt to -- flatten non-list objects like title objects, and cause an error in the process. -- FIXME: We should do this in-place if possible. local newlist = {} for _, sublist in ipairs(val) do for _, item in ipairs(sublist) do insert(newlist, item) end end val = newlist end return val end -- If both `template_default` and `default` are given, `template_default` takes precedence, but only on the template or -- module page. This means a different default can be specified for the template or module page example. However, -- `template_default` doesn't apply if any args are set, which helps (somewhat) with examples on documentation pages -- transcluded into the template page. HACK: We still run into problems on documentation pages transcluded into the -- template page when pagename= is set. Check this on the assumption that pagename= is fairly standard. local function convert_default_val(name, param, pagename_set, any_args_set, add_empty_sublist) if not pagename_set then local val = param.template_default if val ~= nil and not any_args_set and is_own_page() then return convert_val(val, name, param, "template default") end end local val = param.default if val ~= nil then return convert_val(val, name, param, "default") -- Sublist parameters should return an empty table if not given, but only do -- this if the parameter isn't also a list (in which case it will already -- be an empty table). -- FIXME: do this once all modules that pass in a sublist parameter treat an empty sublist identically to a nil argument; some currently do things based on the fact an argument exists at all. -- elseif add_empty_sublist and param.sublist then --return {} end end --[==[ Process arguments with a given list of parameters. Return a table containing the processed arguments. The `args` parameter specifies the arguments to be processed; they are the arguments you might retrieve from {frame:getParent().args} (the template arguments) or in some cases {frame.args} (the invocation arguments). The `params` parameter specifies a list of valid parameters, and consists of a table. If an argument is encountered that is not in the parameter table, an error is thrown. The structure of the `params` table is as described above in the intro comment. '''WARNING:''' The `params` table is destructively modified to save memory. Nonetheless, different keys can share the same value objects in memory without causing problems. The `return_unknown` parameter, if set to {true}, prevents the function from triggering an error when it comes across an argument with a name that it doesn't recognise. Instead, the return value is a pair of values: the first is the processed arguments as usual, while the second contains all the unrecognised arguments that were left unprocessed. This allows you to do multi-stage processing, where the entire set of arguments that a template should accept is not known at once. For example, an inflection-table might do some generic processing on some arguments, but then defer processing of the remainder to the function that handles a specific inflectional type. ]==] function export.process(args, params, return_unknown) -- Process parameters for specific properties local args_new, args_unknown, any_args_set, required, patterns, list_args, index_list, args_placeholders, placeholders_n = {} -- TODO: memoize the processing of each unique `param` value, since it's common for the same value to be used for many parameter names. for name, param in pairs(params) do validate_name(name, "parameter names") if param ~= true then local spec_type = type(param) if type(param) ~= "table" then internal_process_error( "spec for parameter %s must be a table of specs or the value true, but found %s.", name, spec_type ~= "boolean" and spec_type or param ) end -- Populate required table, and make sure aliases aren't set to required. if param.required then if required == nil then required = {} end required[name] = true end local listname, alias_of = param.list, param.alias_of if alias_of then validate_name(alias_of, "the alias_of field of parameter ", name) if alias_of == name then internal_process_error( "parameter %s cannot be an alias of itself.", name ) end local main_param = params[alias_of] -- Check that the alias_of is set to a valid parameter. if not (main_param == true or type(main_param) == "table") then internal_process_error( "parameter %s is an alias of an invalid parameter.", name ) end validate_alias_options(param, name, main_param, alias_of) -- Aliases can't be lists unless the canonical parameter is also a list. if listname and (main_param == true or not main_param.list) then internal_process_error( "list parameter %s is set as an alias of %s, which is not a list parameter.", name, alias_of ) -- Can't be an alias of an alias. elseif main_param ~= true then local main_alias_of = main_param.alias_of if main_alias_of ~= nil then internal_process_error( "alias_of cannot be set to another alias: parameter %s is set as an alias of %s, which is in turn an alias of %s. Set alias_of for %s to %s.", name, alias_of, main_alias_of, name, main_alias_of ) end end end local replaced_by = param.replaced_by if replaced_by then -- replaced_by can be `false`, which is OK validate_name(replaced_by, "the replaced_by field of parameter ", name) if replaced_by == name then internal_process_error( "parameter %s cannot be replaced by itself.", name ) end local main_param = params[replaced_by] -- Check that the replaced_by is set to a valid parameter. if not (main_param == true or type(main_param) == "table") then internal_process_error( "parameter %s is set to be replaced by an invalid parameter.", name ) end -- Can't be a replaced-by of a replaced-by. if main_param ~= true then local main_replaced_by = main_param.replaced_by if main_replaced_by ~= nil then internal_process_error( "replaced_by cannot be set to another replaced-by parameter: parameter %s is set as replaced by %s, which is in turn replaced by %s. Set replaced_by for %s to %s.", name, replaced_by, main_replaced_by, name, main_replaced_by ) end end if param.instead ~= nil then internal_process_error("the `instead` tag can only be given when `replaced_by` is set to `false`.") end elseif replaced_by == false then if param.instead ~= nil and type(param.instead) ~= "string" then internal_process_error( "the `instead` tag must be a string, but saw %s.", param.instead ) end end if replaced_by ~= nil then if param.reason ~= nil and type(param.reason) ~= "string" then internal_process_error( "the `reason` tag must be a string, but saw %s.", param.reason ) end end if listname then if not alias_of then local key = name if type(name) == "string" then key = gsub(name, "\1", "") end local list_arg = {maxindex = 0} args_new[key] = list_arg if list_args == nil then list_args = {} end list_args[key] = list_arg end local list_type = type(listname) if list_type == "string" then -- If the list property is a string, then it represents the name -- to be used as the prefix for list items. This is for use with lists -- where the first item is a numbered parameter and the -- subsequent ones are named, such as 1, pl2, pl3. patterns = save_pattern(name, listname, patterns or {}) elseif listname ~= true then internal_process_error( "list field for parameter %s must be a boolean, string or undefined, but saw a %s.", name, list_type ) elseif type(name) == "number" then if index_list ~= nil then internal_process_error( "only one numeric parameter can be a list, unless the list property is a string." ) end -- If the name is a number, then all indexed parameters from -- this number onwards go in the list. index_list = name else patterns = save_pattern(name, name, patterns or {}) end if find(name, "\1", nil, true) then if args_placeholders then placeholders_n = placeholders_n + 1 args_placeholders[placeholders_n] = name else args_placeholders, placeholders_n = {name}, 1 end end end end end --Process required changes to `params`. if args_placeholders then for i = 1, placeholders_n do local name = args_placeholders[i] params[gsub(name, "\1", "")], params[name] = params[name], nil end end -- Process the arguments for name, val in pairs(args) do any_args_set = true validate_name(name, "argument names", nil, true) -- Guaranteeing that all values are strings avoids issues with type coercion being inconsistent between functions. local val_type = type(val) if val_type ~= "string" then internal_process_error( "argument %s has the type %s; all arguments must be strings.", name, val_type ) end local orig_name, raw_type, index, canonical = name, type(name) if raw_type == "number" then if index_list and name >= index_list then index = name - index_list + 1 name = index_list end elseif patterns then -- Does this argument name match a pattern? for pattern, pname in next, patterns do index = match(name, pattern) -- It matches, so store the parameter name and the -- numeric index extracted from the argument name. if index then index = tonumber(index) name = pname break end end end local param = params[name] -- If the argument is not in the list of parameters, store it in a separate list. if not param then args_unknown = unknown_param(name, val, args_unknown or {}) elseif param == true then canonical = orig_name val = php_trim(val) if val ~= "" then -- If the parameter is duplicated, throw an error. if args_new[name] ~= nil then process_error( "Parameter %s has been entered more than once. This is probably because a parameter alias has been used.", canonical ) end args_new[name] = val end else if param.replaced_by == false then process_error( ("Parameter %%s has been removed and is no longer valid%s.%s"):format( param.reason and ", " .. param.reason or "", param.instead and " Instead, " .. param.instead .. "." or ""), name ) elseif param.replaced_by then process_error( ("Parameter %%s has been replaced by %%s%s."):format( param.reason and ", " .. param.reason or ""), name, param.replaced_by ) end if param.deprecated then track("deprecated parameter", name) end if param.require_index then -- Disallow require_index for numeric parameter names, as this doesn't make sense. if raw_type == "number" then internal_process_error( "cannot set require_index for numeric parameter %s.", name ) -- If a parameter without the trailing index was found, and -- require_index is set on the param, treat it -- as if it isn't recognized. elseif not index then args_unknown = unknown_param(name, val, args_unknown or {}) end end -- Check that separate_no_index is not being used with a numeric parameter. if param.separate_no_index then if raw_type == "number" then internal_process_error( "cannot set separate_no_index for numeric parameter %s.", name ) elseif type(param.alias_of) == "number" then internal_process_error( "cannot set separate_no_index for parameter %s, as it is an alias of numeric parameter %s.", name, param.alias_of ) end end -- If no index was found, use 1 as the default index. -- This makes list parameters like g, g2, g3 put g at index 1. -- If `separate_no_index` is set, then use 0 as the default instead. if not index and param.list then index = param.separate_no_index and 0 or 1 end -- Normalize to the canonical parameter name. If it's a list, but the alias is not, then determine the index. local raw_name = param.alias_of if raw_name then raw_type = type(raw_name) if raw_type == "number" then name = raw_name local main_param = params[raw_name] if main_param ~= true and main_param.list then if not index then index = param.separate_no_index and 0 or 1 end canonical = raw_name + index - 1 else canonical = raw_name end else name = gsub(raw_name, "\1", "") local main_param = params[name] if not index and main_param ~= true and main_param.list then index = param.separate_no_index and 0 or 1 end if not index or index == 0 then canonical = name elseif name == raw_name then canonical = name .. index else canonical = gsub(raw_name, "\1", index) end end else canonical = orig_name end -- Only recognize demo parameters if this is the current template or module's -- page, or its documentation page. if param.demo and not is_own_page("include_documentation") then args_unknown = unknown_param(name, val, args_unknown or {}) end -- Remove leading and trailing whitespace unless no_trim is true. if param.no_trim then check_string_param_modifier(param.type, name, "no_trim") else val = php_trim(val) end -- Empty string is equivalent to nil unless allow_empty is true. if param.allow_empty then check_string_param_modifier(param.type, name, "allow_empty") elseif val == "" then -- If `disallow_missing` is set, keep track of empty parameters -- via the `empty` field in `arg`, which will be used by the -- `disallow_missing` check. This will be deleted before -- returning. if index and param.disallow_missing then local arg = args_new[name] local empty = arg.empty if empty == nil then empty = {maxindex = 0} arg.empty = empty end empty[index] = true if index > empty.maxindex then empty.maxindex = index end end val = nil end -- Allow boolean false. if val ~= nil then -- Convert to proper type if necessary. local main_param = params[raw_name] if main_param ~= true then val = convert_val(val, orig_name, main_param or param) end -- Mark it as no longer required, as it is present. if required then required[name] = nil end -- Store the argument value. if index then local arg = args_new[name] -- If the parameter is duplicated, throw an error. if arg[index] ~= nil then process_error( "Parameter %s has been entered more than once. This is probably because a list parameter has been entered without an index and with index 1 at the same time, or because a parameter alias has been used.", canonical ) end arg[index] = val -- Store the highest index we find. local maxindex = arg.maxindex if index > maxindex then maxindex = index end if arg[0] ~= nil then arg.default, arg[0] = arg[0], nil if maxindex < 1 then maxindex = 1 end end arg.maxindex = maxindex if not params[name].list then args_new[name] = val -- Don't store index 0, as it's a proxy for the default. elseif index > 0 then arg[index] = val end else -- If the parameter is duplicated, throw an error. if args_new[name] ~= nil then process_error( "Parameter %s has been entered more than once. This is probably because a parameter alias has been used.", canonical ) end if not raw_name then args_new[name] = val else local main_param = params[raw_name] if main_param ~= true and main_param.list then local main_arg = args_new[raw_name] main_arg[1] = val -- Store the highest index we find. if main_arg.maxindex < 1 then main_arg.maxindex = 1 end else args_new[raw_name] = val end end end end end end -- Remove holes in any list parameters if needed. This must be handled -- straight after the previous loop, as any instances of `empty` need to be -- converted to nil. if list_args then for name, val in next, list_args do handle_holes(params, val, name) end end -- If the current page is the template which invoked this Lua instance, then ignore the `require` flag, as it -- means we're viewing the template directly. Required parameters sometimes have a `template_default` key set, -- which gets used in such cases as a demo. -- Note: this won't work on other pages in the Template: namespace (including the /documentation subpage), -- or if the #invoke: is on a page in another namespace. local pagename_set = args_new.pagename -- Handle defaults. for name, param in pairs(params) do if param ~= true then local arg_new = args_new[name] if arg_new == nil then args_new[name] = convert_default_val(name, param, pagename_set, any_args_set, true) elseif param.list and arg_new[1] == nil then local default_val = convert_default_val(name, param, pagename_set, any_args_set) if default_val ~= nil then arg_new[1] = default_val if arg_new.maxindex == 0 then arg_new.maxindex = 1 end end end end end -- Flatten nested lists if called for. This must come after setting the default. if list_args then for name, val in next, list_args do args_new[name] = maybe_flatten(params, val, name) end end -- The required table should now be empty. -- If any parameters remain, throw an error, unless we're on the current template or module's page. if required and next(required) ~= nil and not is_own_page() then params_list_error(required, "required") -- Return the arguments table. -- If there are any unknown parameters, throw an error, unless return_unknown is set, in which case return args_unknown as a second return value. elseif return_unknown then return args_new, args_unknown or {} elseif args_unknown and next(args_unknown) ~= nil then params_list_error(args_unknown, "not used by this template") end return args_new end return export c23kzesggrlowmn06oii7zu1bbzht4t 36505 36498 2026-08-09T17:22:33Z Ghybu 12 پووچەڵکردنەوەی پێداچوونەوەی [[Special:Diff/36498|36498]] لە لایەن [[Special:Contributions/Ghybu|Ghybu]] ([[User talk:Ghybu|لێدوان]]) 36505 Scribunto text/plain --[==[TODO: * Change certain flag names, as some are misnomers: * Change `allow_holes` to `keep_holes`, because it's not the inverse of `disallow_holes`. * Change `allow_empty` to `keep_empty`, as it causes them to be kept as "" instead of deleted. * Sort out all the internal error calls. Manual error(format()) calls are used when certain parameters shouldn't be dumped, so find a way to avoid that. ]==] local export = {} local collation_module = "Module:collation" local families_module = "Module:families" local functions_module = "Module:fun" local gender_and_number_utilities_module = "Module:gender and number utilities" local labels_module = "Module:labels" local languages_module = "Module:languages" local math_module = "Module:math" local pages_module = "Module:pages" local parameters_finalize_set_module = "Module:parameters/finalizeSet" local parameters_track_module = "Module:parameters/track" local parse_utilities_module = "Module:parse utilities" local references_module = "Module:references" local scribunto_module = "Module:Scribunto" local scripts_module = "Module:scripts" local string_utilities_module = "Module:string utilities" local table_module = "Module:table" local wikimedia_languages_module = "Module:wikimedia languages" local yesno_module = "Module:yesno" local mw = mw local mw_title = mw.title local string = string local table = table local dump = mw.dumpObject local find = string.find local format = string.format local gsub = string.gsub local insert = table.insert local ipairs = ipairs local list_to_text = mw.text.listToText local make_title = mw_title.makeTitle local match = string.match local max = math.max local new_title = mw_title.new local next = next local pairs = pairs local pcall = pcall local require = require local sub = string.sub local tonumber = tonumber local type = type local unpack = unpack or table.unpack -- Lua 5.2 compatibility local current_title_text, current_namespace, sets -- Defined when needed. local namespaces = mw.site.namespaces --[==[ 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 decode_entities(...) decode_entities = require(string_utilities_module).decode_entities return decode_entities(...) end local function extend(...) extend = require(table_module).extend return extend(...) end local function finalize_set(...) finalize_set = require(parameters_finalize_set_module) return finalize_set(...) end local function get_family_by_code(...) get_family_by_code = require(families_module).getByCode return get_family_by_code(...) end local function get_family_by_name(...) get_family_by_name = require(families_module).getByCanonicalName return get_family_by_name(...) end local function get_language_by_code(...) get_language_by_code = require(languages_module).getByCode return get_language_by_code(...) end local function get_language_by_name(...) get_language_by_name = require(languages_module).getByCanonicalName return get_language_by_name(...) end local function get_script_by_code(...) get_script_by_code = require(scripts_module).getByCode return get_script_by_code(...) end local function get_script_by_name(...) get_script_by_name = require(scripts_module).getByCanonicalName return get_script_by_name(...) end local function get_wm_lang_by_code(...) get_wm_lang_by_code = require(wikimedia_languages_module).getByCode return get_wm_lang_by_code(...) end local function get_wm_lang_by_code_with_fallback(...) get_wm_lang_by_code_with_fallback = require(wikimedia_languages_module).getByCodeWithFallback return get_wm_lang_by_code_with_fallback(...) end local function gsplit(...) gsplit = require(string_utilities_module).gsplit return gsplit(...) end local function is_callable(...) is_callable = require(functions_module).is_callable return is_callable(...) end local function is_integer(...) is_integer = require(math_module).is_integer return is_integer(...) end local function is_internal_title(...) is_internal_title = require(pages_module).is_internal_title return is_internal_title(...) end local function is_positive_integer(...) is_positive_integer = require(math_module).is_positive_integer return is_positive_integer(...) end local function iterate_list(...) iterate_list = require(table_module).iterateList return iterate_list(...) end local function num_keys(...) num_keys = require(table_module).numKeys return num_keys(...) end local function parse_gender_and_number_spec(...) parse_gender_and_number_spec = require(gender_and_number_utilities_module).parse_gender_and_number_spec return parse_gender_and_number_spec(...) end local function parse_references(...) parse_references = require(references_module).parse_references return parse_references(...) end local function pattern_escape(...) pattern_escape = require(string_utilities_module).pattern_escape return pattern_escape(...) end local function php_trim(...) php_trim = require(scribunto_module).php_trim return php_trim(...) end local function scribunto_parameter_key(...) scribunto_parameter_key = require(scribunto_module).scribunto_parameter_key return scribunto_parameter_key(...) end local function sort(...) sort = require(collation_module).sort return sort(...) end local function sorted_pairs(...) sorted_pairs = require(table_module).sortedPairs return sorted_pairs(...) end local function split_labels_on_comma(...) split_labels_on_comma = require(labels_module).split_labels_on_comma return split_labels_on_comma(...) end local function split_on_comma(...) split_on_comma = require(parse_utilities_module).split_on_comma return split_on_comma(...) end local function tonumber_extended(...) tonumber_extended = require(math_module).tonumber_extended return tonumber_extended(...) end local function track(...) track = require(parameters_track_module) return track(...) end local function yesno(...) yesno = require(yesno_module) return yesno(...) end --[==[ intro: This module is used to standardize template argument processing and checking. A typical workflow is as follows (based on [[Module:translations]]): { ... local parent_args = frame:getParent().args local params = { [1] = {required = true, type = "language", default = "und"}, [2] = true, [3] = {list = true}, ["alt"] = true, ["id"] = true, ["sc"] = {type = "script"}, ["tr"] = true, ["ts"] = true, ["lit"] = true, } local args = require("Module:parameters").process(parent_args, params) -- Do further processing of the parsed arguments in `args`. ... } The `params` table should have the parameter names as the keys, and a (possibly empty) table of parameter tags as the value. An empty table as the value merely states that the parameter exists, but should not receive any special treatment; if desired, empty tables can be replaced with the value `true` as a perforamnce optimization. Possible parameter tags are listed below: ; {required = true} : The parameter is required; an error is shown if it is not present. The template's page itself is an exception; no error is shown there. ; {default =} : Specifies a default input value for the parameter, if it is absent or empty. This will be processed as though it were the input instead, so (for example) {default = "und"} with the type {"language"} will return a language object for [[:Category:Undetermined language|Undetermined language]] if no language code is provided. When used on list parameters, this specifies a default value for the first item in the list only. Note that it is not possible to generate a default that depends on the value of other parameters. If used together with {required = true}, the default applies only to template pages (see the following entry), as a side effect of the fact that "required" parameters aren't actually required on template pages. This can be used to show an example of the template in action when the template page is visited; however, it is preferred to use `template_default` for this purpose, for clarity. ; {template_default =} : Specifies a default input value for absent or empty parameters only on template pages. Template pages are any page in the template space (beginning with `Template:`) except for documentation pages (those ending in `.../documentation`). This can be used to provide an example value for a non-required parameter when the template page is visited, without interfering with other uses of the template. Both `template_default` and `default` can be specified for the same parameter. If this is done, `template_default` applies on template pages, and `default` on other pages. As an example, {{tl|cs-IPA}} uses the equivalent of {[1] = {default = "+", template_default = "příklad"}} to supply a default of {"+"} for mainspace and documentation pages (which tells the module to use the value of the {{para|pagename}} parameter, falling back to the actual pagename), but {"příklad"} (which means "example"), on [[Template:cs-IPA]]. ; {alias_of =} : Treat the parameter as an alias of another. When arguments are specified for this parameter, they will automatically be renamed and stored under the alias name. This allows for parameters with multiple alternative names, while still treating them as if they had only one name. The conversion-related properties of an aliased parameter (e.g. `type`, `set`, `convert`, `sublist`) are taken from the aliasee, and the corrresponding properties set on the alias itself are ignored; but other properties on the alias are taken from the alias's spec and not from the aliasee's spec. This means, for example, that if you create an alias of a list parameter, the alias must also specify the `list` property or it is not a list. (In such a case, a value specified for the alias goes into the first item of the aliasee's list. You cannot make a list alias of a non-list parameter; this causes an error to be thrown.) Similarly, if you specify `separate_no_index` on an aliasee but not on the alias, uses of the unindexed aliasee parameter are stored into the `.default` key, but uses of the unindexed alias are stored into the first numbered key of the aliasee's list. Aliases cannot be required, as this prevents the other name or names of the parameter from being used. Parameters that are aliases and required at the same time cause an error to be thrown. ; {allow_empty = true} : If the argument is an empty string value, it is not converted to {nil}, but kept as-is. The use of `allow_empty` is disallowed if a type has been specified, and causes an error to be thrown. ; {no_trim = true} : Spacing characters such as spaces and newlines at the beginning and end of a positional parameter are not removed. (MediaWiki itself automatically trims spaces and newlines at the edge of named parameters.) The use of `no_trim` is disallowed if a type has been specified, and causes an error to be thrown. ; {type =} : Specifies what value type to convert the argument into. The default is to leave it as a text string. Alternatives are: :; {type = "boolean"} :: The value is treated as a boolean value, either true or false. No value, the empty string, and the strings {"0"}, {"no"}, {"n"}, {"false"}, {"f"} and {"off"} are treated as {false}, all other values are considered {true}. :; {type = "number"} :: The value is converted into a number, and throws an error if the value is not parsable as a number. Input values may be signed (`+` or `-`), and may contain decimal points and leading zeroes. If {allow_hex = true}, then hexadecimal values in the form {"0x100"} may optionally be used instead, which otherwise have the same syntax restrictions (including signs, decimal digits, and leading zeroes after {"0x"}). Hexadecimal inputs are not case-sensitive. Lua's special number values (`inf` and `nan`) are not possible inputs. :; {type = "range"} :: The value is interpreted as a hyphen-separated range of two numbers (e.g. {"2-4"} is interpreted as the range from {2} to {4}). A number input without a hyphen is interpreted as a range from that number to itself (e.g. the input {"1"} is interpreted as the range from {1} to {1}). Any optional flags which are available for numbers will also work for ranges. :; {type = "language"} :: The value is interpreted as a full or [[Wiktionary:Languages#Etymology-only languages|etymology-only language]] code language code (or name, if {method = "name"}) and converted into the corresponding object (see [[Module:languages]]). If the code or name is invalid, then an error is thrown. The additional setting {family = true} can be given to allow [[Wiktionary:Language families|language family codes]] to be considered valid and the corresponding object returned. Note that to distinguish an etymology-only language object from a full language object, use {object:hasType("language", "etymology-only")}. :; {type = "full language"} :: The value is interpreted as a full language code (or name, if {method = "name"}) and converted into the corresponding object (see [[Module:languages]]). If the code or name is invalid, then an error is thrown. Etymology-only languages are not allowed. The additional setting {family = true} can be given to allow [[Wiktionary:Language families|language family codes]] to be considered valid and the corresponding object returned. :; {type = "Wikimedia language"} :: The value is interpreted as a code and converted into a Wikimedia language object. If the code is invalid, then an error is thrown. If {fallback = true} is specified, conventional language codes which are different from their Wikimedia equivalent will also be accepted as a fallback. :; {type = "family"} :: The value is interpreted as a language family code (or name, if {method = "name"}) and converted into the corresponding object (see [[Module:families]]). If the code or name is invalid, then an error is thrown. :; {type = "script"} :: The value is interpreted as a script code (or name, if {method = "name"}) and converted into the corresponding object (see [[Module:scripts]]). If the code or name is invalid, then an error is thrown. :; {type = "title"} :: The value is interpreted as a page title and converted into the corresponding object (see the [[mw:Extension:Scribunto/Lua_reference_manual#Title_library|Title library]]). If the page title is invalid, then an error is thrown; by default, external titles (i.e. those on other wikis) are not treated as valid. Options are: ::; {namespace = n} ::: The default namespace, where {n} is a namespace number; this is treated as {0} (the mainspace) if not specified. ::; {allow_external = true} ::: External titles are treated as valid. ::; {prefix = "namespace override"} (default) ::: The default namespace prefix will be prefixed to the value is already prefixed by a namespace prefix. For instance, the input {"Foo"} with namespace {10} returns {"Template:Foo"}, {"Wiktionary:Foo"} returns {"Wiktionary:Foo"}, and {"Template:Foo"} returns {"Template:Foo"}. Interwiki prefixes cannot act as overrides, however: the input {"fr:Foo"} returns {"Template:fr:Foo"}. ::; {prefix = "force"} ::: The default namespace prefix will be prefixed unconditionally, even if the value already appears to be prefixed. This is the way that {{tl|#invoke:}} works when calling modules from the module namespace ({828}): the input {"Foo"} returns {"Module:Foo"}, {"Wiktionary:Foo"} returns {"Module:Wiktionary:Foo"}, and {"Module:Foo"} returns {"Module:Module:Foo"}. ::; {prefix = "full override"} ::: The same as {prefix = "namespace override"}, except that interwiki prefixes can also act as overrides. For instance, {"el:All topics"} with namespace {14} returns {"el:Category:All topics"}. Due to the limitations of MediaWiki, only the first prefix in the value may act as an override, so the namespace cannot be overridden if the first prefix is an interwiki prefix: e.g. {"el:Template:All topics"} with namespace {14} returns {"el:Category:Template:All topics"}. :; {type = "parameter"} :: The value is interpreted as the name of a parameter, and will be normalized using the method that Scribunto uses when constructing a {frame.args} table of arguments. This means that integers will be converted to numbers, but all other arguments will remain as strings (e.g. {"1"} will be normalized to {1}, but {"foo"} and {"1.5"} will remain unchanged). Note that Scribunto also trims parmeter names, following the same trimming method that this module applies by default to all parameter types. :: This type is useful when one set of input arguments is used to construct a {params} table for use in a subsequent {export.process()} call with another set of input arguments; for instance, the set of valid parameters for a template might be defined as {{tl|#invoke:[some module]|args=}} in the template, where {args} is a sublist of valid parameters for the template. :; {type = "qualifier"} :: The value is interpreted as a qualifier and converted into the correct format for passing into `format_qualifiers()` in [[Module:qualifier]] (which currently just means converting it to a one-item list). :; {type = "labels"} :: The value is interpreted as a comma-separated list of labels and converted into the correct format for passing into `show_labels()` in [[Module:labels]] (which is currently a list of strings). Splitting is done on commas not followed by whitespace, except that commas inside of double angle brackets do not count even if not followed by whitespace. This type should be used by for normal labels (typically specified using {{para|l}} or {{para|ll}}) and accent qualifiers (typically specified using {{para|a}} and {{para|aa}}). :; {type = "references"} :: The value is interpreted as one or more references, in the format prescribed by `parse_references()` in [[Module:references]], and converted into a list of objects of the form accepted by `format_references()` in the same module. If a syntax error is found in the reference format, an error is thrown. :; {type = "genders"} :: The value is interpreted as one or more comma-separated gender/number specs, in the format prescribed by [[Module:gender and number]]. Inline modifiers (`<q:...>`, `<qq:...>`, `<l:...>`, `<ll:...>` or `<ref:...>`) may be attached to a gender/number spec. :; {type = function(val) ... end} :: `type` may be set to a function (or callable table), which must take the argument value as its sole argument, and must output one of the other recognized types. This is particularly useful for lists (see below), where certain values need to be interpreted differently to others. ; {list =} : Treat the parameter as a list of values, each having its own parameter name, rather than a single value. The parameters will have a number at the end, except optionally for the first (but see also {require_index = true}). For example, {list = true} on a parameter named "head" will include the parameters {{para|head}} (or {{para|head1}}), {{para|head2}}, {{para|head3}} and so on. If the parameter name is a number, another number doesn't get appended, but the counting simply continues, e.g. for parameter {3} the sequence is {{para|3}}, {{para|4}}, {{para|5}} etc. List parameters are returned as numbered lists, so for a template that is given the parameters `|head=a|head2=b|head3=c`, the processed value of the parameter {"head"} will be { { "a", "b", "c" }}}. : The value for {list =} can also be a string. This tells the module that parameters other than the first should have a different name, which is useful when the first parameter in a list is a number, but the remainder is named. An example would be for genders: {list = "g"} on a parameter named {1} would have parameters {{para|1}}, {{para|g2}}, {{para|g3}} etc. : If the number is not located at the end, it can be specified by putting {"\1"} at the number position. For example, parameters {{para|f1accel}}, {{para|f2accel}}, ... can be captured by using the parameter name {"f\1accel"}, as is done in [[Module:headword/templates]]. ; {set =} : Require that the value of the parameter be one of the specified list of values (or omitted, if {required = true} isn't given). The values in the specified list should be strings corresponding to the raw parameter values except when {type = "number"}, in which case they should be numbers. A individual value in the list can also be an ''alias list'', which is a list where the first value is the "canonical" value and the remainder are aliases. When one of the aliases is used, the resulting parameter field in the returned arguments structure will have the canonical value. The use of `set` is disallowed if {type = "boolean"} and causes an error to be thrown. ; {sublist =} : The value of the parameter is a delimiter-separated list of individual raw values. The resulting field in `args` will be a Lua list (i.e. a table with numeric indices) of the converted values. If {sublist = true} is given, the values will be split on commas (possibly with whitespace on one or both sides of the comma, which is ignored). If {sublist = "comma without whitespace"} is given, the values will be split on commas which are not followed by whitespace, and which aren't preceded by an escaping backslash. Otherwise, the value of `sublist` should be either a Lua pattern specifying the delimiter(s) to split on or a function (or callable table) to do the splitting, which is passed two values (the value to split and a function to signal an error) and should return a list of the split values. ; {convert =} : If given, this specifies a function (or callable table) to convert the raw parameter value into the Lua object used during further processing. The function is passed two arguments, the raw parameter value itself and a function used to signal an error during parsing or conversion, and should return one value, the converted parameter. The error-signaling function contains the name and raw value of the parameter embedded into the message it generates, so these do not need to specified in the message passed into it. If `type` is specified in conjunction with `convert`, the processing by `type` happens first. If `sublist` is given in conjunction with `convert`, the raw parameter value will be split appropriately and `convert` called on each resulting item. ; {allow_hex = true} : When used in conjunction with {type = "number"}, allows hexadecimal numbers as inputs, in the format {"0x100"} (which is not case-sensitive). ; {family = true} : When used in conjunction with {type = "language"}, allows [[Wiktionary:Language families|language family codes]] to be returned. To check if a given object refers to a language family, use {object:hasType("family")}. ; {method = "name"} : When used in conjunction with {type = "language"}, {type = "family"} or {type = "script"}, checks for and parses a language, family or script name instead of a code. ; {allow_holes = true} : This is used in conjunction with list-type parameters. By default, the values are tightly packed in the resulting list. This means that if, for example, an entry specified `head=a|head3=c` but not {{para|head2}}, the returned list will be { {"a", "c"}}}, with the values stored at the indices {1} and {2}, not {1} and {3}. If it is desirable to keep the numbering intact, for example if the numbers of several list parameters correlate with each other (like those of {{tl|affix}}), then this tag should be specified. : If {allow_holes = true} is given, there may be {nil} values in between two real values, which makes many of Lua's table processing functions no longer work, like {#} or {ipairs()}. To remedy this, the resulting table will contain an additional named value, `maxindex`, which tells you the highest numeric index that is present in the table. In the example above, the resulting table will now be { { "a", nil, "c", maxindex = 3}}}. That way, you can iterate over the values from {1} to `maxindex`, while skipping {nil} values in between. ; {disallow_holes = true} : This is used in conjunction with list-type parameters. As mentioned above, normally if there is a hole in the source arguments, e.g. `head=a|head3=c` but not {{para|head2}}, it will be removed in the returned list. If {disallow_holes = true} is specified, however, an error is thrown in such a case. This should be used whenever there are multiple list-type parameters that need to line up (e.g. both {{para|head}} and {{para|tr}} are available and {{para|head3}} lines up with {{para|tr3}}), unless {allow_holes = true} is given and you are prepared to handle the holes in the returned lists. ; {disallow_missing = true} : This is similar to {disallow_holes = true}, but an error will not be thrown if an argument is blank, rather than completely missing. This may be used to tolerate intermediate blank numerical parameters, which sometimes occur in list templates. For instance, `head=a|head2=|head3=c` will not throw an error, but `head=a|head3=c` will. ; {require_index = true} : This is used in conjunction with list-type parameters. By default, the first parameter can have its index omitted. For example, a list parameter named `head` can have its first parameter specified as either {{para|head}} or {{para|head1}}. If {require_index = true} is specified, however, only {{para|head1}} is recognized, and {{para|head}} will be treated as an unknown parameter. {{tl|affixusex}} (and variants {{tl|suffixusex}}, {{tl|prefixusex}}) use this, for example, on all list parameters. ; {separate_no_index = true} : This is used to distinguish between {{para|head}} and {{para|head1}} as different parameters. For example, in {{tl|affixusex}}, to distinguish between {{para|sc}} (a script code for all elements in the usex's language) and {{para|sc1}} (the script code of the first element, used when the first element is prefixed with a language code to indicate that it is in a different language). When this is used, the resulting table will contain an additional named value, `default`, which contains the value for the indexless argument. ; {flatten = true} : This is used in conjunction with list-type parameters when `sublist` or a list-generating type such as {"labels"} or {"genders"} is also specified, and causes the resulting list to be flattened. Not currently compatible with {allow_holes = true}. ; {replaced_by =} : Specifies that the parameter is no longer valid, and has been replaced by some other mechanism. If the value of `replaced_by` is a string, it is the name of the new parameter to use instead. Use the `reason` tag to specify the reason why this change has been made, e.g. {reason = "for consistency with the corresponding parameter in other Romance-language headword templates"}. If the value of `replaced_by` is {false}, there is no replacement parameter. In this case, `instead` should be supplied with a description of what to do instead, e.g. {instead = "use an inline modifier on |2= such as <q:...>, <qq:...>, <l:...> or <ll:...>"}. You can also supply a justification in `reason` if you feel it is appropriate or necessary to do so. ; {reason =} : When used in conjunction with `replaced_by`, specifies the reason for the parameter replacement. ; {instead =} : When used in conjunction with {replaced_by = false}, specifies what to do instead of using the removed parameter. ; {demo = true} : This is used as a way to ensure that the parameter is only enabled on the template's own page (and its documentation page), and in the User: namespace; otherwise, it will be treated as an unknown parameter. This should only be used if special settings are required to showcase a template in its documentation (e.g. adjusting the pagename or disabling categorization). In most cases, it should be possible to do this without using demo parameters, but they may be required if a template/documentation page also contains real uses of the same template as well (e.g. {{tl|shortcut}}), as a way to distinguish them. ; {deprecated = true} : This is for tracking the use of deprecated parameters, including any aliases that are being brought out of use. See [[Wiktionary:Tracking]] for more information. ]==] -- Returns true if the current page is a template or module containing the current {{#invoke}}. -- If the include_documentation argument is given, also returns true if the current page is either page's docuemntation page. local own_page, own_page_or_documentation local function is_own_page(include_documentation) if own_page == nil then if current_namespace == nil then local current_title = mw_title.getCurrentTitle() current_title_text, current_namespace = current_title.prefixedText, current_title.namespace end local frame = current_namespace == 828 and mw.getCurrentFrame() or current_namespace == 10 and mw.getCurrentFrame():getParent() if frame then local frame_title_text = frame:getTitle() own_page = current_title_text == frame_title_text own_page_or_documentation = own_page or current_title_text == frame_title_text .. "/documentation" else own_page, own_page_or_documentation = false, false end end return include_documentation and own_page_or_documentation or own_page end -------------------------------------- Some helper functions ----------------------------- -- Convert a list in `list` to a string, separating the final element from the preceding one(s) by `conjunction`. If -- `dump_vals` is given, pass all values in `list` through mw.dumpObject() (WARNING: this destructively modifies -- `list`). This is similar to serialCommaJoin() in [[Module:table]] when used with the `dontTag = true` option, but -- internally uses mw.text.listToText(). local function concat_list(list, conjunction, dump_vals) if dump_vals then for k, v in pairs(list) do list[k] = dump(v) end end return list_to_text(list, nil, conjunction) end -- A helper function for use with generating error-signaling functions in the presence of raw value conversion. Format a -- message `msg`, including the processed value `processed` if it is different from the raw value `rawval`; otherwise, -- just return `msg`. local function msg_with_processed(msg, rawval, processed) if rawval == processed then return msg end local processed_type = type(processed) return format("%s (processed value %s)", msg, (processed_type == "string" or processed_type == "number") and processed or dump(processed) ) end -------------------------------------- Error handling ----------------------------- local function process_error(fmt, ...) local args = {...} for i, val in ipairs(args) do args[i] = dump(val) end if type(fmt) == "table" then -- hacky signal that we're called from internal_process_error(), and not to omit stack frames return error(format(fmt[1], unpack(args))) end return error(format(fmt, unpack(args)), 3) end local function internal_process_error(fmt, ...) process_error({"Internal error in `params` table: " .. fmt}, ...) end -- Check that a parameter or argument is in the form form Scribunto normalizes input argument keys into (e.g. 1 not "1", "foo" not " foo "). Otherwise, it won't be possible to normalize inputs in the expected way. Unless is_argument is set, also check that the name only contains one placeholder at most, and that strings don't resolve to numeric keys once the placeholder has been substituted. local function validate_name(name, desc, extra_name, is_argument) local normalized = scribunto_parameter_key(name) if name and name == normalized then if is_argument or type(name) ~= "string" then return end local placeholder = find(name, "\1", nil, true) if not placeholder then return elseif find(name, "\1", placeholder + 1, true) then error(format( "Internal error: expected %s to only contain one placeholder, but saw %s", extra_name and (desc .. dump(extra_name)) or desc, dump(name) )) end local first_name = gsub(name, "\1", "1") normalized = scribunto_parameter_key(first_name) if first_name == normalized then return end error(format( "Internal error: %s cannot resolve to numeric parameters once any placeholder has been substituted, but %s resolves to %s", extra_name and (desc .. dump(extra_name)) or desc, dump(name), dump(normalized) )) elseif normalized == nil then error(format( "Internal error: expected %s to be of type string or number, but saw %s", extra_name and (desc .. dump(extra_name)) or desc, type(name) )) end error(format( "Internal error: expected %s to be Scribunto-compatible: %s (a %s) should be %s (a %s)", extra_name and (desc .. dump(extra_name)) or desc, dump(name), type(name), dump(normalized), type(normalized) )) end local function validate_alias_options(...) local invalid = { required = true, default = true, template_default = true, allow_holes = true, disallow_holes = true, disallow_missing = true, } function validate_alias_options(param, name, main_param, alias_of) for k in pairs(param) do if invalid[k] then track("bad alias option") -- internal_process_error( -- "parameter %s cannot have the option %s, as it is an alias of parameter %s.", -- name, option, alias_of -- ) end end -- Soon, aliases will inherit options from the main parameter via __index. Track cases where this would happen. if main_param ~= true then for k in pairs(main_param) do if param[k] == nil and not invalid[k] then if k == "list" then -- these need to be changed to list = false to retain current behaviour track("mismatched list alias option") elseif not (k == "type" or k == "set" or k == "sublist") then -- rarely specified on aliases, as they're effectively inherited already track("mismatched alias option") end end end end end validate_alias_options(...) end -- TODO: give ranges instead of long lists, if possible. local function params_list_error(params, msg) local list, n = {}, 0 for name in sorted_pairs(params) do n = n + 1 list[n] = name end error(format( "Parameter%s %s.", format(n == 1 and " %s is" or "s %s are", concat_list(list, " and ", true)), msg ), 3) end -- Helper function for use with convert_val_error(). Format a list of possible choices using `concat_list` and -- conjunction "or", displaying "either " before the choices if there's more than one. local function format_choice_list(valid) return (#valid > 1 and "either " or "") .. concat_list(valid, " or ") end -- Signal an error for a value `val` that is not of the right type `valid` (which is either a string specifying a type, or -- a list of possible values, in the case where `set` was used). `name` is the name of the parameter and can be a -- function to signal an error (which is assumed to automatically display the parameter's name and value). `seetext` is -- an optional additional explanatory link to display (e.g. [[WT:LOL]], the list of possible languages and codes). local function convert_val_error(val, name, valid, seetext) if is_callable(name) then if type(valid) == "table" then valid = "choice, must be " .. format_choice_list(valid) end name(format("Invalid %s; the value %s is not valid%s", valid, val, seetext and "; see " .. seetext or "")) else if type(valid) == "table" then valid = format_choice_list(valid) else valid = "a valid " .. valid end error(format("Parameter %s must be %s; the value %s is not valid.%s", dump(name), valid, dump(val), seetext and " See " .. seetext .. "." or "")) end end -- Generate the appropriate error-signaling function given parameter value `val` and name `name`. If `name` is already -- a function, it is just returned; otherwise a function is generated and returned that displays the passed-in messaeg -- along with the parameter's name and value. local function make_parse_err(val, name) if is_callable(name) then return name end return function(msg) error(format("%s: parameter %s=%s", msg, name, val)) end end -------------------------------------- Value conversion ----------------------------- -- For a list parameter `name` and corresponding value `list_name` of the `list` field (which should have the same value -- as `name` if `list = true` was given), generate a pattern to match parameters of the list and store the pattern as a -- key in `patterns`, with corresponding value set to `name`. For example, if `list_name` is "tr", the pattern will -- match "tr" as well as "tr1", "tr2", ..., "tr10", "tr11", etc. If the `list_name` contains a \1 in it, the numeric -- portion goes in place of the \1. For example, if `list_name` is "f\1accel", the pattern will match "faccel", -- "f1accel", "f2accel", etc. Any \1 in `name` is removed before storing into `patterns`. local function save_pattern(name, list_name, patterns) name = type(name) == "string" and gsub(name, "\1", "") or name if find(list_name, "\1", nil, true) then patterns["^" .. gsub(pattern_escape(list_name), "\1", "([1-9]%%d*)") .. "$"] = name else patterns["^" .. pattern_escape(list_name) .. "([1-9]%d*)$"] = name list_name = list_name .. "\1" end validate_name(list_name, "the list field of parameter ", name) return patterns end -- A helper function for use with `sublist`. It is an iterator function for use in a for-loop that returns split -- elements of `val` using `sublist` (a Lua split pattern; boolean `true` to split on commas optionally surrounded by -- whitespace; "comma without whitespace" to split only on commas not followed by whitespace which have not been escaped -- by a backslash; or a function to do the splitting, which is passed two values, the value to split and a function to -- signal an error, and should return a list of the split elements). `name` is the parameter name or error-signaling -- function passed into convert_val(). local function split_sublist(val, name, sublist) if sublist == true then return gsplit(val, "%s*,%s*") -- Split an argument on comma, but not comma followed by whitespace. elseif sublist == "comma without whitespace" then -- If difficult cases, use split_on_comma. if find(val, "\\", nil, true) or match(val, ",%s") then return iterate_list(split_on_comma(val)) end -- Otherwise, use gsplit. return gsplit(val, ",") elseif type(sublist) == "string" then return gsplit(val, sublist) elseif not is_callable(sublist) then error(format('Internal error: expected `sublist` to be of type "string" or "function" or boolean `true`, but saw %s', dump(sublist))) end return iterate_list(sublist(val, make_parse_err(val, name))) end -- For parameter named `name` with value `val` and param spec `param`, if the `set` field is specified, verify that the -- value is one of the one specified in `set`, and throw an error otherwise. `name` is taken directly from the -- corresponding parameter passed into convert_val() and may be a function to signal an error. Optional `param_type` is -- a string specifying the conversion type of `val` and is used for special-casing: If `param_type` is "boolean", an -- internal error is thrown (since `set` cannot be used in conjunction with booleans) and if `param_type` is "number", -- no checking happens because in this case `set` contains numbers and is checked inside the number conversion function -- itself, after converting `val` to a number. Return the canonical value of `val` (which may be different from `val` -- if an alias map is given). local function check_set(val, name, param, param_type) if param_type == "boolean" then error(format('Internal error: cannot use `set` with `type = "%s"`', param_type)) -- Needs to be special cased because the check happens after conversion to numbers. elseif param_type == "number" then return val end local set, map = param.set if sets == nil then map = finalize_set(set, name) sets = {[set] = map} else map = sets[set] if map == nil then map = finalize_set(set, name) sets[set] = map end end local newval = map[val] if newval == true then return val elseif newval ~= nil then return newval end local list = {} for k, v in sorted_pairs(map) do if v == true then insert(list, dump(k)) else insert(list, ("%s (alias of %s)"):format(dump(k), dump(v))) end end -- If the parameter is not required then put "or empty" at the end of the list, to avoid implying the parameter is actually required. if not param.required then insert(list, "empty") end convert_val_error(val, name, list) end local function convert_language(val, name, param, allow_etym) local method, func = param.method if method == nil or method == "code" then func, method = get_language_by_code, "code" elseif method == "name" then func, method = get_language_by_name, "name" else error(format('Internal error: expected `method` for type `language` to be "code", "name" or undefined, but saw %s', dump(method))) end local lang = func(val, nil, allow_etym, param.family) if lang then return lang end local list, links = {"language"}, {"[[WT:LOL]]"} if allow_etym then insert(list, "etymology language") insert(links, "[[WT:LOL/E]]") end if param.family then insert(list, "family") insert(links, "[[WT:LOF]]") end convert_val_error(val, name, concat_list(list, " or ") .. " " .. (method == "name" and "name" or "code"), concat_list(links, " and ")) end local function convert_number(val, allow_hex) -- Call tonumber_extended with the `real_finite` flag, which filters out ±infinity and NaN. -- By default, specify base 10, which prevents 0x hex inputs from being converted. -- If `allow_hex` is set, then don't give a base, which means 0x hex inputs will work. local num = tonumber_extended(val, not allow_hex and 10 or nil, "finite_real") if not num then return num end if match(val, "[eEpP.]") then -- float track("number not an integer") end if find(val, "+", nil, true) then track("number with +") end -- Track various unusual number inputs to determine if it should be restricted to positive integers by default (possibly including 0). if not is_positive_integer(num) then track("number not a positive integer") if num == 0 then track("number is 0") elseif not is_integer(num) then track("number not an integer") end end return num end -- TODO: validate parameter specs separately, as it's making the handler code really messy at the moment. local type_handlers = setmetatable({ ["boolean"] = function(val) return yesno(val, true) end, ["family"] = function(val, name, param) local method, func = param.method if method == nil or method == "code" then func, method = get_family_by_code, "code" elseif method == "name" then func, method = get_family_by_name, "name" else error(format('Internal error: expected `method` for type `family` to be "code", "name" or undefined, but saw %s', dump(method))) end return func(val) or convert_val_error(val, name, "family " .. method, "[[WT:LOF]]") end, ["labels"] = function(val, name, param) -- FIXME: Should be able to pass in a parse_err function. return split_labels_on_comma(val) end, ["language"] = function(val, name, param) return convert_language(val, name, param, true) end, ["full language"] = convert_language, ["number"] = function(val, name, param) local allow_hex = param.allow_hex if allow_hex and allow_hex ~= true then error(format( 'Internal error: expected `allow_hex` for type `number` to be of type "boolean" or undefined, but saw %s', dump(allow_hex) )) end local num = convert_number(val, allow_hex) if param.set then -- Don't pass in "number" here; otherwise no checking will happen. num = check_set(num, name, param) end if num then return num end convert_val_error(val, name, (allow_hex and "decimal or hexadecimal " or "") .. "number") end, ["range"] = function(val, name, param) local allow_hex = param.allow_hex if allow_hex and allow_hex ~= true then error(format( 'Internal error: expected `allow_hex` for type `range` to be of type "boolean" or undefined, but saw %s', dump(allow_hex) )) end -- Pattern ensures leading minus signs are accounted for. local m1, m2 = match(val, "^(%s*%S.-)%-(%s*%S.*)") if m1 then m1 = convert_number(m1, allow_hex) if m1 then m2 = convert_number(m2, allow_hex) if m2 then return {m1, m2} end end end -- Try `val` if it couldn't be split into a range, and return a range of `val` to `val` if possible. local num = convert_number(val, allow_hex) if num then return {num, num} end convert_val_error(val, name, (allow_hex and "decimal or hexadecimal " or "") .. "number or a hyphen-separated range of two numbers") end, ["parameter"] = function(val, name, param) -- Use the `no_trim` option, as any trimming will have already been done. return scribunto_parameter_key(val, true) end, ["qualifier"] = function(val, name, param) return {val} end, ["references"] = function(val, name, param) return parse_references(val, make_parse_err(val, name)) end, ["genders"] = function(val, name, param) if not val:find("[,<]") then return {{spec = val}} end -- NOTE: We don't pass in allow_space_around_comma. Consistent with other comma-separated types, there shouldn't -- be spaces around the comma. return parse_gender_and_number_spec { spec = val, parse_err = make_parse_err(val, name), allow_multiple = true, } end, ["script"] = function(val, name, param) local method, func = param.method if method == nil or method == "code" then func, method = get_script_by_code, "code" elseif method == "name" then func, method = get_script_by_name, "name" else error(format('Internal error: expected `method` for type `script` to be "code", "name" or undefined, but saw %s', dump(method))) end return func(val) or convert_val_error(val, name, "script " .. method, "[[WT:LOS]]") end, ["string"] = function(val, name, param) -- To be removed as unnecessary. track("string") return val end, -- TODO: add support for resolving to unsupported titles. -- TODO: split this into "page name" (i.e. internal) and "link target" (i.e. external as well), which is more intuitive. ["title"] = function(val, name, param) local namespace = param.namespace if namespace == nil then namespace = 0 else local valid_type = type(namespace) ~= "number" and 'of type "number" or undefined' or not namespaces[namespace] and "a valid namespace number" or nil if valid_type then error(format('Internal error: expected `namespace` for type `title` to be %s, but saw %s', valid_type, dump(namespace))) end end -- Decode entities. WARNING: mw.title.makeTitle must be called with `decoded` (as it doesn't decode) and mw.title.new must be called with `val` (as it does decode, so double-decoding needs to be avoided). local decoded, prefix, title = decode_entities(val), param.prefix -- If the input is a fragment, treat the title as the current title with the input fragment. if sub(decoded, 1, 1) == "#" then -- If prefix is "force", only get the current title if it's in the specified namespace. current_title includes the namespace prefix. if current_namespace == nil then local current_title = mw_title.getCurrentTitle() current_title_text, current_namespace = current_title.prefixedText, current_title.namespace end if not (prefix == "force" and namespace ~= current_namespace) then title = new_title(current_title_text .. val) end elseif prefix == "force" then -- Unconditionally add the namespace prefix (mw.title.makeTitle). title = make_title(namespace, decoded) elseif prefix == "full override" then -- The first input prefix will be used as an override (mw.title.new). This can be a namespace or interwiki prefix. title = new_title(val, namespace) elseif prefix == nil or prefix == "namespace override" then -- Only allow namespace prefixes to override. Interwiki prefixes therefore need to be treated as plaintext (e.g. "el:All topics" with namespace 14 returns "el:Category:All topics", but we want "Category:el:All topics" instead; if the former is really needed, then the input ":el:Category:All topics" will work, as the initial colon overrides the namespace). mw.title.new can take namespace names as well as numbers in the second argument, and will throw an error if the input isn't a valid namespace, so this can be used to determine if a prefix is for a namespace, since mw.title.new will return successfully only if there's either no prefix or the prefix is for a valid namespace (in which case we want the override). local success success, title = pcall(new_title, val, match(decoded, "^.-%f[:]") or namespace) -- Otherwise, get the title with mw.title.makeTitle, which unconditionally adds the namespace prefix, but behaves like mw.title.new if the namespace is 0. if not success then title = make_title(namespace, decoded) end else error(format('Internal error: expected `prefix` for type `title` to be "force", "full override", "namespace override" or undefined, but saw %s', dump(prefix))) end local allow_external = param.allow_external if allow_external == true then return title or convert_val_error(val, name, "Wiktionary or external page title") elseif not allow_external then return title and is_internal_title(title) and title or convert_val_error(val, name, "Wiktionary page title") end error(format('Internal error: expected `allow_external` for type `title` to be of type "boolean" or undefined, but saw %s', dump(allow_external))) end, ["Wikimedia language"] = function(val, name, param) local fallback = param.fallback if fallback == true then return get_wm_lang_by_code_with_fallback(val) or convert_val_error(val, name, "Wikimedia language or language code") elseif not fallback then return get_wm_lang_by_code(val) or convert_val_error(val, name, "Wikimedia language code") end error(format('Internal error: expected `fallback` for type `Wikimedia language` to be of type "boolean" or undefined, but saw %s', dump(fallback))) end, }, { -- TODO: decode HTML entities in all input values. Non-trivial to implement, because we need to avoid any downstream functions decoding the output from this module, which would be double-decoding. Note that "title" has this implemented already, and it needs to have both the raw input and the decoded input to avoid double-decoding by me.title.new, so any implementation can't be as simple as decoding in __call then passing the result to the handler. __call = function(self, val, name, param, param_type, default) local val_type = type(val) -- TODO: check this for all possible parameter types. if val_type == param_type then return val elseif val_type ~= "string" then local expected = "string" if default and (param_type == "boolean" or param_type == "number") then expected = param_type .. " or " .. expected end error(format( "Internal error: %sargument %s has the type %s; expected a %s.", default and (default .. " for ") or "", name, dump(val_type), expected )) end local func = self[param_type] if func == nil then error(format("Internal error: %s is not a recognized parameter type.", dump(param_type))) end return func(val, name, param) end }) --[==[ func: export.convert_val(val, name, param) Convert a parameter value according to the associated specs listed in the `params` table passed to [[Module:parameters]]. `val` is the value to convert for a parameter whose name is `name` (used only in error messages). `param` is the spec (the value part of the `params` table for the parameter). In place of passing in the parameter name, `name` can be a function that throws an error, displaying the specified message along with the parameter name and value. This function processes all the conversion-related fields in `param`, including `type`, `set`, `sublist`, `convert`, etc. It returns the converted value. ]==] local function convert_val(val, name, param, default) local param_type = param.type or "string" -- If param.type is a function, resolve it to a recognized type. if is_callable(param_type) then param_type = param_type(val) end local convert, sublist = param.convert, param.sublist -- `val` might not be a string if it's the default value. if sublist and type(val) == "string" then local retlist, set = {}, param.set if convert then local thisindex, thisval, insval, parse_err = 0 if is_callable(name) then -- We assume the passed-in error function in `name` already shows the parameter name and raw value. function parse_err(msg) name(format("%s: item #%s=%s", msg_with_processed(msg, thisval, insval), thisindex, thisval) ) end else function parse_err(msg) error(format("%s: item #%s=%s of parameter %s=%s", msg_with_processed(msg, thisval, insval), thisindex, thisval, name, val) ) end end for v in split_sublist(val, name, sublist) do thisindex, thisval = thisindex + 1, v if set then v = check_set(v, name, param, param_type) end insert(retlist, convert(type_handlers(v, name, param, param_type, default), parse_err)) end else for v in split_sublist(val, name, sublist) do if set then v = check_set(v, name, param, param_type) end insert(retlist, type_handlers(v, name, param, param_type, default)) end end return retlist elseif param.set then val = check_set(val, name, param, param_type) end local retval = type_handlers(val, name, param, param_type, default) if convert then local parse_err if is_callable(name) then -- We assume the passed-in error function in `name` already shows the parameter name and raw value. if retval == val then -- This is an optimization to avoid creating a closure. The second arm works correctly even -- when retval == val. parse_err = name else function parse_err(msg) name(msg_with_processed(msg, val, retval)) end end else function parse_err(msg) error(format("%s: parameter %s=%s", msg_with_processed(msg, val, retval), name, val)) end end retval = convert(retval, parse_err) end -- If `sublist` is set but the input wasn't a string, return `retval` as a one-item list. if sublist then retval = {retval} end return retval end export.convert_val = convert_val -- used by [[Module:parameter utilities]] local function unknown_param(name, val, args_unknown) track("unknown parameters") args_unknown[name] = val return args_unknown end local function check_string_param_modifier(param_type, name, tag) if param_type and not (param_type == "string" or param_type == "parameter" or is_callable(param_type)) then internal_process_error( "%s cannot be set unless %s is set to %s (the default), %s or a function: parameter %s has the type %s.", tag, "type", "string", "parameter", name, param_type ) end end local function hole_error(params, name, listname, this, nxt, extra) -- `process_error` calls `dump` on values to be inserted into -- error messages, but with numeric lists this causes "numeric" -- to look like the name of the list rather than a description, -- as `dump` adds quote marks. Insert it early to avoid this, -- but add another %s specifier in all other cases, so that -- actual list names will be displayed properly. local offset, specifier, starting_from = 0, "%s", "" local msg = "Item %%d in the list of %s parameters must be given if item %%d is given, because %sthere shouldn't be any gaps due to missing%s parameters." local specs = {} if type(listname) == "string" then specs[2] = listname elseif type(name) == "number" then offset = name - 1 -- To get the original parameter. specifier = "numeric" -- If the list doesn't start at parameter 1, avoid implying -- there can't be any gaps in the numeric parameters if -- some parameter with a lower key is optional. for j = name - 1, 1, -1 do local _param = params[j] if not (_param and _param.required) then starting_from = format("(starting from parameter %d) ", dump(j + 1)) break end end else specs[2] = name end specs[1] = this + offset -- Absolute index for this item. insert(specs, nxt + offset) -- Absolute index for the next item. process_error(format(msg, specifier, starting_from, extra or ""), unpack(specs)) end local function check_disallow_holes(params, val, name, listname, extra) for i = 1, val.maxindex do if val[i] == nil then hole_error(params, name, listname, i, num_keys(val)[i], extra) end end end local function handle_holes(params, val, name) local param = params[name] local disallow_holes = param.disallow_holes -- Iterate up the list, and throw an error if a hole is found. if disallow_holes then check_disallow_holes(params, val, name, param.list, " or empty") end -- Iterate up the list, and throw an error if a hole is found due to a -- missing parameter, treating empty parameters as part of the list. This -- applies beyond maxindex if blank arguments are supplied beyond it, so -- isn't mutually exclusive with `disallow_holes`. local empty = val.empty if param.disallow_missing then if empty then -- Remove `empty` from `val`, so it doesn't get returned. val.empty = nil for i = 1, max(val.maxindex, empty.maxindex) do if val[i] == nil and not empty[i] then local keys = extend(num_keys(val), num_keys(empty)) sort(keys) hole_error(params, name, param.list, i, keys[i]) end end -- If there's no table of empty parameters, the check is identical to -- `disallow_holes`, except that the error message only refers to -- missing parameters, not missing or empty ones. If `disallow_holes` is -- also set, there's no point checking again. elseif not disallow_holes then check_disallow_holes(params, val, name, param.list) end end -- If `allow_holes` is set, there's nothing left to do. if param.allow_holes then -- do nothing -- Otherwise, remove any holes: `pairs` won't work, as it's unsorted, and -- iterating from 1 to `maxindex` times out with inputs like |100000000000=, -- so use num_keys to get a list of numerical keys sorted from lowest to -- highest, then iterate up the list, moving each value in `val` to the -- lowest unused positive integer key. This also avoids the need to create a -- new table. If `disallow_holes` is specified, then there can't be any -- holes in the list, so there's no reason to check again; this doesn't -- apply to `disallow_missing`, however. else if not disallow_holes then local keys, i = num_keys(val), 0 while true do i = i + 1 local key = keys[i] if key == nil then break elseif i ~= key then track("holes compressed") val[i], val[key] = val[key], nil end end end -- Some code depends on only numeric params being present when no holes are -- allowed (e.g. by checking for the presence of arguments using next()), so -- remove `maxindex`. val.maxindex = nil end end local function maybe_flatten(params, val, name) local param = params[name] if param.flatten then if param.allow_holes then process_error("For parameter %s, can't set both `allow_holes` and `flatten`", name) end if not param.sublist and param.type ~= "genders" and param.type ~= "labels" and param.type ~= "references" and param.type ~= "qualifier" then process_error("For parameter %s, can only set `flatten` along with `sublist` or a list-generating type", name) end -- Do the flattening ourselves rather than calling flatten() in [[Module:table]], which will attempt to -- flatten non-list objects like title objects, and cause an error in the process. -- FIXME: We should do this in-place if possible. local newlist = {} for _, sublist in ipairs(val) do for _, item in ipairs(sublist) do insert(newlist, item) end end val = newlist end return val end -- If both `template_default` and `default` are given, `template_default` takes precedence, but only on the template or -- module page. This means a different default can be specified for the template or module page example. However, -- `template_default` doesn't apply if any args are set, which helps (somewhat) with examples on documentation pages -- transcluded into the template page. HACK: We still run into problems on documentation pages transcluded into the -- template page when pagename= is set. Check this on the assumption that pagename= is fairly standard. local function convert_default_val(name, param, pagename_set, any_args_set, add_empty_sublist) if not pagename_set then local val = param.template_default if val ~= nil and not any_args_set and is_own_page() then return convert_val(val, name, param, "template default") end end local val = param.default if val ~= nil then return convert_val(val, name, param, "default") -- Sublist parameters should return an empty table if not given, but only do -- this if the parameter isn't also a list (in which case it will already -- be an empty table). -- FIXME: do this once all modules that pass in a sublist parameter treat an empty sublist identically to a nil argument; some currently do things based on the fact an argument exists at all. -- elseif add_empty_sublist and param.sublist then --return {} end end --[==[ Process arguments with a given list of parameters. Return a table containing the processed arguments. The `args` parameter specifies the arguments to be processed; they are the arguments you might retrieve from {frame:getParent().args} (the template arguments) or in some cases {frame.args} (the invocation arguments). The `params` parameter specifies a list of valid parameters, and consists of a table. If an argument is encountered that is not in the parameter table, an error is thrown. The structure of the `params` table is as described above in the intro comment. '''WARNING:''' The `params` table is destructively modified to save memory. Nonetheless, different keys can share the same value objects in memory without causing problems. The `return_unknown` parameter, if set to {true}, prevents the function from triggering an error when it comes across an argument with a name that it doesn't recognise. Instead, the return value is a pair of values: the first is the processed arguments as usual, while the second contains all the unrecognised arguments that were left unprocessed. This allows you to do multi-stage processing, where the entire set of arguments that a template should accept is not known at once. For example, an inflection-table might do some generic processing on some arguments, but then defer processing of the remainder to the function that handles a specific inflectional type. ]==] function export.process(args, params, return_unknown) -- Process parameters for specific properties local args_new, args_unknown, any_args_set, required, patterns, list_args, index_list, args_placeholders, placeholders_n = {} -- TODO: memoize the processing of each unique `param` value, since it's common for the same value to be used for many parameter names. for name, param in pairs(params) do validate_name(name, "parameter names") if param ~= true then local spec_type = type(param) if type(param) ~= "table" then internal_process_error( "spec for parameter %s must be a table of specs or the value true, but found %s.", name, spec_type ~= "boolean" and spec_type or param ) end -- Populate required table, and make sure aliases aren't set to required. if param.required then if required == nil then required = {} end required[name] = true end local listname, alias_of = param.list, param.alias_of if alias_of then validate_name(alias_of, "the alias_of field of parameter ", name) if alias_of == name then internal_process_error( "parameter %s cannot be an alias of itself.", name ) end local main_param = params[alias_of] -- Check that the alias_of is set to a valid parameter. if not (main_param == true or type(main_param) == "table") then internal_process_error( "parameter %s is an alias of an invalid parameter.", name ) end validate_alias_options(param, name, main_param, alias_of) -- Aliases can't be lists unless the canonical parameter is also a list. if listname and (main_param == true or not main_param.list) then internal_process_error( "list parameter %s is set as an alias of %s, which is not a list parameter.", name, alias_of ) -- Can't be an alias of an alias. elseif main_param ~= true then local main_alias_of = main_param.alias_of if main_alias_of ~= nil then internal_process_error( "alias_of cannot be set to another alias: parameter %s is set as an alias of %s, which is in turn an alias of %s. Set alias_of for %s to %s.", name, alias_of, main_alias_of, name, main_alias_of ) end end end local replaced_by = param.replaced_by if replaced_by then -- replaced_by can be `false`, which is OK validate_name(replaced_by, "the replaced_by field of parameter ", name) if replaced_by == name then internal_process_error( "parameter %s cannot be replaced by itself.", name ) end local main_param = params[replaced_by] -- Check that the replaced_by is set to a valid parameter. if not (main_param == true or type(main_param) == "table") then internal_process_error( "parameter %s is set to be replaced by an invalid parameter.", name ) end -- Can't be a replaced-by of a replaced-by. if main_param ~= true then local main_replaced_by = main_param.replaced_by if main_replaced_by ~= nil then internal_process_error( "replaced_by cannot be set to another replaced-by parameter: parameter %s is set as replaced by %s, which is in turn replaced by %s. Set replaced_by for %s to %s.", name, replaced_by, main_replaced_by, name, main_replaced_by ) end end if param.instead ~= nil then internal_process_error("the `instead` tag can only be given when `replaced_by` is set to `false`.") end elseif replaced_by == false then if param.instead ~= nil and type(param.instead) ~= "string" then internal_process_error( "the `instead` tag must be a string, but saw %s.", param.instead ) end end if replaced_by ~= nil then if param.reason ~= nil and type(param.reason) ~= "string" then internal_process_error( "the `reason` tag must be a string, but saw %s.", param.reason ) end end if listname then if not alias_of then local key = name if type(name) == "string" then key = gsub(name, "\1", "") end local list_arg = {maxindex = 0} args_new[key] = list_arg if list_args == nil then list_args = {} end list_args[key] = list_arg end local list_type = type(listname) if list_type == "string" then -- If the list property is a string, then it represents the name -- to be used as the prefix for list items. This is for use with lists -- where the first item is a numbered parameter and the -- subsequent ones are named, such as 1, pl2, pl3. patterns = save_pattern(name, listname, patterns or {}) elseif listname ~= true then internal_process_error( "list field for parameter %s must be a boolean, string or undefined, but saw a %s.", name, list_type ) elseif type(name) == "number" then if index_list ~= nil then internal_process_error( "only one numeric parameter can be a list, unless the list property is a string." ) end -- If the name is a number, then all indexed parameters from -- this number onwards go in the list. index_list = name else patterns = save_pattern(name, name, patterns or {}) end if find(name, "\1", nil, true) then if args_placeholders then placeholders_n = placeholders_n + 1 args_placeholders[placeholders_n] = name else args_placeholders, placeholders_n = {name}, 1 end end end end end --Process required changes to `params`. if args_placeholders then for i = 1, placeholders_n do local name = args_placeholders[i] params[gsub(name, "\1", "")], params[name] = params[name], nil end end -- Process the arguments for name, val in pairs(args) do any_args_set = true validate_name(name, "argument names", nil, true) -- Guaranteeing that all values are strings avoids issues with type coercion being inconsistent between functions. local val_type = type(val) if val_type ~= "string" then internal_process_error( "argument %s has the type %s; all arguments must be strings.", name, val_type ) end local orig_name, raw_type, index, canonical = name, type(name) if raw_type == "number" then if index_list and name >= index_list then index = name - index_list + 1 name = index_list end elseif patterns then -- Does this argument name match a pattern? for pattern, pname in next, patterns do index = match(name, pattern) -- It matches, so store the parameter name and the -- numeric index extracted from the argument name. if index then index = tonumber(index) name = pname break end end end local param = params[name] -- If the argument is not in the list of parameters, store it in a separate list. if not param then args_unknown = unknown_param(name, val, args_unknown or {}) elseif param == true then canonical = orig_name val = php_trim(val) if val ~= "" then -- If the parameter is duplicated, throw an error. if args_new[name] ~= nil then process_error( "Parameter %s has been entered more than once. This is probably because a parameter alias has been used.", canonical ) end args_new[name] = val end else if param.replaced_by == false then process_error( ("Parameter %%s has been removed and is no longer valid%s.%s"):format( param.reason and ", " .. param.reason or "", param.instead and " Instead, " .. param.instead .. "." or ""), name ) elseif param.replaced_by then process_error( ("Parameter %%s has been replaced by %%s%s."):format( param.reason and ", " .. param.reason or ""), name, param.replaced_by ) end if param.deprecated then track("deprecated parameter", name) end if param.require_index then -- Disallow require_index for numeric parameter names, as this doesn't make sense. if raw_type == "number" then internal_process_error( "cannot set require_index for numeric parameter %s.", name ) -- If a parameter without the trailing index was found, and -- require_index is set on the param, treat it -- as if it isn't recognized. elseif not index then args_unknown = unknown_param(name, val, args_unknown or {}) end end -- Check that separate_no_index is not being used with a numeric parameter. if param.separate_no_index then if raw_type == "number" then internal_process_error( "cannot set separate_no_index for numeric parameter %s.", name ) elseif type(param.alias_of) == "number" then internal_process_error( "cannot set separate_no_index for parameter %s, as it is an alias of numeric parameter %s.", name, param.alias_of ) end end -- If no index was found, use 1 as the default index. -- This makes list parameters like g, g2, g3 put g at index 1. -- If `separate_no_index` is set, then use 0 as the default instead. if not index and param.list then index = param.separate_no_index and 0 or 1 end -- Normalize to the canonical parameter name. If it's a list, but the alias is not, then determine the index. local raw_name = param.alias_of if raw_name then raw_type = type(raw_name) if raw_type == "number" then name = raw_name local main_param = params[raw_name] if main_param ~= true and main_param.list then if not index then index = param.separate_no_index and 0 or 1 end canonical = raw_name + index - 1 else canonical = raw_name end else name = gsub(raw_name, "\1", "") local main_param = params[name] if not index and main_param ~= true and main_param.list then index = param.separate_no_index and 0 or 1 end if not index or index == 0 then canonical = name elseif name == raw_name then canonical = name .. index else canonical = gsub(raw_name, "\1", index) end end else canonical = orig_name end -- Only recognize demo parameters if this is the current template or module's -- page, or its documentation page. if param.demo and not is_own_page("include_documentation") then args_unknown = unknown_param(name, val, args_unknown or {}) end -- Remove leading and trailing whitespace unless no_trim is true. if param.no_trim then check_string_param_modifier(param.type, name, "no_trim") else val = php_trim(val) end -- Empty string is equivalent to nil unless allow_empty is true. if param.allow_empty then check_string_param_modifier(param.type, name, "allow_empty") elseif val == "" then -- If `disallow_missing` is set, keep track of empty parameters -- via the `empty` field in `arg`, which will be used by the -- `disallow_missing` check. This will be deleted before -- returning. if index and param.disallow_missing then local arg = args_new[name] local empty = arg.empty if empty == nil then empty = {maxindex = 0} arg.empty = empty end empty[index] = true if index > empty.maxindex then empty.maxindex = index end end val = nil end -- Allow boolean false. if val ~= nil then -- Convert to proper type if necessary. local main_param = params[raw_name] if main_param ~= true then val = convert_val(val, orig_name, main_param or param) end -- Mark it as no longer required, as it is present. if required then required[name] = nil end -- Store the argument value. if index then local arg = args_new[name] -- If the parameter is duplicated, throw an error. if arg[index] ~= nil then process_error( "Parameter %s has been entered more than once. This is probably because a list parameter has been entered without an index and with index 1 at the same time, or because a parameter alias has been used.", canonical ) end arg[index] = val -- Store the highest index we find. local maxindex = arg.maxindex if index > maxindex then maxindex = index end if arg[0] ~= nil then arg.default, arg[0] = arg[0], nil if maxindex < 1 then maxindex = 1 end end arg.maxindex = maxindex if not params[name].list then args_new[name] = val -- Don't store index 0, as it's a proxy for the default. elseif index > 0 then arg[index] = val end else -- If the parameter is duplicated, throw an error. if args_new[name] ~= nil then process_error( "Parameter %s has been entered more than once. This is probably because a parameter alias has been used.", canonical ) end if not raw_name then args_new[name] = val else local main_param = params[raw_name] if main_param ~= true and main_param.list then local main_arg = args_new[raw_name] main_arg[1] = val -- Store the highest index we find. if main_arg.maxindex < 1 then main_arg.maxindex = 1 end else args_new[raw_name] = val end end end end end end -- Remove holes in any list parameters if needed. This must be handled -- straight after the previous loop, as any instances of `empty` need to be -- converted to nil. if list_args then for name, val in next, list_args do handle_holes(params, val, name) end end -- If the current page is the template which invoked this Lua instance, then ignore the `require` flag, as it -- means we're viewing the template directly. Required parameters sometimes have a `template_default` key set, -- which gets used in such cases as a demo. -- Note: this won't work on other pages in the Template: namespace (including the /documentation subpage), -- or if the #invoke: is on a page in another namespace. local pagename_set = args_new.pagename -- Handle defaults. for name, param in pairs(params) do if param ~= true then local arg_new = args_new[name] if arg_new == nil then args_new[name] = convert_default_val(name, param, pagename_set, any_args_set, true) elseif param.list and arg_new[1] == nil then local default_val = convert_default_val(name, param, pagename_set, any_args_set) if default_val ~= nil then arg_new[1] = default_val if arg_new.maxindex == 0 then arg_new.maxindex = 1 end end end end end -- Flatten nested lists if called for. This must come after setting the default. if list_args then for name, val in next, list_args do args_new[name] = maybe_flatten(params, val, name) end end -- The required table should now be empty. -- If any parameters remain, throw an error, unless we're on the current template or module's page. if required and next(required) ~= nil and not is_own_page() then params_list_error(required, "required") -- Return the arguments table. -- If there are any unknown parameters, throw an error, unless return_unknown is set, in which case return args_unknown as a second return value. elseif return_unknown then return args_new, args_unknown or {} elseif args_unknown and next(args_unknown) ~= nil then params_list_error(args_unknown, "not used by this template") end return args_new end return export nvds2x1q4or3uw7hq15nht1c9ben6p9 apple 0 620 36492 1649 2026-08-09T16:51:18Z Ghybu 12 /* ئینگلیزی */ 36492 wikitext text/x-wiki == ئینگلیزی == [[پەڕگە:Red Apple.jpg|thumb|A red '''apple''']] [[پەڕگە:Granny Smith Apples.jpg|thumb|A green '''apple''']] === ناو === # [[سێو]] # [[دارسێو]] [[پۆل:ئینگلیزی]] g4cj57kgxtkf6uphwx1d0d6qqls5bit 36494 36492 2026-08-09T16:56:18Z Ghybu 12 /* ئینگلیزی */ 36494 wikitext text/x-wiki == ئینگلیزی == [[پەڕگە:Red Apple.jpg|thumb|A red '''apple''']] [[پەڕگە:Granny Smith Apples.jpg|thumb|A green '''apple''']] === ناو === {{head|en|ناو}} # [[سێو]] # [[دارسێو]] [[پۆل:ئینگلیزی]] sjux9t5vwfupi48v41nd6siu4hk1b2w 36524 36494 2026-08-09T18:41:33Z Ghybu 12 /* ناو */ 36524 wikitext text/x-wiki == ئینگلیزی == [[پەڕگە:Red Apple.jpg|thumb|A red '''apple''']] [[پەڕگە:Granny Smith Apples.jpg|thumb|A green '''apple''']] === ناو === {{en-noun}} # [[سێو]] # [[دارسێو]] [[پۆل:ئینگلیزی]] 2uphosbohwy3pza8eod96grbdgufda9 armut 0 649 36514 24131 2026-08-09T18:17:44Z Ghybu 12 /* تورکی */ 36514 wikitext text/x-wiki == تورکی == === وشەڕەتناسی === {{واڵا}} ===ناو === {{head|tr|ناو}} # [[هەرمێ]] [[پۆل:تورکی]] 8vovzqnkrhh3enn4eicmkauqrszkpmh 36515 36514 2026-08-09T18:20:21Z Ghybu 12 /* ناو */ 36515 wikitext text/x-wiki == تورکی == === وشەڕەتناسی === {{واڵا}} ===ناو === {{head|tr|ناو}} # [[ھەرمێ]] [[پۆل:تورکی]] lzheanwmyh2h7kl2o0g7yjci3r3a0rw elma 0 1313 36493 24262 2026-08-09T16:54:51Z Ghybu 12 /* ناو */ 36493 wikitext text/x-wiki == تورکی == === وشەڕەتناسی === {{واڵا}} ===ناو === {{head|tr|ناو}} # [[سێو]] nfuolhx3wkj1irb5ierkhj1qetbxj10 pear 0 2394 36512 22276 2026-08-09T18:15:16Z Ghybu 12 /* ئینگلیزی */ 36512 wikitext text/x-wiki == ئینگلیزی == ===وشەڕەتناسی === {{واڵا}} === ناو === {{head|en|ناو}} # [[ھەرمێ]] # جۆرێکی میوەیە، تا سەرەکەی بچووک دەبێتەوە، و خوارەکەی فراونترە، و تامێکی شیرینی ھەیە == سەرچاوەکان == * فەرھەنگی ڤەژین * [https://dictionary.cambridge.org/ زانکۆی کامبرێج] {{Stub entry}} [[پۆل:ئینگلیزی]] 9azh5r2q0mkibc8khcc5pgr8suj3dfa 36513 36512 2026-08-09T18:16:22Z Ghybu 12 /* سەرچاوەکان */ 36513 wikitext text/x-wiki == ئینگلیزی == ===وشەڕەتناسی === {{واڵا}} === ناو === {{head|en|ناو}} # [[ھەرمێ]] # جۆرێکی میوەیە، تا سەرەکەی بچووک دەبێتەوە، و خوارەکەی فراونترە، و تامێکی شیرینی ھەیە === سەرچاوەکان === * فەرھەنگی ڤەژین * [https://dictionary.cambridge.org/ زانکۆی کامبرێج] [[پۆل:ئینگلیزی]] 10l66neq14zunvw4ks83g123psuvwd3 36525 36513 2026-08-09T18:42:34Z Ghybu 12 /* ئینگلیزی */ 36525 wikitext text/x-wiki == ئینگلیزی == ===وشەڕەتناسی === {{واڵا}} === ناو === {{en-noun}} # [[ھەرمێ]] # جۆرێکی میوەیە، تا سەرەکەی بچووک دەبێتەوە، و خوارەکەی فراونترە، و تامێکی شیرینی ھەیە === سەرچاوەکان === * فەرھەنگی ڤەژین * [https://dictionary.cambridge.org/ زانکۆی کامبرێج] [[پۆل:ئینگلیزی]] r14ln9rack6a8jfresf6qy8nolt5x8g تفاحة 0 4524 36495 23385 2026-08-09T16:57:13Z Ghybu 12 /* عەرەبی */ 36495 wikitext text/x-wiki == عەرەبی == === وشەڕەتناسی === {{واڵا}} ===ناو=== {{head|ar|ناو}} # [[سێو]] [[پۆل:عەرەبی]] 9z24ixww8x7j7jpeh19jvn3dgebsb84 36496 36495 2026-08-09T17:02:24Z Ghybu 12 /* عەرەبی */ 36496 wikitext text/x-wiki == عەرەبی == === وشەڕەتناسی === {{واڵا}} ===ناو=== {{head|ar|head=تُفَّاحَة}} # [[سێو]] [[پۆل:عەرەبی]] 9vbelqitoebgenbmg9hcyw6pirtck4z 36499 36496 2026-08-09T17:11:53Z Ghybu 12 /* عەرەبی */ 36499 wikitext text/x-wiki == عەرەبی == === وشەڕەتناسی === {{واڵا}} ===ناو=== {{head|ar|ناو|head=تُفَّاحَة}} # [[سێو]] [[پۆل:عەرەبی]] enfs6rdyvv01nbzw2dc3i3yr85lclqm 36516 36499 2026-08-09T18:26:24Z Ghybu 12 /* عەرەبی */ 36516 wikitext text/x-wiki == عەرەبی == ===ناو=== {{head|ar|ناو|head=كُمَّثْرَى|g=f}} # [[ھەرمێ]] l7awuqn5r3prcbn00aqaro2vnxuni05 36517 36516 2026-08-09T18:28:24Z Ghybu 12 پووچەڵکردنەوەی پێداچوونەوەی [[Special:Diff/36516|36516]] لە لایەن [[Special:Contributions/Ghybu|Ghybu]] ([[User talk:Ghybu|لێدوان]]) 36517 wikitext text/x-wiki == عەرەبی == === وشەڕەتناسی === {{واڵا}} ===ناو=== {{head|ar|ناو|head=تُفَّاحَة|g=f}} # [[سێو]] [[پۆل:عەرەبی]] 6u27dpm96inodnrlvy0iglf6kpblx61 سێو 0 5476 36510 36478 2026-08-09T17:37:32Z Ghybu 12 /* وەرگێڕانەکان */ 36510 wikitext text/x-wiki == کوردیی ناوەندی == [[File:Fresh ripe apples on a tree (54015357813).jpg|thumb|'''سێوی''' سوور]] === وشەڕەتناسی === {{واڵا}} === ناو === #جۆرێکی [[میوە|میوەیە]]. سێ ڕەنگی هەیە: [[سەوز]] و [[سوور]] و [[زەرد]]. ==== وەرگێڕانەکان ==== {{وەرگێڕان-سەر|میوەیە}} * ئینگلیزی: {{t+|en|apple}} * تورکی: {{t+|tr|elma}} * عەرەبی: {{t+|ar|تُفَّاحَة}} * فارسی: {{t+|fa|سیب|tr=sēb}} {{وەرگێڕان-بن}} 19kbtkinzcr09o2p4vj4sh8krnlog1r 36523 36510 2026-08-09T18:39:52Z Ghybu 12 /* ناو */ 36523 wikitext text/x-wiki == کوردیی ناوەندی == [[File:Fresh ripe apples on a tree (54015357813).jpg|thumb|'''سێوی''' سوور]] === وشەڕەتناسی === {{واڵا}} === ناو === {{ckb-noun}} #جۆرێکی [[میوە|میوەیە]]. سێ ڕەنگی هەیە: [[سەوز]] و [[سوور]] و [[زەرد]]. ==== وەرگێڕانەکان ==== {{وەرگێڕان-سەر|میوەیە}} * ئینگلیزی: {{t+|en|apple}} * تورکی: {{t+|tr|elma}} * عەرەبی: {{t+|ar|تُفَّاحَة}} * فارسی: {{t+|fa|سیب|tr=sēb}} {{وەرگێڕان-بن}} jb38jrlhrvyp1mye0gdbeldzjkearp3 کردن 0 6881 36528 36070 2026-08-09T20:12:14Z Ghybu 12 /* چاوگ */ 36528 wikitext text/x-wiki == کوردیی ناوەندی == === وشەڕەتناسی === {{واڵا}} === چاوگ === {{ckb-verb}} #دروستکردن #ئەنجامدان {| class="wikitable sortable mw-collapsible" |+گەردانکردن ! ! colspan="4" |-کرد- ! colspan="4" |-کە- |- !جێناو !ڕابردووی نزیک !ڕابردووی تەواو !ڕابردووی بەردەوام !ڕابردووی دوور !ڕانەبردوو !دانانی !داخوازی !نەرێنی |- |من |کردم |کردوومە |دەمکرد |کردبووم |دەکەم |بکەم | | |- |تۆ |کردت |کردووتە |دەتکرد |کردبووت |دەکەیت |بکەیت |بکە |مەکە |- |ئەو |کردی |کردوویە |دەیکرد |کردبووی |دەکات |بکات | | |- |ئێمە |کردمان |کردوومانە |دەمانکرد |کردبوومان |دەکەین |بکەین | | |- |ئێوە |کردتان |کردووتانە |دەتانکرد |کردبووتان |دەکەن |بکەن |بکەن |مەکەن |- |ئەوان |کردیان |کردوویانە |دەیانکرد |کردبوویان |دەکەن |بکەن | | |} {| class="wikitable sortable mw-collapsible" |+داڕشتن !ناوی بکەری !ناوی بەرکاری !ناوی چاوگی !ناوی شوێن !ناوی ئامێر !هاوەڵناوی بکەری !هاوەڵناوی بەرکاری !هاوەڵناوی لێهاتوویی |- |بکەر |کردوو |کردن |کردگە |کەیەک |بکەر |کراو |کردنی |- | |کردە |کردار |کرگە |کەیەنگ |کەیندە | |کەیەن |- | |کردگ |کەیمان/کرمان |کردمان |پێکەر |کەیۆک | |کردەنی |- | |کردی |کرشت/کەیشت |کردارگە | |کەیەک | |کردەمەنی |- | | | |کردووگە | |کردار | | |- | | | | | |کەیناک | | |- | | | | | |کردەوار | | |} {{Stub entry}} [[پۆل:چاوگ]] [[پۆل:چاوگە سادە کوردییەکان]] qeutxlyo4ecem37396epe1ww95ln5zl 36532 36528 2026-08-09T20:42:13Z Ghybu 12 format 36532 wikitext text/x-wiki == کوردیی ناوەندی == === وشەڕەتناسی === {{واڵا}} === چاوگ === {{ckb-verb}} #دروستکردن #ئەنجامدان {| class="wikitable sortable mw-collapsible" |- | colspan="9" style="padding-left:1em"| گەردانکردن &nbsp;&nbsp; |- ! ! colspan="4" |-کرد- ! colspan="4" |-کە- |- !جێناو !ڕابردووی نزیک !ڕابردووی تەواو !ڕابردووی بەردەوام !ڕابردووی دوور !ڕانەبردوو !دانانی !داخوازی !نەرێنی |- |من |کردم |کردوومە |دەمکرد |کردبووم |دەکەم |بکەم | | |- |تۆ |کردت |کردووتە |دەتکرد |کردبووت |دەکەیت |بکەیت |بکە |مەکە |- |ئەو |کردی |کردوویە |دەیکرد |کردبووی |دەکات |بکات | | |- |ئێمە |کردمان |کردوومانە |دەمانکرد |کردبوومان |دەکەین |بکەین | | |- |ئێوە |کردتان |کردووتانە |دەتانکرد |کردبووتان |دەکەن |بکەن |بکەن |مەکەن |- |ئەوان |کردیان |کردوویانە |دەیانکرد |کردبوویان |دەکەن |بکەن | | |} {| class="wikitable sortable mw-collapsible" |- | colspan="9" style="padding-left:1em"| داڕشتن &nbsp;&nbsp; |- !ناوی بکەری !ناوی بەرکاری !ناوی چاوگی !ناوی شوێن !ناوی ئامێر !هاوەڵناوی بکەری !هاوەڵناوی بەرکاری !هاوەڵناوی لێهاتوویی |- |بکەر |کردوو |کردن |کردگە |کەیەک |بکەر |کراو |کردنی |- | |کردە |کردار |کرگە |کەیەنگ |کەیندە | |کەیەن |- | |کردگ |کەیمان/کرمان |کردمان |پێکەر |کەیۆک | |کردەنی |- | |کردی |کرشت/کەیشت |کردارگە | |کەیەک | |کردەمەنی |- | | | |کردووگە | |کردار | | |- | | | | | |کەیناک | | |- | | | | | |کردەوار | | |} {{Stub entry}} [[پۆل:چاوگ]] [[پۆل:چاوگە سادە کوردییەکان]] 4aotp11qv0z6rkp9tvoaako7pnz9336 kirin 0 7347 36527 17722 2026-08-09T20:10:48Z Ghybu 12 /* کرمانجی */ 36527 wikitext text/x-wiki == کرمانجی == === چاوگ === {{kmr-verb}} # [[کردن]] [[Category:کرمانجی]] rxbdepzt76pooodeyd3vni42kdb9uzd 36530 36527 2026-08-09T20:14:37Z Ghybu 12 /* کرمانجی */ 36530 wikitext text/x-wiki == کرمانجی == === چاوگ === {{kmr-verb}} # [[کردن]] 2x00ypmwzrdzcwqdlxvshqhjk0rowvc میدیاویکی:Common.css 8 7829 36541 34310 2026-08-10T04:18:18Z Aram 9 NavToggle { float: right; > float: left; } بەپێی [[لێدوانی بەکارھێنەر:-Haryad#داڕێژە:trans-top|ئێرە]] 36541 css text/css /* ---------- <nowiki> CSS Style * Indent with tab characters * One line space between each declaration * One selector per line * Opening brace on the same line as the last selector * Don't omit optional semicolons * Closing brace on its own line * Closing brace indented to match the preceding style declarations * Alternatively, very short style rules may be all on one line * If changing e.g. rules for displaying scripts, consider also updating [[MediaWiki:Mobile.css]] * Single quotes around font-family values that do not consist of ASCII lowercase letters and hyphens. ---------- */ /* csslint adjoining-classes:false */ /* See also [[Special:Mypage/vector.css]], [[MediaWiki:Vector.css]] */ /* make headers include contained floats, so they don't wander around (yes, is magic, it tells the browser the tag is a container) */ h1, h2, h3, h4, h5, h6 { width: auto; } sup, sub { /* prevent odd line-height for raised and lowered characters */ line-height: 1em; } /* and keep links at normal font size/weight, else they inherit the header attributes */ .editsection { font-size: 100%; font-weight: normal; } pre code, code code { border: none; padding: 0; } pre var, code var { color: #777; } /* Convenience column count classes for list templates and such */ .column-count-2 { -moz-column-count: 2; -ms-column-count: 2; -webkit-column-count: 2; column-count: 2; } .column-count-3 { -moz-column-count: 3; -ms-column-count: 3; -webkit-column-count: 3; column-count: 3; } /* This fixes a bug where certain Tibetan terms are cut off at the bottom... */ #firstHeading { overflow: visible; } .ns-0 .timed-text { display: none; } .client-js .audiometa { display: none; } .client-nojs .audiofile { display: none; } .translations-columm { background-color: #FFFFE0; width: 48%; text-align: left; vertical-align: top; } /* wikitable is part of core. prettytable is deprecated but still used in some places, until fully replaced: */ table.prettytable { margin: 1em 1em 1em 0; background: #f9f9f9; border: 1px #aaaaaa solid; border-collapse: collapse; } table.prettytable th, table.prettytable td { border: 1px #aaaaaa solid; padding: 0.2em; } table.prettytable th { background: #f2f2f2; text-align: center; } table.prettytable caption { margin-left: inherit; margin-right: inherit; } /* main page-style boxes */ table.bluebox { background: #f9f9ff; border: 1px solid #aaaaaa; margin-bottom: 0.5em; width: 100%; } table.bluebox td { padding: 0.5em; } table.bluebox th { background-color: #006699; color: #ffffff; padding: 0.5em; } /* default setting for {{qualifier}} and {{sense}}. (Related classes that can be overridden: .qualifier-comma, .qualifier-content, .ib-brac, .qualifier-brac, .sense-qualifier-colon.) */ .ib-comma { font-style: normal; } .ib-content { font-style: italic; } .ib-content i, .ib-content em { font-style: normal; } .context-qualifier-colon { display: none; } /* default setting for {{seeCites}} */ .see-cites { } /* default style for indented "see also" */ .disambig-see-also, .disambig-see-also-2 { text-indent: 2em; } .mention-tr { font-style: italic; } /* add left-to-right mark */ .mention-gloss-paren::before, .mention-tr-paren::before { content: "‎"; } /* non-italic and non-bold gloss */ .mention-gloss-double-quote, .mention-gloss-paren, .mention-tr-gloss-separator-comma, .mention-tr-paren, .mention-tr-gloss-separator-comma, .mention-gloss { font-style: normal; font-weight: normal; } /* * highlight Arabic, Hebrew, cuneiform (Sumero-Akkadian and Ugaritic and Old Persian) * in usage examples, as it is not bolded */ .Arab.e-example b, .Arab.e-quotation b, .Hebr.e-example b, .Hebr.e-quotation b, .Xsux.e-example b, .Xsux.e-quotation b, .Ugar.e-example b, .Ugar.e-quotation b, .Xpeo.e-example b, .Xpeo.e-quotation b { background-color: #FFF0F5; font-size: inherit; } /* default style for "form of" definitions */ .use-with-mention, .form-of-definition { font-style: italic; } .use-with-mention i { font-style: normal; } .form-of-definition-link { font-style: normal; } .use-with-mention .mention, .form-of-definition-link .mention { font-style: normal; font-weight: bold; } /* Inflection tables ----------------------------- */ /* To colour links in inflection tables (currently done with {{notred}}) black without using #ifexist and allowing for user override */ .inflection-table a.new { color: inherit; } /* fill a parent NavContent collapsing box */ .NavContent table.inflection { width: 100%; } /* basic inflection table styles */ table.inflection { border-collapse: collapse; border: 1px solid; background: #ffffff; } table.inflection caption { caption-side: bottom; padding: 0.33em 1em; text-align: left; font-size: smaller; } /* subtle grey for alternate row backgrounds */ table.inflection tr:nth-child(odd) { background: #fcfcfc; } /* and for row headers */ table.inflection tr:nth-child(odd):not(.rowgroup) th { background: #f6f6f6; } table.inflection td, table.inflection th { padding: 0.33em 1em; vertical-align: baseline; } /* but vertically center multi-row cells */ table.inflection td[rowspan] { vertical-align: middle; } table.inflection td { text-align: center; } /* regular row headers, in the left column */ table.inflection th { background: #f9f9f9; text-align: left; } /* rowgroup header rows */ table.inflection tr.rowgroup th { background: #eeeeee; text-align: center; } /* first-of-several rowgroup header cells is aligned left */ table.inflection tr.rowgroup th:first-child { text-align: left; } table.inflection tr.rowgroup th:first-child:only-child { text-align: center; } /* rows with dividers above */ table.inflection tr.divider th { border-top: 3px solid #999999; } /* transliterations */ table.inflection .translit { color: #888; } /* header links aren’t coloured until hover/active */ table.inflection th a:link, table.inflection th a:visited { color: inherit; text-decoration: inherit; } table.inflection th a:hover { color: #0645ad; text-decoration: underline; } table.inflection th a:active { color: #faa700; text-decoration: underline; } /* Russian verb tables */ table.inflection-ru.inflection-verb { background: #ffffff; } /* and for row headers */ table.inflection-ru.inflection-verb tr:nth-child(odd):not(.rowgroup) th { background: #fcfcda; } table.inflection-ru.inflection-verb th { background: #ffffe0; } table.inflection-ru.inflection-verb tr.rowgroup th { background: #e0e0ff; } table.inflection-ru.inflection-verb tr.divider th { border-top-color: #9090d0; } /* ---- end of Inflection tables */ /* InterProject */ .interProject { display: none; clear: both; border-top: 2px dotted #AAAAAA; margin-top: 2em; } /* CSS moved from various templates */ .checktrans { background-color: #f0fff0; } .rfc-trans { background-color: #f0fff0; } .trreq { background-color: #f0fff0; } .ttbc { background-color: #f0fff0; } /* t+ t t- templates: */ .tneg { vertical-align: 20%; font-size: 80%; } .tneg a { color: #cc2200 !important; } /* monobook redlink colour, standard #ba0000 */ .tneg a:visited { color: #a55858 !important; } /* standard visited redlink colour */ .tpos { vertical-align: 20%; font-size: 80%; } .tpos a { color: #002bb8 !important; } /* standard bluelink colour */ .tpos a:visited { color: #5a3696 !important; } /* standard visited bluelink colour */ .tpos a:active { color: #faa700 !important; } /* standard bluelink colour */ .tunk { vertical-align: 20%; font-size: 80%; } .tunk a { color: #3366BB !important; } /* standard external interwiki colour */ .tunk a:active { color: #3366BB !important; } /* redundant */ /* for parentheses, we use .tlc and .tlcp, the latter has no definition here (display:inline) */ .tlc { display: none; } /* Main Page heading removal */ .page-Main_Page #footer-info-lastmod, .page-Main_Page #siteSub, .page-Main_Page #contentSub, .page-Main_Page #firstHeading, .page-Main_Page #catlinks { display: none; /* NOT important */ } /* live ajax link checking support */ /* #002bb8 for (unvisited) blue, #ba0000 for red; so #5d155c for unknown */ a.dunno, #p-personal a.dunno { color: #5d155c; } /* #5a3696 for (visited) blue, #a55858 for red; so #744777 for unknown */ a.dunno:visited, #p-personal a.dunno:visited { color: #744777; } /* for pages which exist but lack the section we're looking for */ .partlynew:link, #p-personal :link.partlynew, .partlynew :link { color: #B88D00; } .partlynew:visited, #p-personal .partlynew:visited, .partlynew :visited { color: #826F34; } /* partlynew should have lower precedence than red-links... */ .partlynew .new:link { color: #BA0000; } .partlynew .new:visited { color: #a55858; } .accelerated:link { color: #1fba00; } .accelerated:visited { color: #66a759; } /* Try to make search results more obviously links */ .mw-special-Search #bodyContent li a { font-weight: bold; } .mw-special-Search .searchmatch { font-weight: normal; color: #000000; background-color: #FFEE77; } /* To give a flexible column like structure to manual indices [[User:Conrad.Irwin]] */ ol.index, div.index ol { margin: 0.2em; } ol.index li, div.index ol li, div.index h3 { float: left; width: 20em; list-style-type: none; margin-left: 0; margin-right: 0; overflow: hidden; } div.index h3 { font-size: 160%; width: 12.5em; margin-right: 0.2em; } /* [[WT:FEED]] */ #p-feedback a { cursor: pointer; } /* Old version */ #mw-revision-info { border: 2px solid #8888FF; border-left: 0px; border-right: 0px; font-size: 110%; margin: 5px; margin-left: 0px; } #mw-editingold { margin-left: 15px; margin: 5px; padding: 5px; border: 2px solid #CC0000; border-width: 2px 0px; } /* Make Recent changes look nice again */ span.newpage, span.minor, span.searchmatch, span.bot, span.unpatrolled { font-family: sans-serif !important; } /* NavFrames */ div.Boxmerge, div.NavFrame, details.NavFrame { margin: 0px 0px -1px 0px; padding: 2px; border: 1px solid #aaaaaa; text-align: center; border-collapse: collapse; font-size: 95%; overflow: auto; width: auto; } div.Boxmerge div.NavFrame { border-style: none; border-style: hidden; } div.NavPic { background-color: #ffffff; margin: 0px; padding: 2px; float: left; } div.NavFrame div.NavHead, details.NavFrame summary.NavHead { min-height: 1.6em; font-weight: bold; font-size: 100%; text-align: left; background-color: #efefef; padding-left: 10px; background-image: -webkit-gradient(linear, left top, left bottom, from(#EFEFEF), to(#DFDFDF), color-stop(0.6, #E3E3E3)); background-image: -moz-linear-gradient(top, #EFEFEF, #E3E3E3 60%, #DFDFDF); background-image: -o-linear-gradient(top, #EFEFEF, #E3E3E3 60%, #DFDFDF); } div.NavFrame div.NavHead:hover, details.NavFrame summary.NavHead:hover { background-color: #F6F6F6; background-image: -webkit-gradient(linear, left top, left bottom, from(#EFEFEF), to(#F3F3F3), color-stop(0.6, #EAEAEA)); background-image: -moz-linear-gradient(top, #EFEFEF, #EAEAEA 60%, #F3F3F3); background-image: -o-linear-gradient(top, #EFEFEF, #EAEAEA 60%, #F3F3F3); } div.NavFrame div.NavHead:active, details.NavFrame summary.NavHead:active { background-color: #E3E3E3; background-image: -webkit-gradient(linear, left top, left bottom, from(#E3E3E3), to(#DFDFDF), color-stop(0.6, #D3D3D3)); background-image: -moz-linear-gradient(top, #E3E3E3, #D3D3D3 60%, #DFDFDF); background-image: -o-linear-gradient(top, #E3E3E3, #D3D3D3 60%, #DFDFDF); } div.pseudo div.NavHead:hover { background-color: #EFEFEF; } div.pseudo div.NavHeadpseudo:active { background-color: #EFEFEF; } div.NavFrame p { font-size: 100%; } div.NavFrame div.NavContent, details.NavFrame div.NavContent { font-size: 100%; } div.NavFrame div.NavContent p, details.NavFrame div.NavContent p { font-size: 100%; } div.NavEnd { margin: 0px; padding: 0px; line-height: 1px; clear: both; } span.NavToggle { cursor: pointer; float: left; font-weight: normal; font-size: smaller; } .list-switcher-element { text-align: center; } .list-switcher-element .NavToggle { float: none; } .term-list-header { font-style: italic; font-weight: inherit; } .term-list-header:before { content: "("; } .term-list-header:after { content: "):"; } /* Main page fixes */ #interwiki-completelist { font-weight: bold; } /* Style for horizontal UL lists */ .horizontal p, .horizontal ul, .horizontal li { display: inline; } .horizontal ul { padding: 0 0 0 .2em; margin: 0; } .horizontal li { padding: 0 0.1em 0 0.1em; } /* Syle for generic inline lists */ .inline p, .inline dd, .inline dt, .inline ul, .inline li { margin: 0; display: inline; } /* [[w:CSS filter#Negation pseudo-class hack]] since IE <8 doesn't support pseudo elements and IE 8 doesn't support :last-child pseudo-class. */ html:not([dummyparam]) .horizontal li:after { content: ","; } html:not([dummyparam]) .horizontal li:last-child:after { content: "."; } /* stuff visible when previewing an edit only */ .previewonly { display: none; } #wikiPreview .previewonly { display: inline !important; } /* senseids */ .senseid:target { background-color: #DEF; } /* show the "serial and" in the disambiguation see also */ .serial-and { display: inline; } /* hide the serial commas */ .serial-comma { display: none; } /* Gender and number templates */ .gender, .number, .noun-class { font-style: italic; } abbr { border-bottom-width: 0; } /* hide "did you mean" on Noarticletext self-links */ .did-you-mean strong { display: none; } /* make script/module errors smaller */ .scribunto-error { font-size: 75%; } /* allow inline hieroglyphs */ .mw-hiero-outer { display: inline-table; vertical-align: middle; font-size: 11px; } /* formats derived terms */ .derivedterms { background: #F8F8FF; } .derivedterms .CategoryTreeChildren { column-count: 3; -moz-column-count: 3; -webkit-column-count: 3; } /* softer highlighting of 'updated since last visit' */ span.updatedmarker { background-color:#D0F0C0; } /* {{side box}} */ table.mbox-small { /* For the "small=yes" option. */ clear: right; float: right; margin: 4px 0 4px 1em; width: 238px; font-size: 88%; line-height: 1.25em; } table.mbox-small-left { /* For the "small=left" option. */ margin: 4px 1em 4px 0; width: 238px; border-collapse: collapse; font-size: 88%; line-height: 1.25em; } .HQToggle, .nyms-toggle, .usex-toggle { font-size: smaller; position: relative; bottom: 1px; left: 5px; } /* {{TOC limit}} */ /* Allow limiting of which header levels are shown in a TOC; <div class="toclimit-3">, for instance, will limit to showing ==headings== and ===headings=== but no further (as long as there are no =headings= on the page, which there shouldn't be according to the MoS). */ .toclimit-2 .toclevel-1 ul, .toclimit-3 .toclevel-2 ul, .toclimit-4 .toclevel-3 ul, .toclimit-5 .toclevel-4 ul, .toclimit-6 .toclevel-5 ul, .toclimit-7 .toclevel-6 ul { display: none; } /* Support for scripts. See [[Wiktionary:Scripts]]. */ /* Default style for headwords. */ strong.headword { } /* Default style for mentions outside of "form of" definitions. See [[Wiktionary:Votes/2007-10/style for mentioned terms]] */ .mention { font-style: italic; } .mention i, i .mention { font-style: normal; } /* "Empty" script, used for languages with no script set. This is the same as Latn in practice, but allows tagging and formatting of such cases. */ .None, .Imag, .Morse, .Semap, .Zyyy { } /* Latin This is the default script, so it has no definitions. */ .Latn { } /* This is needed to overcome the "smartness" of certain web browsers */ .Latn[lang=ja], .Latn[lang=ko], .Latn[lang=zh] { font-family: inherit; } /* Latin Fraktur */ .Latf { font-family: UnifrakturMaguntia, UnifrakturCook, Unifraktur, 'Code2001', Tahoma, 'Arial Unicode MS', sans-serif; font-size: 125%; } /* Navajo */ .nv-Latn { font-family: Calibri, 'Aboriginal Sans', 'DejaVu Sans', 'Arial Unicode MS', sans-serif !important; } /* Pitjantjatjara (ḻ ṉ ṟ ṯ and capitals) */ .pjt-Latn { font-family: 'Microsoft Sans Serif', Tahoma, 'Code2000', sans-serif; } /* Latin extended */ /* no styles for .Latinx, .unicode, .Unicode */ /* Aghwan */ .Aghb { font-family: OptimaModoki, sans-serif; } .Aghb, .Aghb * { font-style: normal; } /* Ahom */ .Ahom { font-family: AhomUnicode, 'Noto Serif Ahom', 'Unifont Upper', sans-serif; } .Ahom, .Ahom * { font-style: normal; } /* Arabic */ .Arab, .fa-Arab, .glk-Arab, .kk-Arab, .ks-Arab, .ku-Arab, .mzn-Arab, .ota-Arab, .pa-Arab, .ps-Arab, .sd-Arab, .tt-Arab, .ug-Arab, .ur-Arab, .ms-Arab { font-family: 'Iranian Sans', 'Segoe UI', Tahoma, 'Microsoft Sans Serif', 'Arial Unicode MS', sans-serif; font-size: 133%; direction: rtl; unicode-bidi: embed; } /* no italics, no bold */ .Arab, .Arab *, .fa-Arab, .fa-Arab *, .glk-Arab, .glk-Arab *, .kk-Arab, .kk-Arab *, .ks-Arab, .ks-Arab *, .ku-Arab, .ku-Arab *, .mzn-Arab, .mzn-Arab *, .ota-Arab, .ota-Arab *, .pa-Arab, .pa-Arab *, .ps-Arab, .ps-Arab *, .sd-Arab, .sd-Arab *, .tt-Arab, .tt-Arab *, .ug-Arab, .ug-Arab *, .ur-Arab, .ur-Arab *, .ms-Arab, .ms-Arab * { font-style: normal; font-weight: normal; } /* Persian, Ottoman Turkish */ .fa-Arab, .ota-Arab { font-family: 'Iranian Sans', 'Geeza Pro', Tahoma, sans-serif; } /* Kashmiri */ .ks-Arab { font-family: 'Noto Naskh Arabic', Amiri, Mirza, 'Scheherazade New', Scheherazade, 'Geeza Pro', Tahoma, sans-serif; } /* Kurdish, Punjabi Shahmukhi, Urdu */ .ku-Arab, .pa-Arab, .ur-Arab { font-family: Tahoma, 'Arial Unicode MS', 'UT Cairo', 'UT Naskh', sans-serif; } /* Pashto */ .ps-Arab { font-family: Pokhto, 'Pashto Kror Asiatype', 'Scheherazade New', Scheherazade, sans-serif; } /* Sindhi */ .sd-Arab { font-family: 'MB Sindhi', Tahoma, 'MBSarang Sattar', 'MB Bhitai Sattar', 'MB Lateefi', 'Ayaz Gul', 'Iranian Sans', sans-serif; } /* Uyghur */ .ug-Arab { font-family: 'UKIJ Tuz', 'Microsoft Uighur', 'Scheherazade New', Scheherazade, 'Iranian Sans', 'Code2000', sans-serif; } /* Imperial Aramaic */ .Armi { font-family: 'Segoe UI Historic', 'Aramaic Imperial Yeb', 'Noto Sans Imperial Aramaic', sans-serif; direction: rtl; unicode-bidi: embed; } .Armi, .Armi * { font-style: normal; } /* Armenian */ .Armn { font-family: Mshtakan, Arial, 'Segoe UI', Tahoma, 'Arian AMU', 'DejaVu Sans', sans-serif; } /* no italics */ .Armn, .Armn * { font-style: normal; } /* Avestan */ .Avst { font-family: Avestan, Ahuramzda, 'Noto Sans Avestan', sans-serif; font-size: 125%; direction: rtl; unicode-bidi: embed; font-variant-ligatures: none; /* Avestan language */ } /* no italics */ .Avst, .Avst * { font-style: normal; } .Avst:lang(pal) { /* Pazend (Middle Persian in Avestan script) */ font-variant-ligatures: normal; } /* Balinese */ .Bali { font-family: 'Noto Sans Balinese', sans-serif; } .Bali, .Bali * { font-style: normal; } /* Bamum */ .Bamu { font-family: 'Noto Sans Bamum', sans-serif; } .Bamu, .Bamu * { font-style: normal; } /* Batak */ .Batk { font-family: Batak-Unicode, sans-serif; } .Batk, .Batk * { font-style: normal; } /* Bengali */ .Beng { font-family: 'Bangla Sangam MN', UniBangla, 'Arial Unicode MS', 'Code2000', Likhan, 'UT Bengali Dhaka', Vrinda, sans-serif; font-size: 130%; } .Beng, .Beng * { font-style: normal; } .as-Beng { font-family: 'Bangla Sangam MN', UniBangla, 'Arial Unicode MS', 'Code2000', Likhan, 'UT Bengali Dhaka', Vrinda, sans-serif; font-size: 130%; } .as-Beng, .as-Beng * { font-style: normal; } /* Zhuyin (Bopomofo) */ .Bopo { font-family: MOESongUN, DFKai-SB, 'Microsoft Yahei', 'Microsoft Jhenghei', 'Source Han Sans TC', 'Source Han Sans TW', 'Noto Sans CJK TC', sans-serif; } .Bopo, .Bopo * { font-style: normal; } /* Brahmi */ .Brah { font-family: 'Segoe UI Historic', 'Noto Sans Brahmi'; } .Brah, .Brah * { font-style: normal; } /* Braille */ .Brai { font-size: 150%; } .Brai, .Brai * { font-style: normal; } /* Buginese */ .Bugi { font-family: Saweri, sans-serif; } .Bugi, .Bugi * { font-style: normal; } /* Buhid */ .Buhd, .Buhd * { font-style: normal; } /* Chakma */ .Cakm { font-family: RibengUni, sans-serif; } .Cakm, .Cakm * { font-style: normal; } /* Unified Canadian Aboriginal Syllabics */ .Cans { font-family: 'Euphemia UCAS', Euphemia, 'Aboriginal Sans', OskiBlackfoot, 'Code2000', 'Everson Mono Unicode', sans-serif; font-size: 110%; } .Cans, .Cans * { font-style: normal; } /* Carian, Italic, Linear B, Lycian, Lydian */ .Cari, .Ital, .Linb, .Lyci, .Lydi { font-family: 'Segoe UI Historic', Aegean, 'Noto Sans Carian', 'Noto Sans Old Italic', 'Noto Sans Linear B', 'Noto Sans Lycian', 'Noto Sans Lydian', sans-serif; font-size: 125%; } .Cari, .Cari *, .Ital, .Ital *, .Linb, .Linb *, .Lyci, .Lyci *, .Lydi, .Lydi * { font-style: normal; } /* Cham */ .Cham { font-family: 'Code2000', 'JG ChamCambodia', sans-serif; font-size: 1.1em; } .Cham, .Cham * { font-style: normal; } /* Cherokee */ .Cher { font-family: Digohweli, 'Aboriginal Sans', 'Code2000', Marin, 'Rotinonhsonni Sans', 'Everson Mono Unicode', sans-serif; font-size: 110%; } .Cher, .Cher * { font-style: normal; } /* Coptic */ .Copt { font-family: Quivira, 'New Athena Unicode', 'Code2000', sans-serif; font-size: 1.3em; } .Copt, .Copt * { font-style: normal; } /* Cypriot */ .Cprt { font-family: 'Segoe UI Historic', Aegean, sans-serif; font-size: 1.15em; direction: rtl; unicode-bidi: embed; } .Cprt, .Cprt * { font-style: normal; } /* Cyrillic */ .Cyrl, .Cyrs { font-family: Helvetica, Geneva, 'Arial Unicode MS', 'Lucida Sans Unicode', 'Code2000', sans-serif; } .Cyrl, .Cyrl * { font-style: normal; } /* Old Cyrillic (Old Church Slavonic, Old East Slavic) */ .Cyrs { font-family: BukyVede, 'Kliment Std', 'RomanCyrillic Std', Menaion, 'Menaion Medieval', Lazov, Dilyana, 'Hirmos Ponomar', 'Hirmos Ponomar TT', 'Fedorovsk Unicode', 'Fedorovsk Unicode TT', 'Code2000', 'DejaVu Sans', 'Lucida Grande', 'Arial Unicode MS', 'Lucida Sans Unicode', sans-serif; font-size: 125%; } .Cyrs, .Cyrs * { font-style: normal; } /* Devanagari */ .Deva { font-family: 'Adobe Devanagari', 'Noto Serif Devanagari', Utsaah, 'Devanagari MT', Raghu, Gargi, JanaSanskrit, JanaHindi, Siddhanta, sans-serif; font-size: 120%; } i.Deva { font-style: normal; } /* Deseret */ .Dsrt { font-family: 'Segoe UI Symbol', 'Code2001', 'MPH 2B Damase', 'Everson Mono', sans-serif; } .Dsrt, .Dsrt * { font-style: normal; } /* Demotic (Egyptian) */ .Egyd, .Egyd * { font-style: normal; direction: rtl; } /* Egyptian hieroglyphs */ .Egyp { font-family: 'Noto Sans Egyptian Hieroglyphs', Abydos, Aegyptus, 'Segoe UI Historic', sans-serif; font-size: 150%; } .Egyp, .Egyp * { font-style: normal; } /* Ethiopic (Ge'ez) */ .Ethi { font-family: 'Abyssinica SIL', Nyala, 'Code2000', 'Ethiopia Jiret', 'GF Zemen Unicode', 'TITUS Cyberbit Basic', 'Visual Geez Unicode', 'Visual Geez Unicode Agazian', 'Visual Geez Unicode Title', sans-serif; font-size: 120%; } .Ethi, .Ethi * { font-style: normal; } /* Khutsuri */ .Geok, .Geok * { font-style: normal; } /* Georgian */ .Geor { font-family: 'DejaVu Sans', 'Arial Unicode MS', Sylfaen, sans-serif; } .Geor, .Geor * { font-style: normal; } /* Glagolitic */ .Glag { font-family: 'Segoe UI Historic', BukyVede, Dilyana, 'Noto Sans Glagolitic', sans-serif; font-size: 150%; } .Glag, .Glag * { font-style: normal; } /* Gothic */ .Goth { font-family: 'Segoe UI Historic', 'Code2001', Skeirs, 'MPH 2B Damase', sans-serif; } .Goth, .Goth * { font-style: normal; } b.Goth, strong.Goth { font-size: larger; } /* Greek */ .Grek { font-family: Athena, Gentium, 'Gentium Plus', 'Palatino Linotype', 'Arial Unicode MS', 'Lucida Sans Unicode', 'Lucida Grande', 'Code2000', sans-serif; } i.Grek.mention, i.polytonic.mention { font-style: normal; } /* Polytonic Greek */ .polytonic { font-family: 'SBL Greek', 'New Athena Unicode', 'DejaVu Sans', Athena, Gentium, 'Gentium Plus', 'Palatino Linotype', Times, 'Arial Unicode MS', 'Lucida Sans Unicode', 'Lucida Grande', 'Code2000', sans-serif; } /* Gujarati */ .Gujr { font-family: 'Noto Sans Gujarati', 'Gujarati Sangam MN', 'Gujarati MT', Shruti, 'Lucida Grande', 'Arial Unicode MS', 'Lucida Sans Unicode', 'Code2000', 'TITUS Cyberbit Basic', 'Chrysanthi Unicode', 'Bitstream Cyberbit', 'Bitstream CyberBase', 'Bitstream Vera', 'Visual Geez Unicode', sans-serif; font-size: 125%; } .Gujr, .Gujr * { font-style: normal; } /* Gurmukhi */ .Guru { font-family: 'Gurmukhi MN', 'Gurmukhi MT', 'UT Punjabi Amritsar', 'Lucida Grande', 'Arial Unicode MS', 'Lucida Sans Unicode', 'Code2000', 'TITUS Cyberbit Basic', 'Chrysanthi Unicode', 'Bitstream Cyberbit', 'Bitstream CyberBase', 'Bitstream Vera', 'Visual Geez Unicode', sans-serif; font-size: 125%; } .Guru, .Guru * { font-style: normal; } /* Chinese (Han) */ /* Hani: generic */ /* Hans: simplified */ /* Hant: traditional */ .Hans { font-family: 'PingFang SC', DengXian, 'Source Han Sans SC', 'Source Han Sans CN', 'Noto Sans CJK SC', 'Microsoft Yahei', SimHei, SimSun, NSimSun, SimSun-ExtB, Song, 'Heiti SC', HanaMinA, HanaMinB, sans-serif; } .Hani, .Hant { font-family: 'PingFang TC', 'Source Han Sans TC', 'Source Han Sans TW', 'Noto Sans CJK TC', 'Microsoft Jhenghei', MOESongUN, PMingLiU, PMingLiU-ExtB, MingLiU, MingLiU-ExtB, Ming, 'Heiti TC', HanaMinA, HanaMinB, sans-serif; } .Hani, .Hans, .Hant { font-size: 120%; line-height: 1; } .Hani, .Hani *, .Hans, .Hans *, .Hant, .Hant * { font-style: normal; } .Hani:lang(vi) { font-family: 'Nom Na Tong', 'HAN NOM A', 'HAN NOM B', Sun-ExtA, Sun-ExtB, Ming-Lt-HKSCS-UNI-H, Ming-Lt-HKSCS-ExtB, HanaMinA, HanaMinB, HanaMin, 'PingFang TC', MingLiU, MingLiU-ExtB, 'MingLiU_HKSCS', 'MingLiU_HKSCS-ExtB', SimSun, SimSun-ExtB, 'Arial Unicode MS', 'TITUS Cyberbit Basic', sans-serif; /* CJK Unified Ideographs Extension C and Extension D (U+2A700..U+2B734, U+2B740..U+2B81F) font-family: Sun-ExtB, 'MingLiU_HKSCS-ExtB', Ming-Lt-HKSCS-ExtB, HanaMinB, sans-serif; **/ } /* Hanunoo */ .Hano { font-family: Quivira, 'MPH 2B Damase', sans-serif; font-size: 1.1em; } .Hano, .Hano * { font-style: normal; } /* Hatran */ .Hatr { font-family: 'Noto Sans Hatran', sans-serif; direction: rtl; } /* Hebrew */ .Hebr { font-family: 'SBL Hebrew', 'Taamey David CLM', 'Taamey Frank CLM', Alef, 'Noto Sans Hebrew', Narkisim, Miriam, Kinryu, 'Arial Hebrew', Arial, 'Adobe Hebrew', serif; font-size: 115%; direction: rtl; unicode-bidi: embed; } .Hebr, .Hebr * { font-style: normal; font-weight: normal; } big.Hebr, strong.Hebr, b.Hebr, b .Hebr { font-size: 144%; } .Hebr b { font-size: 125%; } /* Hluw: Anatolian Hieroglyphs */ .Hluw { font-family: 'Noto Sans Anatolian Hieroglyphs', Anatolian; font-size: 200%; } .Hluw, .Hluw * { font-style: normal; } /* Hiragana: see .Jpan */ /* Javanese */ .Java { font-family: 'Tuladha Jejeg', 'Javanese Text', adjisaka, 'Noto Sans Javanese', sans-serif; } .Java, .Java * { font-style: normal; } /* Japanese scripts .Hira = Hiragana .Kana = Katakana (used for Ainu language) .Jpan = Hiragana + Katakana + Kanji */ .Hira, .Jpan, .Kana { font-family: 'Hiragino Kaku Gothic Pro', Osaka, 'Yu Gothic', Meiryo, 'Source Han Sans J', 'Source Han Sans JP', 'Noto Sans CJK JP', 'Droid Sans Japanese', 'MS PGothic', 'MS Gothic', 'MS PMincho', 'MS Mincho', HanaMinA, HanaMinB, sans-serif; font-size: 120%; line-height: 1; } .Hira, .Hira *, .Jpan, .Jpan *, .Kana, .Kana * { font-style: normal; } .Jpan ruby rt { font-family: 'Yu Gothic UI', 'Meiryo UI', 'MS UI Gothic', sans-serif; font-size: 60%; } /* Kayah Li, Ol Chiki, Rejang */ .Kali, .Olck { font-family: 'Code2000', sans-serif; } .Kali, .Kali *, .Olck, .Olck *, .Rjng, .Rjng * { font-style: normal; } /* Katakana (Ainu language): see .Jpan */ /* Kharoshthi */ .Khar { font-family: 'Segoe UI Historic', 'MPH 2B Damase', sans-serif; font-size: 1.1em; direction: rtl; unicode-bidi: embed; } .Khar, .Khar * { font-style: normal; } /* Khmer */ .Khmr { font-family: 'Khmer OS', 'Khmer OS Content', 'Leelawadee UI', 'Noto Sans Khmer', 'Code2000', 'Khmer Mondulkiri U OT ls', sans-serif; font-size: 125%; } .Khmr, .Khmr * { font-style: normal; } /* Kannada */ .Knda { font-family: 'Kannada Sangam MN', JanaKannada, Tunga, Kedage, RaghuKannada, Sampige, 'Arial Unicode MS', 'Code2000', 'Bitstream Cyberbit', 'Bitstream CyberBase', sans-serif; font-size: 125%; } .Knda, .Knda * { font-style: normal; } /* Korean */ .Kore, .Hang { font-family: 'Apple SD Gothic Neo', 'Malgun Gothic', Dotum, Gulim, 'NanumBarunGothic YetHangul', NanumBarunGothic, UnDotum, 'Source Han Sans K', 'Source Han Sans KR', 'Noto Sans CJK KR', NanumGothic, 'NanumMyeongjo YetHangul', NanumMyeongjo, Batang, UnBatang, sans-serif; font-size: 120%; line-height: 1; } .Kore, .Kore *, .Hang, .Hang * { font-style: normal; } /* Kaithi */ .Kthi { font-family: 'Noto Sans Kaithi', sans-serif; } .Kthi, .Kthi * { font-style: normal; } /* Lanna */ .Lana { font-family: 'Lanna Alif', 'Noto Sans Tai Tham', sans-serif; } .Lana, .Lana * { font-style: normal; } /* Lao */ .Laoo { font-family: 'Phetsarath OT', 'Saysettha OT', 'JG Basic Lao Opentype', 'JG Basic2 Lao Opentype', 'JG LaoTimes Opentype', 'Phagnoum Lao Unicode Opentype', 'JG Lao Old Arial Opentype', DokChampa, 'Code2000', 'JG Lao Classic Opentype', 'Alice0 Unicode', 'Alice1 Unicode', 'Alice2 Unicode', 'Alice3 Unicode', 'Alice4 Unicode', 'Alice5 Unicode', sans-serif; font-size: 125%; } .Laoo, .Laoo * { font-style: normal; } /* Lepcha */ .Lepc { font-family: 'Noto Sans Lepcha', Mingzat, sans-serif; } .Lepc, .Lepc * { font-style: normal; } /* Limbu */ .Limb { font-family: 'Code2000', 'MPH 2B Damase', sans-serif; } .Limb, .Limb * { font-style: normal; } /* Lisu */ .Lisu { font-family: 'Noto Sans Lisu', sans-serif; } .Lisu, .Lisu * { font-style: normal; } /* Mandaic */ .Mand { font-family: 'Noto Sans Mandaic', sans-serif; } .Mand, .Mand * { font-style: normal; } /* Manichaean */ .Mani, .Mani * { font-style: normal; } /* Meroitic cursive */ .Merc { font-family: 'Segoe UI Historic', Aegyptus, sans-serif; font-size: 150%; } .Merc, .Merc * { font-style: normal; } /* Meroitic hieroglyphs */ .Mero { font-family: Aegyptus, sans-serif; font-size: 150%; } .Mero, .Mero * { font-style: normal; } /* Malayalam */ .Mlym { font-family: 'Malayalam Sangam MN', Kartika, 'Code2000', 'Lucida Grande', 'Arial Unicode MS', 'Lucida Sans Unicode', 'TITUS Cyberbit Basic', 'Chrysanthi Unicode', 'Bitstream Cyberbit', 'Bitstream CyberBase', 'Bitstream Vera', 'Visual Geez Unicode', sans-serif; font-size: 125%; } .Mlym, .Mlym * { font-style: normal; } /* Classical Mongolian, Manchu, Xibe, Clear Script */ .Mong, .mnc-Mong, /* Manchu */ .sjo-Mong, /* Xibe */ .xwo-Mong /* Clear Script, Todo */ { font-family: 'Mongolian Baiti', 'Noto Sans Mongolian', sans-serif; font-size: 140%; -webkit-writing-mode: vertical-lr; -moz-writing-mode: vertical-lr; writing-mode: vertical-lr; layout-flow: vertical-ideographic; } .Mong, .Mong *, .mnc-Mong, .mnc-Mong *, .sjo-Mong, .sjo-Mong *, .xwo-Mong, .xwo-Mong * { font-style: normal; } /* Meitei Mayek */ .Mtei { font-family: 'Eeyek Unicode', 'Noto Sans Meetei Mayek', sans-serif; } .Mtei, .Mtei * { font-style: normal; } /* Burmese */ .Mymr { font-family: TharLon, Padauk, 'Myanmar Text', 'Myanmar3', 'Myanmar2', 'Myanmar1', ParabaikSans, 'MyMyanmar sans-serif'; font-size: 130%; } .Mymr, .Mymr * { font-style: normal; font-weight: normal; } big.Mymr, strong.Mymr, b.Mymr, b .Mymr { font-size: 162%; } .Mymr b { font-size: 130%; } /* North Arabian */ .Narb { font-family: 'Noto Sans Old North Arabian', sans-serif; } .Narb, .Narb * { font-style: normal; } /* N'ko */ .Nkoo, .Nkoo * { font-style: normal; } /* Ogham */ .Ogam { font-family: 'Segoe UI Historic', Beth-Luis-Nion, Pollach, 'Maigh Nuad', 'Craobh Ruadh', 'Everson Mono Ogham', Cog, Crosta, 'TITUS Ogham', 'Ragnarok Ogham', sans-serif; font-size: 125%; } .Ogam, .Ogam * { font-style: normal; font-weight: normal; } big.Ogam, strong.Ogam, b.Ogam, b .Ogam { font-size: 156%; } .Ogam b { font-size: 125%; } /* Orkhon Runes */ .Orkh { font-family: 'Segoe UI Historic', Orkun, 'Old Turkic', sans-serif; } .Orkh, .Orkh * { font-style: normal; } /* Oriya */ .Orya { font-family: 'Oriya Sangam MN', sans-serif; font-size: 125%; } .Orya, .Orya * { font-style: normal; } /* Osmanya */ .Osma, .Osma * { font-style: normal; } /* Palmyrene */ .Palm, .Palm * { font-family: 'Noto Sans Palmyrene'; } /* Old Permian */ .Perm { font-family: 'Noto Sans Old Permic'; } /* Phags-pa */ .Phag { font-family: 'BabelStone Phags-pa Book', 'Microsoft PhagsPa', sans-serif; -webkit-writing-mode: vertical-lr; -moz-writing-mode: vertical-lr; writing-mode: vertical-lr; layout-flow: vertical-ideographic; } .Phag, .Phag * { font-style: normal; } /* Inscriptional Pahlavi */ .Phli { font-family: 'Segoe UI Historic', 'Noto Sans Inscriptional Pahlavi', Shapour, 'ZH Mono', sans-serif; } .Phli, .Phli * { font-style: normal; } /* Book Pahlavi */ .Phlv, .Phlv * { font-style: normal; } /* Phoenician */ .Phnx { font-family: 'Segoe UI Historic', 'ALPHABETUM Unicode', 'MPH 2B Damase', Aegean, 'Code2001', 'Free Sans', sans-serif; font-size: 125%; direction: rtl; unicode-bidi: embed; } .Phnx, .Phnx * { font-style: normal; } /* Pollard */ .Plrd, .Plrd * { font-style: normal; } /* Parthian */ .Prti { font-family: 'Segoe UI Historic', 'Noto Sans Inscriptional Parthian', 'ZH Mono', sans-serif; } .Prti, .Prti * { font-style: normal; } /* Rejang */ .Rjng, .Rjng * { font-family: 'Noto Sans Rejang', 'Code2000', sans-serif; } /* Runic (Germanic) */ .Runr { font-family: 'Segoe UI Historic', Junicode, 'Free Mono', 'Caslon Roman', 'Segoe UI Symbol', 'Code2000', 'Everson Mono', 'TITUS Cyberbit Basic', sans-serif; font-size: 130%; } .Runr, .Runr * { font-style: normal; } /* Samaritan */ .Samr { font-family: 'Noto Sans Samaritan', Quivira, 'Everson Mono', Unifont, sans-serif; font-size: 150%; direction: rtl; unicode-bidi: embed; } .Samr, .Samr * { font-style: normal; } /* South Arabian */ .Sarb { font-family: 'Segoe UI Historic', Qataban, 'Noto Sans Old South Arabian', sans-serif; } .Sarb, .Sarb * { font-style: normal; } /* Saurashtra */ .Saur { font-family: Pagul, sans-serif; } .Saur, .Saur * { font-style: normal; } /* SignWriting */ .Sgnw, .Sgnw * { font-style: normal; } /* Shavian */ .Shaw { font-family: 'Segoe UI Historic', 'Code2001', 'Everson Mono', sans-serif; } .Shaw, .Shaw * { font-style: normal; } /* Sharada */ .Shrd { font-family: 'Noto Sans Sharada', sans-serif; font-size: 125%; } .Shrd, .Shrd * { font-style: normal; } /* Sinhalese */ .Sinh { font-family: 'Sinhala Sangam MN', KaputaUnicode, KandyUnicode, Dinamina, DinaminaUniWeb, Potha, Madhura, sans-serif; font-size: 125%; } .Sinh, .Sinh * { font-style: normal; } /* Sorang Sompeng */ .Sora, .Sora * { font-style: normal; } /* Sundanese */ .Sund { font-family: 'Sundanese Unicode', 'Noto Sans Sundanese', sans-serif; } .Sund, .Sund * { font-style: normal; } /* Syloti Nagri */ .Sylo { font-family: 'Noto Sans Syloti Nagri', sans-serif; } .Sylo, .Sylo * { font-style: normal; } /* Syriac */ .Syrc { font-family: 'Estrangelo Edessa', 'San Francisco', 'Code2000', 'Bitstream Cyberbit', 'Bitstream CyberBase', 'Bitstream Vera', 'TITUS Cyberbit Basic', sans-serif; font-size: 1.3em; direction: rtl; unicode-bidi: embed; } .Syrc, .Syrc * { font-style: normal; } /* Serto or Western form of Syriac */ .Syrc:lang(tru) { font-family: 'Noto Sans Syriac Western', 'Serto Jerusalem', 'Noto Sans Syriac'; } /* Madnhaya or Eastern form of Syriac */ .Syrc:lang(aii) { font-family: 'Noto Sans Syriac Eastern', 'Noto Sans Syriac'; } /* Tagbanwa */ .Tagb { font-family: Quivira, Tagbanwa, sans-serif; font-size: 1.1em; } .Tagb, .Tagb * { font-style: normal; } /* Tai Le */ .Tale { font-family: 'Microsoft Tai Le', 'Tai Le Valentinium', 'MPH 2B Damase', sans-serif; } .Tale, .Tale * { font-style: normal; } /* New Tai Lue */ .Talu { font-family: 'Noto Sans New Tai Lue', 'Microsoft New Tai Lue', 'Dai Banna SIL Book', 'Husb_New_Tai_Lue_WB', sans-serif; } .Talu, .Talu * { font-style: normal; } /* Tamil */ .Taml { font-family: 'Tamil Sangam MN', InaiMathi, Vijaya, Akshar, JanaTamil, 'Code2000', ETTamilNew, 'Lohit Tamil', 'Arial Unicode MS', 'Free Serif', Latha, sans-serif; font-size: 125%; } .Taml, .Taml * { font-style: normal; } /* Tangut */ .Tang { font-family: 'BabelStone Tangut Wenhai', 'Tangut N4694', 'Tangut Yinchuan', 'New Tangut', 'New Tangut Std', 'Tangut TWU', 'Babelstone Tangut Wenhai', sans-serif; font-size: 1.2em; text-orientation: upright; -webkit-text-orientation: upright } .Tang, .Tang * { font-style: normal; font-weight: normal; } /* Tai Viet */ .Tavt { font-family: 'Tai Heritage Pro', 'Noto Sans Tai Viet', sans-serif; } .Tavt, .Tavt * { font-style: normal; } /* Telugu */ .Telu { font-family: 'Telugu Sangam MN', sans-serif; font-size: 125%; } .Telu, .Telu * { font-style: normal; } /* Tifinagh (Berber) */ .Tfng { font-family: 'Hapax Berbère', Ebrima, 'Code2000', DejaVu, sans-serif; font-size: 120%; } .Tfng, .Tfng * { font-style: normal; } /* Tagalog */ .Tglg { font-family: 'Noto Sans Tagalog', 'Tagalog Stylized', 'Baybayin Lopez', 'Tagalog Doctrina 1593', 'Noto Sans Hanunoo', 'Code2000', sans-serif; } .Tglg, .Tglg * { font-style: normal; } /* Thaana */ .Thaa, .Thaa * { font-style: normal; } /* Thai */ .Thai { font-family: 'Leelawadee UI', Leelawadee, 'Arial Unicode MS', 'Code2000', sans-serif; font-size: 125%; } .Thai, .Thai * { font-style: normal; } /* Tibetan */ .Tibt, .xzh-Tibt { font-family: 'Noto Serif Tibetan', 'Noto Sans Tibetan', Jomolhari-ID, 'Tibetan Machine Uni', 'Tibetan Machine Web', Jomolhari, 'Microsoft Himalaya', sans-serif; font-size: 130%; } .Tibt, .Tibt *, .xzh-Tibt, .xzh-Tibt * { font-style: normal; } .Tibt.mention { font-size: 130%; } /* Zhang-Zhung */ .xzh-Tibt { font-family: 'BabelStone Tibetan sMar-chen', sans-serif; } /* Ugaritic */ .Ugar { font-family: 'Oxford Ugaritic', 'Segoe UI Historic', Aegean, 'Code2001', sans-serif; } .Ugar, .Ugar * { font-style: normal; } /* Vai */ .Vaii { font-family: Ebrima, 'Code2000', sans-serif; font-size: 1.1em; } .Vaii, .Vaii * { font-style: normal; } /* Old Persian cuneiform */ .Xpeo { font-family: 'Segoe UI Historic', Aegean, Xerxes, 'Code2001', sans-serif; } .Xpeo, .Xpeo * { font-style: normal; } /* Sumero-Akkadian cuneiform */ .Xsux { font-family: Akkadian, FreeIdgSerif, CuneiformComposite, 'Segoe UI Historic', sans-serif; /* Neo-Assyrian form */ /* font-family: FreeIdgSerif, Akkadian, CuneiformComposite, 'Segoe UI Historic', sans-serif; */ /* Old Assyrian form */ /* font-family: Akkadian, CuneiformComposite, FreeIdgSerif, 'Segoe UI Historic', sans-serif; /* /* Neo-Sumerian form */ /* font-family: Akkadian, CuneiformComposite, FreeIdgSerif, 'Segoe UI Historic', sans-serif; /* /* Classical Sumerian form */ font-size: 125%; } .Xsux, .Xsux * { font-style: normal; font-weight: normal; } /* Specify Hittite fonts */ .Xsux:lang(hit) { font-family: UllikummiA, Akkadian, FreeIdgSerif, CuneiformComposite, 'Segoe UI Historic', sans-serif; } /* Specify Sumerian fonts */ .Xsux:lang(sux) { font-family: CuneiformComposite, Akkadian, 'Segoe UI Historic', sans-serif; } /* Yi */ .Yiii, .Yiii * { font-style: normal; } /* Mathematical notation */ .Zmth, .Zmth * { font-style: normal; } /* Symbols */ .Zsym { font-family: Symbola, sans-serif; font-size: 150%; } .Zsym, .Zsym * { font-style: normal; } /* Rumi (Malay) numerals */ .Ruminumerals { font-size:1.2em; } /* Multilingual writing systems and notations */ /* English Phonemic Representation (AHD) */ .enPR { font-family: 'Arial Unicode MS', 'Lucida Grande', Gentium, 'Gentium Plus', 'Gentium Alternative', 'TITUS Cyberbit Basic', 'Code2000', 'Lucida Sans Unicode', sans-serif; } /* International Phonetic Alphabet */ .IPA, .IPAchar { font-family: Gentium, 'Gentium Plus', GentiumAlt, 'DejaVu Sans', 'Segoe UI', 'Lucida Grande', 'Charis SIL', 'Doulos SIL', 'TITUS Cyberbit Basic', 'Code2000', 'Lucida Sans Unicode', sans-serif; font-size: 110%; font-variant-ligatures: no-common-ligatures; } .IPA, .IPA *, .IPAchar, .IPAchar * { font-style: normal; } /* Musical notation */ .musical { font-family: Musica, 'Musical Symbols', Euterpe, sans-serif; font-size: 1.4em; } .musical, .musical * { font-style: normal; } /* A kludge that lets the members of [[:Category:Redirected combining characters]] be displayed. */ body.page-Category_Combining_characters div#mw-pages li, body.page-Category_Redirected_combining_characters div#mw-pages li, body.page-Category_IPA_combining_characters div#mw-pages li { letter-spacing: 0.3em; } /* Tabbed Languages support */ #languageLinks .selectedTab a { font-size: 19px; color: #000; padding: 6px; display: block; border-left: 1px solid #CCC; border-bottom: 1px solid #CCC; margin-bottom: -3px; background-color: #fff; margin-right: -6px; position: relative; border-top: 1px solid #CCC; white-space: nowrap; } #languageLinks .unselectedTab a { font-size: 16px; color: #929292; padding: 6px; display: block; border-left: 1px solid #CCC; border-top: 1px solid #CCC; border-bottom: 1px solid #CCC; margin-bottom: -3px; margin-left: 15px; margin-right: -5px; white-space: nowrap; background-color: #F6F6F6; } #languageLinks tr td { padding: 0px; } tr td.languageContainer { padding-left: 8px; padding-bottom: 10px; padding-right: 8px; border: 1px solid #CCC; vertical-align: top; width: 100%; } .unselectedtab { margin-left: 5px; } .editlangsection { margin-top: -13px; margin-right: 5px; border: 1px solid #CCC; margin-bottom: -2px; border-bottom-width: 0px; -webkit-border-top-left-radius: 5px; -webkit-border-top-right-radius: 5px; -moz-border-radius-topleft: 5px; -moz-border-radius-topright: 5px; border-top-left-radius: 5px; border-top-right-radius: 5px; color: white; line-height: 14px; font-size: 12px; padding: 0px 7px; text-align: center; clear: both; background-color: white; float: right; } #languageLinks a.addLanguageButton { font-size: 12px; padding: 3px 6px; } @media screen and (max-width:36em) { #tabstable>tbody>tr>td { display: block; float: left; width: 100%; } #languageLinks tr { display: block; float: left; margin-bottom: -3px; margin-left: -5px; margin-top: 5px; } #languageLinks .unselectedTab a { margin-left: 0; margin-right: 5px; border-right: 1px solid #CCC; } #languageLinks .selectedTab a { margin-left: 0; margin-right: 5px; border-right: 1px solid #CCC; border-bottom: 1px solid #FFF; } #tabstable { width: 100%; } #languageLinks a.addLanguageButton { margin-top: 2px; margin-bottom: 1px; } .editlangsection { border-bottom-width: 1px; } } /* Grease Pit edit button fix */ .page-Wiktionary_Grease_pit.skin-vector #ca-edit, .page-Wiktionary_Etymology_scriptorium.skin-vector #ca-edit, .page-Wiktionary_Information_desk.skin-vector #ca-edit, .page-Wiktionary_Beer_parlour.skin-vector #ca-edit, .page-Wiktionary_Tea_room.skin-vector #ca-edit{ display: none; } /* Romanization tables ---------- */ table.romanization { clear: both; background: white; white-space: nowrap; border-collapse: collapse; border: 1px solid white; vertical-align: baseline; text-align: center; } table.romanization th, table.romanization td { padding: 0.25em; border: 1px solid white; } table.romanization th { background: rgb(87%, 91%, 96%); } table.romanization tr.romanization-source td { background: rgba(87%, 91%, 96%, 0.5); } table.romanization td, table.romanization tr.romanization-target td { background: rgb(90%, 90%, 90%); } /* ----- end Romanization tables */ /* Definition dates, in {{defdate}} */ .defdate { font-size: smaller; } /* unit tests filter */ table.unit-tests.unit-tests-hide-passing tr.unit-test-pass { display: none; } table.unit-tests th.unit-tests-img-corner { background: #def; min-width: 1em; } table.unit-tests.unit-tests-hide-passing th.unit-tests-img-corner { background: red; } /* multi-column lists */ li, .group-in-column { column-break-inside: avoid; page-break-inside: avoid; break-inside: avoid; -webkit-column-break-inside: avoid; -webkit-page-break-inside: avoid; -webkit-break-inside: avoid; -o-column-break-inside: avoid; -o-page-break-inside: avoid; -o-break-inside: avoid; -moz-column-break-inside: avoid; -moz-page-break-inside: avoid; -moz-break-inside: avoid; } /* {{shortcut}} */ .shortcut-box, .category-edit-box { border: 1px solid #aaa; color: black; background: #f9f9f9; margin: 0 0 .5em 1em; text-align: center; padding: 5px; float: right; clear: both; font-weight: bold; font-size: smaller; } /* Display tabs with 4 spaces, see [[phab:T59824]] and [[phab:T59825]] */ textarea#wpTextbox1 + div:last-child, /* workaround for [[mw:User:Remember the dot/Syntax highlighter]] */ textarea, pre, code, .mw-highlight { -moz-tab-size: 4; -o-tab-size: 4; tab-size: 4; } /* stop double-bolding of self-links like in [[acquit]], [[fishbone]] */ /* old code: b .selflink, strong .selflink { font-weight: inherit; } */ b, strong { font-weight: bold; } /* archive templates */ .archive-box { border: 1px #AAAAAA solid; padding: 3px 10px; margin-bottom: 1em; color: black; background: #F9F9F9; } .archive-box.archive-passed { color: black; background: #F3F9FF; } .archive-box.archive-failed { color: black; background: #F9F3FF; } tr:target { background: #def; } .character-sample { display: inline-block; min-width: 1em; font-size: 300%; text-align: center; background: rgba(0, 0, 0, 0.1); } .character-sample > :only-child { display: block; } .character-sample-primary{ display: inline-table; min-width: 1.1em; font-size: 300%; line-height: 110%; text-align: center; background: rgba(0, 0, 0, 0.1); height: 1.1em; } .character-sample-secondary { display: inline-block; min-width: 1em; text-align: center; } table.symboltable td { font-family: 'DejaVu Sans', Symbola, Quivira, sans-serif; text-align: center; } #firstHeading var, .headword var { color: #888; } body[class*="page-Template_table"] .CategoryTreeLabel { font-style: inherit; } body[class*="page-Template_table"] .CategoryTreeLabel:before { content: "•"; padding-right: 0.75em; } /* Temporary kludge to be used because categoryTree is generating a newline. This CSS line should be removed after the developers fix it. */ .CategoryTreeTag + p br:first-child{display:none;} .vsShow { display: none; } /* Used by column templates (see [[Module:columns]]). */ .ul-column-count[data-column-count="2"] > ul, .ul-column-count[data-column-count="2"] > ol, .ul-column-count[data-column-count="2"] > dl { -moz-column-count: 2; -webkit-column-count: 2; -ms-column-count: 2; column-count: 2; } .ul-column-count[data-column-count="3"] > ul, .ul-column-count[data-column-count="3"] > ol, .ul-column-count[data-column-count="3"] > dl { -moz-column-count: 3; -webkit-column-count: 3; -ms-column-count: 3; column-count: 3; } .ul-column-count[data-column-count="4"] > ul, .ul-column-count[data-column-count="4"] > ol, .ul-column-count[data-column-count="4"] > dl { -moz-column-count: 4; -webkit-column-count: 4; -ms-column-count: 4; column-count: 4; } .ul-column-count[data-column-count="5"] > ul, .ul-column-count[data-column-count="5"] > ol, .ul-column-count[data-column-count="5"] > dl { -moz-column-count: 5; -webkit-column-count: 5; -ms-column-count: 5; column-count: 5; } .ul-column-count[data-column-count="6"] > ul, .ul-column-count[data-column-count="6"] > ol, .ul-column-count[data-column-count="6"] > dl { -moz-column-count: 6; -webkit-column-count: 6; -ms-column-count: 6; column-count: 6; } /* Default style for hypothetical inflections -- currently italic. */ .hypothetical { font-style: italic; } /* Style for entries using deprecated templates */ .deprecated { color: olivedrab; } .deprecated a { color: darkgreen; } /* == wrap toggle links with [ and ] == */ .HQToggle:before, .NavToggle:before, .nyms-toggle:before, .usex-toggle:before { content:'['; } .HQToggle:after, .NavToggle:after, .nyms-toggle:after, .usex-toggle:after { content:']'; } .HQToggle a, .NavToggle a, .nyms-toggle a, .usex-toggle a { user-select: none; } .client-js .NavFrame .NavContent { display: none; } .client-js .not .NavContent { /* ??? */ display: block; } /* Hidden quotes */ .client-js .ns-0 ol>li>ul { display: none; } /* WikiHiero kludge */ /* disable for now, apparently not necessary and breaks wrapping .wikt-hierokludge { display: inline-block; } */ /* Support lists that automatically split into limited-width columns (for translations tables and the like): */ .multicolumn-list > ul { -webkit-column-width: 25em; -moz-column-width: 25em; -ms-column-width: 25em; column-width: 25em; column-gap: 20px; } .multicolumn-list-wide > ul { -webkit-column-width: 55em; -moz-column-width: 55em; -ms-column-width: 55em; column-width: 55em; column-gap: 20px; } .multicolumn-list-narrow > ul { -webkit-column-width: 22em; -moz-column-width: 22em; -ms-column-width: 22em; column-width: 22em; column-gap: 20px; } /* Horizontal rules */ body.ns-0 .mw-parser-output > :not(.collapsible-block) + h2:not(:first-of-type) { border-top: 1px solid #a2a9b1; padding-top: 1em; margin-top: 0.2em; } body.ns-0 hr { display: none; } /* </nowiki> */ /* T156351: Support for Parsoid's Cite implementation */ a[ rel="mw:referencedBy" ]::before { content: " ^"; } 8zt87trwh1ve986rrq0zktxu1alxh47 میدیاویکی:Gadgets-definition 8 7833 36539 27863 2026-08-10T04:17:20Z Aram 9 /* not yet sorted */ گۆڕینی FloatNavToggleLeft > FloatNavToggleRight بەپێی [[لێدوانی بەکارھێنەر:-Haryad#داڕێژە:trans-top|ئێرە]] 36539 wikitext text/x-wiki بۆ بەڵگەنامەدارکردنی ئەم ئامرازانە، بڕوانە [[تایبەت:ئامرازەکان]]. بۆ زانیاریی گشتی لەسەر ئامرازەکانی ویکیفەرھەنگ، بڕوانە [[ویکیفەرھەنگ:ئامرازەکان]]. ھەروەھا بۆ ھەڵبژاردەکانی تر، بڕوانە [[وف:ھەڵبژاردەکان]]. == not yet sorted == * FloatNavToggleRight[ResourceLoader]|FloatNavToggleRight.css == editing-gadgets == * Cat-a-lot[ResourceLoader|rights=autoconfirmed|dependencies=mediawiki.util]|Cat-a-lot.css|Cat-a-lot.js * charinsert [ResourceLoader |default |actions=edit] |charinsert.js * charinsert-core [ResourceLoader |hidden |dependencies=jquery.textSelection, user, mediawiki.storage] |charinsert-styles.css |charinsert-core.js * FastRevert[ResourceLoader|dependencies=mediawiki.util]|FastRevert.js * DotsSyntaxHighlighter[ResourceLoader]|DotsSyntaxHighlighter.js * TranslationAdder[ResourceLoader|rights=delete|dependencies=ext.gadget.Editor,ext.gadget.LegacyScriptsNewNode,mediawiki.cookie,ext.gadget.LanguageUtils,mediawiki.util,ext.gadget.TranslationAdder-Data]|TranslationAdder.js == appearance == * dark-mode-toggle [ResourceLoader |dependencies=mediawiki.util, mediawiki.api, mediawiki.Uri, mediawiki.storage |peers=dark-mode-toggle-pagestyles] |dark-mode-toggle.js * dark-mode-toggle-pagestyles [hidden |skins=vector, vector-2022, minerva, monobook] |dark-mode-toggle-pagestyles.css * dark-mode [ResourceLoader |supportsUrlLoad |skins=vector, vector-2022, monobook, modern, minerva, timeless] |dark-mode.css * MoreMenu-local[ResourceLoader|dependencies=mediawiki.api,mediawiki.util,user.options]|MoreMenu-local.js * UTCLiveClock[ResourceLoader|supportsUrlLoad|type=general|dependencies=mediawiki.util,mediawiki.api|peers=UTCLiveClock-pagestyles]|UTCLiveClock.js|UTCLiveClock.css * UTCLiveClock-pagestyles[hidden|skins=vector,vector-2022,monobook]|UTCLiveClock-pagestyles.css == Hidden gadgets and utils == * Editor[ResourceLoader|hidden|dependencies=mediawiki.util,mediawiki.api]|Editor.js * LegacyScriptsNewNode[ResourceLoader|hidden|targets=desktop,mobile]|LegacyScriptsNewNode.js * LanguageUtils[ResourceLoader|hidden|dependencies=mediawiki.api,ext.gadget.StorageUtils]|LanguageUtils.js * StorageUtils[ResourceLoader|hidden|dependencies=mediawiki.cookie,mediawiki.storage]|StorageUtils.js * TranslationAdder-Data[ResourceLoader|hidden|dependencies=mediawiki.api,mediawiki.Uri,ext.gadget.LanguageUtils]|TranslationAdder-Data.js * VisibilityToggles[ResourceLoader|hidden|targets=desktop,mobile|dependencies=user,mediawiki.cookie]|VisibilityToggles.js * defaultVisibilityToggles[ResourceLoader|hidden|default|targets=desktop,mobile|dependencies=ext.gadget.VisibilityToggles]|defaultVisibilityToggles.js 2x7k44izka7y1tdkr5tf5uc087vljso میدیاویکی:Gadget-FloatNavToggleRight.css 8 7836 36536 18672 2026-08-10T04:15:14Z Aram 9 Aram پەڕەی [[میدیاویکی:Gadget-FloatNavToggleLeft.css]]ی بە بێ بەجێھشتنی ڕەوانەکەرێک گواستەوە بۆ [[میدیاویکی:Gadget-FloatNavToggleRight.css]]: بەپێی [[لێدوانی بەکارھێنەر:-Haryad#داڕێژە:trans-top|ئێرە]] 18671 css text/css .NavToggle { float: left !important; position: static !important; right: inherit; margin-top: 0.1em; margin-right: 5px;} 44fqdjn1rkiuvzu17zr2ynv4ov2ca0s 36537 36536 2026-08-10T04:16:09Z Aram 9 پێچەوانەکردنەوەی ڕاست و چەپ؛ بەپێی [[لێدوانی بەکارھێنەر:-Haryad#داڕێژە:trans-top|ئێرە]] 36537 css text/css .NavToggle { float: right !important; position: static !important; left: inherit; margin-top: 0.1em; margin-left: 5px;} og5rqh5xo4fuz9d5ftpg5e0ji7ov9tu میدیاویکی:Gadget-FloatNavToggleRight 8 7838 36535 18677 2026-08-10T04:14:47Z Aram 9 Aram پەڕەی [[میدیاویکی:Gadget-FloatNavToggleLeft]]ی بە بێ بەجێھشتنی ڕەوانەکەرێک گواستەوە بۆ [[میدیاویکی:Gadget-FloatNavToggleRight]]: بەپێی [[لێدوانی بەکارھێنەر:-Haryad#داڕێژە:trans-top|ئێرە]] 18677 wikitext text/x-wiki بەستەرەکانی پێڕستەکان (بۆ نموونە، [▼ پیشان بدە] و [▲ بشارەوە] لە وەرگێرانەکان) ببە لای چەپ migpgf3z033wkmdnc257n5eatar1eo5 36538 36535 2026-08-10T04:16:39Z Aram 9 چەپ > ڕاست؛ بەپێی [[لێدوانی بەکارھێنەر:-Haryad#داڕێژە:trans-top|ئێرە]] 36538 wikitext text/x-wiki بەستەرەکانی پێڕستەکان (بۆ نموونە، [▼ پیشان بدە] و [▲ بشارەوە] لە وەرگێرانەکان) ببە لای ڕاست o54wxeezuvkpgcbm96spw62n5ie682l مۆدیوول:kmr-headword 828 9176 36506 36470 2026-08-09T17:25:27Z Ghybu 12 پووچەڵکردنەوەی پێداچوونەوەی [[Special:Diff/36470|36470]] لە لایەن [[Special:Contributions/Ghybu|Ghybu]] ([[User talk:Ghybu|لێدوان]]) 36506 Scribunto text/plain local export = {} local pos_functions = {} local m_scripts = require("Module:scripts") local lang = require("Module:languages").getByCode("kmr") local langname = lang:getCanonicalName() local ku_sc = m_scripts.getByCode("ku-Arab") local PAGENAME = mw.title.getCurrentTitle().text 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 = true}, ["tr"] = {list = true, allow_holes = true}, ["id"] = {}, ["ar"] = {list = true}, ["suff"] = {type = "boolean"}, ["c"] = {type = "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["verbs"] = { 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["adjectives"] = { params = { [1] = {alias_of = "comparative"}, ["comparative"] = {list = true}, --comparative(s) [2] = {alias_of = "superlative"}, ["superlative"] = {list = true}, --superlative(s) ["c"] = {type = "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 = PAGENAME ..'tirîn'}}) end end end end } local noun_params = { [1] = {alias_of = "g"}, ["g"] = {list = true}, --gender(s) [2] = {alias_of = "pl"}, ["pl"] = {list = true}, --plural(s) ["f"] = {list = true}, --feminine form(s) ["m"] = {list = true}, --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("Module:string utilities").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["nouns"] = { 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 9iwna87tgf9zo7a53tcrkrf14nga4ib 36507 36506 2026-08-09T17:26:02Z Ghybu 12 پووچەڵکردنەوەی پێداچوونەوەی [[Special:Diff/36506|36506]] لە لایەن [[Special:Contributions/Ghybu|Ghybu]] ([[User talk:Ghybu|لێدوان]]) 36507 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["verbs"] = { 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["adjectives"] = { 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["nouns"] = { 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 ih8b3wxbld7q4usbnr7uchim30ohta3 مۆدیوول:headword/page 828 9407 36501 34462 2026-08-09T17:18:34Z Ghybu 12 36501 Scribunto text/plain local export = {} local languages_module = "Module:languages" local maintenance_category_module = "Module:maintenance category" local pages_module = "Module:pages" local string_compare_module = "Module:string/compare" local string_decode_entities_module = "Module:string/decodeEntities" local string_remove_comments_module = "Module:string/removeComments" local string_utilities_module = "Module:string utilities" local table_module = "Module:table" local template_parser_module = "Module:template parser" local mw = mw local string = string local table = table local ustring = mw.ustring local concat = table.concat local find = string.find local format = string.format local gsub = string.gsub local insert = table.insert local load_data = mw.loadData local match = string.match local new_title = mw.title.new local pairs = pairs local require = require local sub = string.sub local toNFC = ustring.toNFC local toNFD = ustring.toNFD local ugsub = ustring.gsub local function class_else_type(...) class_else_type = require(template_parser_module).class_else_type return class_else_type(...) end local function decode_entities(...) decode_entities = require(string_decode_entities_module) return decode_entities(...) end local function encode_entities(...) encode_entities = require(string_utilities_module).encode_entities return encode_entities(...) end local function get_category(...) get_category = require(maintenance_category_module).get_category return get_category(...) end local function get_lang(...) get_lang = require(languages_module).getByCode return get_lang(...) end local function list_to_set(...) list_to_set = require(table_module).listToSet return list_to_set(...) end local function parse(...) parse = require(template_parser_module).parse return parse(...) end local function remove_comments(...) remove_comments = require(string_remove_comments_module) return remove_comments(...) end local function physical_to_logical_pagename_if_mammoth(...) physical_to_logical_pagename_if_mammoth = require(pages_module).physical_to_logical_pagename_if_mammoth return physical_to_logical_pagename_if_mammoth(...) end local function split(...) split = require(string_utilities_module).split return split(...) end local function string_compare(...) string_compare = require(string_compare_module) return string_compare(...) end local function uupper(...) uupper = require(string_utilities_module).upper return uupper(...) 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 langnames local function get_langnames() langnames, get_langnames = load_data("Module:languages/canonical names"), nil return langnames end -- Combining character data used when categorising unusual characters. These resolve into two patterns, used to find -- single combining characters (i.e. character + diacritic(s)) or double combining characters (i.e. character + -- diacritic(s) + character). -- Charsets are in the format used by Unicode's UnicodeSet tool: https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp. -- Single combining characters. -- Charset: [[:M:]&[:^Canonical_Combining_Class=/^Double_/:]&[:^subhead=Grapheme joiner:]&[:^Variation_Selector=Yes:]] -- Note: concatenating hundreds of lines at once gives an error, so () are used every 150 lines to break it up into chunks. local comb_chars_single = ("\204\128-\205\142" .. -- U+0300-U+034E "\205\144-\205\155" .. -- U+0350-U+035B "\205\163-\205\175" .. -- U+0363-U+036F "\210\131-\210\137" .. -- U+0483-U+0489 "\214\145-\214\189" .. -- U+0591-U+05BD "\214\191" .. -- U+05BF "\215\129" .. -- U+05C1 "\215\130" .. -- U+05C2 "\215\132" .. -- U+05C4 "\215\133" .. -- U+05C5 "\215\135" .. -- U+05C7 "\216\144-\216\154" .. -- U+0610-U+061A "\217\139-\217\159" .. -- U+064B-U+065F "\217\176" .. -- U+0670 "\219\150-\219\156" .. -- U+06D6-U+06DC "\219\159-\219\164" .. -- U+06DF-U+06E4 "\219\167" .. -- U+06E7 "\219\168" .. -- U+06E8 "\219\170-\219\173" .. -- U+06EA-U+06ED "\220\145" .. -- U+0711 "\220\176-\221\138" .. -- U+0730-U+074A "\222\166-\222\176" .. -- U+07A6-U+07B0 "\223\171-\223\179" .. -- U+07EB-U+07F3 "\223\189" .. -- U+07FD "\224\160\150-\224\160\153" .. -- U+0816-U+0819 "\224\160\155-\224\160\163" .. -- U+081B-U+0823 "\224\160\165-\224\160\167" .. -- U+0825-U+0827 "\224\160\169-\224\160\173" .. -- U+0829-U+082D "\224\161\153-\224\161\155" .. -- U+0859-U+085B "\224\162\151-\224\162\159" .. -- U+0897-U+089F "\224\163\138-\224\163\161" .. -- U+08CA-U+08E1 "\224\163\163-\224\164\131" .. -- U+08E3-U+0903 "\224\164\186-\224\164\188" .. -- U+093A-U+093C "\224\164\190-\224\165\143" .. -- U+093E-U+094F "\224\165\145-\224\165\151" .. -- U+0951-U+0957 "\224\165\162" .. -- U+0962 "\224\165\163" .. -- U+0963 "\224\166\129-\224\166\131" .. -- U+0981-U+0983 "\224\166\188" .. -- U+09BC "\224\166\190-\224\167\132" .. -- U+09BE-U+09C4 "\224\167\135" .. -- U+09C7 "\224\167\136" .. -- U+09C8 "\224\167\139-\224\167\141" .. -- U+09CB-U+09CD "\224\167\151" .. -- U+09D7 "\224\167\162" .. -- U+09E2 "\224\167\163" .. -- U+09E3 "\224\167\190" .. -- U+09FE "\224\168\129-\224\168\131" .. -- U+0A01-U+0A03 "\224\168\188" .. -- U+0A3C "\224\168\190-\224\169\130" .. -- U+0A3E-U+0A42 "\224\169\135" .. -- U+0A47 "\224\169\136" .. -- U+0A48 "\224\169\139-\224\169\141" .. -- U+0A4B-U+0A4D "\224\169\145" .. -- U+0A51 "\224\169\176" .. -- U+0A70 "\224\169\177" .. -- U+0A71 "\224\169\181" .. -- U+0A75 "\224\170\129-\224\170\131" .. -- U+0A81-U+0A83 "\224\170\188" .. -- U+0ABC "\224\170\190-\224\171\133" .. -- U+0ABE-U+0AC5 "\224\171\135-\224\171\137" .. -- U+0AC7-U+0AC9 "\224\171\139-\224\171\141" .. -- U+0ACB-U+0ACD "\224\171\162" .. -- U+0AE2 "\224\171\163" .. -- U+0AE3 "\224\171\186-\224\171\191" .. -- U+0AFA-U+0AFF "\224\172\129-\224\172\131" .. -- U+0B01-U+0B03 "\224\172\188" .. -- U+0B3C "\224\172\190-\224\173\132" .. -- U+0B3E-U+0B44 "\224\173\135" .. -- U+0B47 "\224\173\136" .. -- U+0B48 "\224\173\139-\224\173\141" .. -- U+0B4B-U+0B4D "\224\173\149-\224\173\151" .. -- U+0B55-U+0B57 "\224\173\162" .. -- U+0B62 "\224\173\163" .. -- U+0B63 "\224\174\130" .. -- U+0B82 "\224\174\190-\224\175\130" .. -- U+0BBE-U+0BC2 "\224\175\134-\224\175\136" .. -- U+0BC6-U+0BC8 "\224\175\138-\224\175\141" .. -- U+0BCA-U+0BCD "\224\175\151" .. -- U+0BD7 "\224\176\128-\224\176\132" .. -- U+0C00-U+0C04 "\224\176\188" .. -- U+0C3C "\224\176\190-\224\177\132" .. -- U+0C3E-U+0C44 "\224\177\134-\224\177\136" .. -- U+0C46-U+0C48 "\224\177\138-\224\177\141" .. -- U+0C4A-U+0C4D "\224\177\149" .. -- U+0C55 "\224\177\150" .. -- U+0C56 "\224\177\162" .. -- U+0C62 "\224\177\163" .. -- U+0C63 "\224\178\129-\224\178\131" .. -- U+0C81-U+0C83 "\224\178\188" .. -- U+0CBC "\224\178\190-\224\179\132" .. -- U+0CBE-U+0CC4 "\224\179\134-\224\179\136" .. -- U+0CC6-U+0CC8 "\224\179\138-\224\179\141" .. -- U+0CCA-U+0CCD "\224\179\149" .. -- U+0CD5 "\224\179\150" .. -- U+0CD6 "\224\179\162" .. -- U+0CE2 "\224\179\163" .. -- U+0CE3 "\224\179\179" .. -- U+0CF3 "\224\180\128-\224\180\131" .. -- U+0D00-U+0D03 "\224\180\187" .. -- U+0D3B "\224\180\188" .. -- U+0D3C "\224\180\190-\224\181\132" .. -- U+0D3E-U+0D44 "\224\181\134-\224\181\136" .. -- U+0D46-U+0D48 "\224\181\138-\224\181\141" .. -- U+0D4A-U+0D4D "\224\181\151" .. -- U+0D57 "\224\181\162" .. -- U+0D62 "\224\181\163" .. -- U+0D63 "\224\182\129-\224\182\131" .. -- U+0D81-U+0D83 "\224\183\138" .. -- U+0DCA "\224\183\143-\224\183\148" .. -- U+0DCF-U+0DD4 "\224\183\150" .. -- U+0DD6 "\224\183\152-\224\183\159" .. -- U+0DD8-U+0DDF "\224\183\178" .. -- U+0DF2 "\224\183\179" .. -- U+0DF3 "\224\184\177" .. -- U+0E31 "\224\184\180-\224\184\186" .. -- U+0E34-U+0E3A "\224\185\135-\224\185\142" .. -- U+0E47-U+0E4E "\224\186\177" .. -- U+0EB1 "\224\186\180-\224\186\188" .. -- U+0EB4-U+0EBC "\224\187\136-\224\187\142" .. -- U+0EC8-U+0ECE "\224\188\152" .. -- U+0F18 "\224\188\153" .. -- U+0F19 "\224\188\181" .. -- U+0F35 "\224\188\183" .. -- U+0F37 "\224\188\185" .. -- U+0F39 "\224\188\190" .. -- U+0F3E "\224\188\191" .. -- U+0F3F "\224\189\177-\224\190\132" .. -- U+0F71-U+0F84 "\224\190\134" .. -- U+0F86 "\224\190\135" .. -- U+0F87 "\224\190\141-\224\190\151" .. -- U+0F8D-U+0F97 "\224\190\153-\224\190\188" .. -- U+0F99-U+0FBC "\224\191\134" .. -- U+0FC6 "\225\128\171-\225\128\190" .. -- U+102B-U+103E "\225\129\150-\225\129\153" .. -- U+1056-U+1059 "\225\129\158-\225\129\160" .. -- U+105E-U+1060 "\225\129\162-\225\129\164" .. -- U+1062-U+1064 "\225\129\167-\225\129\173" .. -- U+1067-U+106D "\225\129\177-\225\129\180" .. -- U+1071-U+1074 "\225\130\130-\225\130\141" .. -- U+1082-U+108D "\225\130\143" .. -- U+108F "\225\130\154-\225\130\157" .. -- U+109A-U+109D "\225\141\157-\225\141\159" .. -- U+135D-U+135F "\225\156\146-\225\156\149" .. -- U+1712-U+1715 "\225\156\178-\225\156\180" .. -- U+1732-U+1734 "\225\157\146" .. -- U+1752 "\225\157\147" .. -- U+1753 "\225\157\178" .. -- U+1772 "\225\157\179" .. -- U+1773 "\225\158\180-\225\159\147") .. -- U+17B4-U+17D3 ("\225\159\157" .. -- U+17DD "\225\162\133" .. -- U+1885 "\225\162\134" .. -- U+1886 "\225\162\169" .. -- U+18A9 "\225\164\160-\225\164\171" .. -- U+1920-U+192B "\225\164\176-\225\164\187" .. -- U+1930-U+193B "\225\168\151-\225\168\155" .. -- U+1A17-U+1A1B "\225\169\149-\225\169\158" .. -- U+1A55-U+1A5E "\225\169\160-\225\169\188" .. -- U+1A60-U+1A7C "\225\169\191" .. -- U+1A7F "\225\170\176-\225\171\142" .. -- U+1AB0-U+1ACE "\225\172\128-\225\172\132" .. -- U+1B00-U+1B04 "\225\172\180-\225\173\132" .. -- U+1B34-U+1B44 "\225\173\171-\225\173\179" .. -- U+1B6B-U+1B73 "\225\174\128-\225\174\130" .. -- U+1B80-U+1B82 "\225\174\161-\225\174\173" .. -- U+1BA1-U+1BAD "\225\175\166-\225\175\179" .. -- U+1BE6-U+1BF3 "\225\176\164-\225\176\183" .. -- U+1C24-U+1C37 "\225\179\144-\225\179\146" .. -- U+1CD0-U+1CD2 "\225\179\148-\225\179\168" .. -- U+1CD4-U+1CE8 "\225\179\173" .. -- U+1CED "\225\179\180" .. -- U+1CF4 "\225\179\183-\225\179\185" .. -- U+1CF7-U+1CF9 "\225\183\128-\225\183\140" .. -- U+1DC0-U+1DCC "\225\183\142-\225\183\187" .. -- U+1DCE-U+1DFB "\225\183\189-\225\183\191" .. -- U+1DFD-U+1DFF "\226\131\144-\226\131\176" .. -- U+20D0-U+20F0 "\226\179\175-\226\179\177" .. -- U+2CEF-U+2CF1 "\226\181\191" .. -- U+2D7F "\226\183\160-\226\183\191" .. -- U+2DE0-U+2DFF "\227\128\170-\227\128\175" .. -- U+302A-U+302F "\227\130\153" .. -- U+3099 "\227\130\154" .. -- U+309A "\234\153\175-\234\153\178" .. -- U+A66F-U+A672 "\234\153\180-\234\153\189" .. -- U+A674-U+A67D "\234\154\158" .. -- U+A69E "\234\154\159" .. -- U+A69F "\234\155\176" .. -- U+A6F0 "\234\155\177" .. -- U+A6F1 "\234\160\130" .. -- U+A802 "\234\160\134" .. -- U+A806 "\234\160\139" .. -- U+A80B "\234\160\163-\234\160\167" .. -- U+A823-U+A827 "\234\160\172" .. -- U+A82C "\234\162\128" .. -- U+A880 "\234\162\129" .. -- U+A881 "\234\162\180-\234\163\133" .. -- U+A8B4-U+A8C5 "\234\163\160-\234\163\177" .. -- U+A8E0-U+A8F1 "\234\163\191" .. -- U+A8FF "\234\164\166-\234\164\173" .. -- U+A926-U+A92D "\234\165\135-\234\165\147" .. -- U+A947-U+A953 "\234\166\128-\234\166\131" .. -- U+A980-U+A983 "\234\166\179-\234\167\128" .. -- U+A9B3-U+A9C0 "\234\167\165" .. -- U+A9E5 "\234\168\169-\234\168\182" .. -- U+AA29-U+AA36 "\234\169\131" .. -- U+AA43 "\234\169\140" .. -- U+AA4C "\234\169\141" .. -- U+AA4D "\234\169\187-\234\169\189" .. -- U+AA7B-U+AA7D "\234\170\176" .. -- U+AAB0 "\234\170\178-\234\170\180" .. -- U+AAB2-U+AAB4 "\234\170\183" .. -- U+AAB7 "\234\170\184" .. -- U+AAB8 "\234\170\190" .. -- U+AABE "\234\170\191" .. -- U+AABF "\234\171\129" .. -- U+AAC1 "\234\171\171-\234\171\175" .. -- U+AAEB-U+AAEF "\234\171\181" .. -- U+AAF5 "\234\171\182" .. -- U+AAF6 "\234\175\163-\234\175\170" .. -- U+ABE3-U+ABEA "\234\175\172" .. -- U+ABEC "\234\175\173" .. -- U+ABED "\239\172\158" .. -- U+FB1E "\239\184\160-\239\184\175" .. -- U+FE20-U+FE2F "\240\144\135\189" .. -- U+101FD "\240\144\139\160" .. -- U+102E0 "\240\144\141\182-\240\144\141\186" .. -- U+10376-U+1037A "\240\144\168\129-\240\144\168\131" .. -- U+10A01-U+10A03 "\240\144\168\133" .. -- U+10A05 "\240\144\168\134" .. -- U+10A06 "\240\144\168\140-\240\144\168\143" .. -- U+10A0C-U+10A0F "\240\144\168\184-\240\144\168\186" .. -- U+10A38-U+10A3A "\240\144\168\191" .. -- U+10A3F "\240\144\171\165" .. -- U+10AE5 "\240\144\171\166" .. -- U+10AE6 "\240\144\180\164-\240\144\180\167" .. -- U+10D24-U+10D27 "\240\144\181\169-\240\144\181\173" .. -- U+10D69-U+10D6D "\240\144\186\171" .. -- U+10EAB "\240\144\186\172" .. -- U+10EAC "\240\144\187\188-\240\144\187\191" .. -- U+10EFC-U+10EFF "\240\144\189\134-\240\144\189\144" .. -- U+10F46-U+10F50 "\240\144\190\130-\240\144\190\133" .. -- U+10F82-U+10F85 "\240\145\128\128-\240\145\128\130" .. -- U+11000-U+11002 "\240\145\128\184-\240\145\129\134" .. -- U+11038-U+11046 "\240\145\129\176" .. -- U+11070 "\240\145\129\179" .. -- U+11073 "\240\145\129\180" .. -- U+11074 "\240\145\129\191-\240\145\130\130" .. -- U+1107F-U+11082 "\240\145\130\176-\240\145\130\186" .. -- U+110B0-U+110BA "\240\145\131\130" .. -- U+110C2 "\240\145\132\128-\240\145\132\130" .. -- U+11100-U+11102 "\240\145\132\167-\240\145\132\180" .. -- U+11127-U+11134 "\240\145\133\133" .. -- U+11145 "\240\145\133\134" .. -- U+11146 "\240\145\133\179" .. -- U+11173 "\240\145\134\128-\240\145\134\130" .. -- U+11180-U+11182 "\240\145\134\179-\240\145\135\128" .. -- U+111B3-U+111C0 "\240\145\135\137-\240\145\135\140" .. -- U+111C9-U+111CC "\240\145\135\142" .. -- U+111CE "\240\145\135\143" .. -- U+111CF "\240\145\136\172-\240\145\136\183" .. -- U+1122C-U+11237 "\240\145\136\190" .. -- U+1123E "\240\145\137\129" .. -- U+11241 "\240\145\139\159-\240\145\139\170" .. -- U+112DF-U+112EA "\240\145\140\128-\240\145\140\131" .. -- U+11300-U+11303 "\240\145\140\187" .. -- U+1133B "\240\145\140\188" .. -- U+1133C "\240\145\140\190-\240\145\141\132" .. -- U+1133E-U+11344 "\240\145\141\135" .. -- U+11347 "\240\145\141\136" .. -- U+11348 "\240\145\141\139-\240\145\141\141" .. -- U+1134B-U+1134D "\240\145\141\151" .. -- U+11357 "\240\145\141\162" .. -- U+11362 "\240\145\141\163" .. -- U+11363 "\240\145\141\166-\240\145\141\172" .. -- U+11366-U+1136C "\240\145\141\176-\240\145\141\180" .. -- U+11370-U+11374 "\240\145\142\184-\240\145\143\128" .. -- U+113B8-U+113C0 "\240\145\143\130" .. -- U+113C2 "\240\145\143\133" .. -- U+113C5 "\240\145\143\135-\240\145\143\138" .. -- U+113C7-U+113CA "\240\145\143\140-\240\145\143\144" .. -- U+113CC-U+113D0 "\240\145\143\146" .. -- U+113D2 "\240\145\143\161" .. -- U+113E1 "\240\145\143\162" .. -- U+113E2 "\240\145\144\181-\240\145\145\134" .. -- U+11435-U+11446 "\240\145\145\158" .. -- U+1145E "\240\145\146\176-\240\145\147\131" .. -- U+114B0-U+114C3 "\240\145\150\175-\240\145\150\181" .. -- U+115AF-U+115B5 "\240\145\150\184-\240\145\151\128" .. -- U+115B8-U+115C0 "\240\145\151\156" .. -- U+115DC "\240\145\151\157" .. -- U+115DD "\240\145\152\176-\240\145\153\128" .. -- U+11630-U+11640 "\240\145\154\171-\240\145\154\183" .. -- U+116AB-U+116B7 "\240\145\156\157-\240\145\156\171" .. -- U+1171D-U+1172B "\240\145\160\172-\240\145\160\186" .. -- U+1182C-U+1183A "\240\145\164\176-\240\145\164\181" .. -- U+11930-U+11935 "\240\145\164\183" .. -- U+11937 "\240\145\164\184" .. -- U+11938 "\240\145\164\187-\240\145\164\190" .. -- U+1193B-U+1193E "\240\145\165\128") .. -- U+11940 ("\240\145\165\130" .. -- U+11942 "\240\145\165\131" .. -- U+11943 "\240\145\167\145-\240\145\167\151" .. -- U+119D1-U+119D7 "\240\145\167\154-\240\145\167\160" .. -- U+119DA-U+119E0 "\240\145\167\164" .. -- U+119E4 "\240\145\168\129-\240\145\168\138" .. -- U+11A01-U+11A0A "\240\145\168\179-\240\145\168\185" .. -- U+11A33-U+11A39 "\240\145\168\187-\240\145\168\190" .. -- U+11A3B-U+11A3E "\240\145\169\135" .. -- U+11A47 "\240\145\169\145-\240\145\169\155" .. -- U+11A51-U+11A5B "\240\145\170\138-\240\145\170\153" .. -- U+11A8A-U+11A99 "\240\145\176\175-\240\145\176\182" .. -- U+11C2F-U+11C36 "\240\145\176\184-\240\145\176\191" .. -- U+11C38-U+11C3F "\240\145\178\146-\240\145\178\167" .. -- U+11C92-U+11CA7 "\240\145\178\169-\240\145\178\182" .. -- U+11CA9-U+11CB6 "\240\145\180\177-\240\145\180\182" .. -- U+11D31-U+11D36 "\240\145\180\186" .. -- U+11D3A "\240\145\180\188" .. -- U+11D3C "\240\145\180\189" .. -- U+11D3D "\240\145\180\191-\240\145\181\133" .. -- U+11D3F-U+11D45 "\240\145\181\135" .. -- U+11D47 "\240\145\182\138-\240\145\182\142" .. -- U+11D8A-U+11D8E "\240\145\182\144" .. -- U+11D90 "\240\145\182\145" .. -- U+11D91 "\240\145\182\147-\240\145\182\151" .. -- U+11D93-U+11D97 "\240\145\187\179-\240\145\187\182" .. -- U+11EF3-U+11EF6 "\240\145\188\128" .. -- U+11F00 "\240\145\188\129" .. -- U+11F01 "\240\145\188\131" .. -- U+11F03 "\240\145\188\180-\240\145\188\186" .. -- U+11F34-U+11F3A "\240\145\188\190-\240\145\189\130" .. -- U+11F3E-U+11F42 "\240\145\189\154" .. -- U+11F5A "\240\147\145\128" .. -- U+13440 "\240\147\145\135-\240\147\145\149" .. -- U+13447-U+13455 "\240\150\132\158-\240\150\132\175" .. -- U+1611E-U+1612F "\240\150\171\176-\240\150\171\180" .. -- U+16AF0-U+16AF4 "\240\150\172\176-\240\150\172\182" .. -- U+16B30-U+16B36 "\240\150\189\143" .. -- U+16F4F "\240\150\189\145-\240\150\190\135" .. -- U+16F51-U+16F87 "\240\150\190\143-\240\150\190\146" .. -- U+16F8F-U+16F92 "\240\150\191\164" .. -- U+16FE4 "\240\150\191\176" .. -- U+16FF0 "\240\150\191\177" .. -- U+16FF1 "\240\155\178\157" .. -- U+1BC9D "\240\155\178\158" .. -- U+1BC9E "\240\156\188\128-\240\156\188\173" .. -- U+1CF00-U+1CF2D "\240\156\188\176-\240\156\189\134" .. -- U+1CF30-U+1CF46 "\240\157\133\165-\240\157\133\169" .. -- U+1D165-U+1D169 "\240\157\133\173-\240\157\133\178" .. -- U+1D16D-U+1D172 "\240\157\133\187-\240\157\134\130" .. -- U+1D17B-U+1D182 "\240\157\134\133-\240\157\134\139" .. -- U+1D185-U+1D18B "\240\157\134\170-\240\157\134\173" .. -- U+1D1AA-U+1D1AD "\240\157\137\130-\240\157\137\132" .. -- U+1D242-U+1D244 "\240\157\168\128-\240\157\168\182" .. -- U+1DA00-U+1DA36 "\240\157\168\187-\240\157\169\172" .. -- U+1DA3B-U+1DA6C "\240\157\169\181" .. -- U+1DA75 "\240\157\170\132" .. -- U+1DA84 "\240\157\170\155-\240\157\170\159" .. -- U+1DA9B-U+1DA9F "\240\157\170\161-\240\157\170\175" .. -- U+1DAA1-U+1DAAF "\240\158\128\128-\240\158\128\134" .. -- U+1E000-U+1E006 "\240\158\128\136-\240\158\128\152" .. -- U+1E008-U+1E018 "\240\158\128\155-\240\158\128\161" .. -- U+1E01B-U+1E021 "\240\158\128\163" .. -- U+1E023 "\240\158\128\164" .. -- U+1E024 "\240\158\128\166-\240\158\128\170" .. -- U+1E026-U+1E02A "\240\158\130\143" .. -- U+1E08F "\240\158\132\176-\240\158\132\182" .. -- U+1E130-U+1E136 "\240\158\138\174" .. -- U+1E2AE "\240\158\139\172-\240\158\139\175" .. -- U+1E2EC-U+1E2EF "\240\158\147\172-\240\158\147\175" .. -- U+1E4EC-U+1E4EF "\240\158\151\174" .. -- U+1E5EE "\240\158\151\175" .. -- U+1E5EF "\240\158\163\144-\240\158\163\150" .. -- U+1E8D0-U+1E8D6 "\240\158\165\132-\240\158\165\138") -- U+1E944-U+1E94A -- Double combining characters. -- Charset: [[:M:]&[:Canonical_Combining_Class=/^Double_/:]&[:^subhead=Grapheme joiner:]&[:^Variation_Selector=Yes:]] local comb_chars_double = "\205\156-\205\162" .. -- U+035C-U+0362 "\225\183\141" .. -- U+1DCD "\225\183\188" -- U+1DFC -- Variation selectors etc.; separated out so that we don't get categories for them. -- Charset: [[:M:]&[[:subhead=Grapheme joiner:][:Variation_Selector=Yes:]]]. local comb_chars_other = "\205\143" .. -- U+034F "\225\160\139-\225\160\141" .. -- U+180B-U+180D "\225\160\143" .. -- U+180F "\239\184\128-\239\184\143" .. -- U+FE00-U+FE0F "\243\160\132\128-\243\160\135\175" -- U+E0100-U+E01EF local comb_chars_all = comb_chars_single .. comb_chars_double .. comb_chars_other local comb_chars = { combined_single = "[^" .. comb_chars_all .. "][" .. comb_chars_single .. comb_chars_other .. "]+%f[^" .. comb_chars_all .. "]", combined_double = "[^" .. comb_chars_all .. "][" .. comb_chars_single .. comb_chars_other .. "]*[" .. comb_chars_double .. "]+[" .. comb_chars_all .. "]*.[" .. comb_chars_single .. comb_chars_other .. "]*", diacritics_single = "[" .. comb_chars_single .. "]", diacritics_double = "[" .. comb_chars_double .. "]", diacritics_all = "[" .. comb_chars_all .. "]" } -- Somewhat curated list from https://unicode.org/Public/emoji/16.0/emoji-sequences.txt. -- NOTE: There are lots more emoji sequences involving non-emoji Plane 0 symbols followed by 0xFE0F, which we don't -- (yet?) handle. local emoji_chars = "\226\140\154" .. -- U+231A (⌚) "\226\140\155" .. -- U+231B (⌛) "\226\140\168" .. -- U+2328 (⌨) "\226\143\143" .. -- U+23CF (⏏) "\226\143\169-\226\143\179" .. -- U+23E9-U+23F3 (⏩-⏳) "\226\143\184-\226\143\186" .. -- U+23F8-U+23FA (⏸-⏺) "\226\150\170" .. -- U+25AA (▪) "\226\150\171" .. -- U+25AB (▫) "\226\150\182" .. -- U+25B6 (▶) "\226\151\128" .. -- U+25C0 (◀) "\226\151\187-\226\151\190" .. -- U+25FB-U+25FE (◻-◾) "\226\152\128-\226\152\132" .. -- U+2600-U+2604 (☀-☄) "\226\152\142" .. -- U+260E (☎) "\226\152\145" .. -- U+2611 (☑) "\226\152\148" .. -- U+2614 (☔) "\226\152\149" .. -- U+2615 (☕) "\226\152\152" .. -- U+2618 (☘) "\226\152\157" .. -- U+261D (☝) "\226\152\160" .. -- U+2620 (☠) "\226\152\162" .. -- U+2622 (☢) "\226\152\163" .. -- U+2623 (☣) "\226\152\166" .. -- U+2626 (☦) "\226\152\170" .. -- U+262A (☪) "\226\152\174" .. -- U+262E (☮) "\226\152\175" .. -- U+262F (☯) "\226\152\184-\226\152\186" .. -- U+2638-U+263A (☸-☺) "\226\153\136-\226\153\147" .. -- U+2648-U+2653 (♈-♓) "\226\153\159" .. -- U+265F (♟) "\226\153\160" .. -- U+2660 (♠) "\226\153\163" .. -- U+2663 (♣) "\226\153\165" .. -- U+2665 (♥) "\226\153\166" .. -- U+2666 (♦) "\226\153\168" .. -- U+2668 (♨) "\226\153\187" .. -- U+267B (♻) "\226\153\190" .. -- U+267E (♾) "\226\153\191" .. -- U+267F (♿) "\226\154\146-\226\154\151" .. -- U+2692-U+2697 (⚒-⚗) "\226\154\153" .. -- U+2699 (⚙) "\226\154\155" .. -- U+269B (⚛) "\226\154\156" .. -- U+269C (⚜) "\226\154\160" .. -- U+26A0 (⚠) "\226\154\161" .. -- U+26A1 (⚡) "\226\154\170" .. -- U+26AA (⚪) "\226\154\171" .. -- U+26AB (⚫) "\226\154\176" .. -- U+26B0 (⚰) "\226\154\177" .. -- U+26B1 (⚱) "\226\154\189" .. -- U+26BD (⚽) "\226\154\190" .. -- U+26BE (⚾) "\226\155\132" .. -- U+26C4 (⛄) "\226\155\133" .. -- U+26C5 (⛅) "\226\155\136" .. -- U+26C8 (⛈) "\226\155\142" .. -- U+26CE (⛎) "\226\155\143" .. -- U+26CF (⛏) "\226\155\145" .. -- U+26D1 (⛑) "\226\155\147" .. -- U+26D3 (⛓) "\226\155\148" .. -- U+26D4 (⛔) "\226\155\169" .. -- U+26E9 (⛩) "\226\155\170" .. -- U+26EA (⛪) "\226\155\176-\226\155\181" .. -- U+26F0-U+26F5 (⛰-⛵) "\226\155\183-\226\155\186" .. -- U+26F7-U+26FA (⛷-⛺) "\226\155\189" .. -- U+26FD (⛽) "\226\156\130" .. -- U+2702 (✂) "\226\156\133" .. -- U+2705 (✅) "\226\156\136-\226\156\141" .. -- U+2708-U+270D (✈-✍) "\226\156\143" .. -- U+270F (✏) "\226\156\146" .. -- U+2712 (✒) "\226\156\148" .. -- U+2714 (✔) "\226\156\150" .. -- U+2716 (✖) "\226\156\157" .. -- U+271D (✝) "\226\156\161" .. -- U+2721 (✡) "\226\156\168" .. -- U+2728 (✨) "\226\156\179" .. -- U+2733 (✳) "\226\156\180" .. -- U+2734 (✴) "\226\157\132" .. -- U+2744 (❄) "\226\157\135" .. -- U+2747 (❇) "\226\157\140" .. -- U+274C (❌) "\226\157\142" .. -- U+274E (❎) "\226\157\147-\226\157\149" .. -- U+2753-U+2755 (❓-❕) "\226\157\151" .. -- U+2757 (❗) "\226\157\163" .. -- U+2763 (❣) "\226\157\164" .. -- U+2764 (❤) "\226\158\149-\226\158\151" .. -- U+2795-U+2797 (➕-➗) "\226\158\161" .. -- U+27A1 (➡) "\226\158\176" .. -- U+27B0 (➰) "\226\158\191" .. -- U+27BF (➿) "\226\164\180" .. -- U+2934 (⤴) "\226\164\181" .. -- U+2935 (⤵) "\226\172\133-\226\172\135" .. -- U+2B05-U+2B07 (⬅-⬇) "\226\172\155" .. -- U+2B1B (⬛) "\226\172\156" .. -- U+2B1C (⬜) "\226\173\144" .. -- U+2B50 (⭐) "\226\173\149" .. -- U+2B55 (⭕) "\227\128\176" .. -- U+3030 (〰) "\227\128\189" .. -- U+303D (〽) "\227\138\151" .. -- U+3297 (㊗) "\227\138\153" .. -- U+3299 (㊙) "\240\159\128\132" .. -- U+1F004 (🀄) "\240\159\131\143" .. -- U+1F0CF (🃏) "\240\159\133\176" .. -- U+1F170 (🅰) "\240\159\133\177" .. -- U+1F171 (🅱) "\240\159\133\190" .. -- U+1F17E (🅾) "\240\159\133\191" .. -- U+1F17F (🅿) "\240\159\134\142" .. -- U+1F18E (🆎) "\240\159\134\145-\240\159\134\154" .. -- U+1F191-U+1F19A (🆑-🆚) "\240\159\136\129" .. -- U+1F201 (🈁) "\240\159\136\130" .. -- U+1F202 (🈂) "\240\159\136\154" .. -- U+1F21A (🈚) "\240\159\136\175" .. -- U+1F22F (🈯) "\240\159\136\178-\240\159\136\186" .. -- U+1F232-U+1F23A (🈲-🈺) "\240\159\137\144" .. -- U+1F250 (🉐) "\240\159\137\145" .. -- U+1F251 (🉑) "\240\159\140\128-\240\159\153\143" .. -- U+1F300-U+1F64F (🌀-🙏) "\240\159\154\128-\240\159\155\151" .. -- U+1F680-U+1F6D7 (🚀-🛗) "\240\159\155\156-\240\159\155\172" .. -- U+1F6DC-U+1F6EC (🛜-🛬) "\240\159\155\176-\240\159\155\188" .. -- U+1F6F0-U+1F6FC (🛰-🛼) "\240\159\159\160-\240\159\159\171" .. -- U+1F7E0-U+1F7EB (🟠-🟫) "\240\159\159\176" .. -- U+1F7F0 (🟰) "\240\159\164\140-\240\159\169\147" .. -- U+1F90C-U+1FA53 (🤌-🩓) "\240\159\169\160-\240\159\169\173" .. -- U+1FA60-U+1FA6D (🩠-🩭) "\240\159\169\176-\240\159\169\188" .. -- U+1FA70-U+1FA7C (🩰-🩼) "\240\159\170\128-\240\159\170\137" .. -- U+1FA80-U+1FA89 (🪀-🪉) "\240\159\170\143-\240\159\171\134" .. -- U+1FA8F-U+1FAC6 (🪏-🫆) "\240\159\171\142-\240\159\171\156" .. -- U+1FACE-U+1FADC (🫎-🫜) "\240\159\171\159-\240\159\171\169" .. -- U+1FADF-U+1FAE9 (🫟-🫩) "\240\159\171\176-\240\159\171\184" -- U+1FAF0-U+1FAF8 (🫰-🫸) local unsupported_characters local function get_unsupported_characters() unsupported_characters, get_unsupported_characters = {}, nil for k, v in pairs(load_data("Module:links/data").unsupported_characters) do unsupported_characters[v] = k end return unsupported_characters end -- The list of unsupported titles and invert it (so the keys are pagenames and values are canonical titles). local unsupported_titles local function get_unsupported_titles() unsupported_titles, get_unsupported_titles = {}, nil for k, v in pairs(load_data("Module:links/data").unsupported_titles) do unsupported_titles[v] = k end return unsupported_titles end -- To save on memory, we only cache names with either non-ASCII characters in them or ASCII characters to be removed or -- transformed (apostrophe, double quote, hyphen). local L2_sort_key_cache = {} function export.get_L2_sort_key(L2) if L2 == "Translingual" then return "\1" elseif L2 == "English" then return "\2" elseif match(L2, "^[%z\1-\b\14-!#-&(-,.-\127]+$") then return L2 end local sort_key = L2_sort_key_cache[L2] if sort_key then return sort_key end sort_key = toNFC(ugsub(ugsub(toNFD(L2), "[" .. comb_chars_all .. "'\"ʻʼ]+", ""), "[%s%-]+", " ")) L2_sort_key_cache[L2] = sort_key return sort_key end --[==[ Given a pagename (or {nil} for the current page), create and return a data structure describing the page. The returned object includes the following fields: * `comb_chars`: A table containing various Lua character class patterns for different types of combined characters (those that decompose into multiple characters in the NFD decomposition). The patterns are meant to be used with {mw.ustring.find()}. The keys are: ** `single`: Single combining characters (character + diacritic), without surrounding brackets; ** `double`: Double combining characters (character + diacritic + character), without surrounding brackets; ** `vs`: Variation selectors, without surrounding brackets; ** `all`: Concatenation of `single` + `double` + `vs`, without surrounding brackets; ** `diacritics_single`: Like `single` but with surrounding brackets; ** `diacritics_double`: Like `double` but with surrounding brackets; ** `diacritics_all`: Like `all` but with surrounding brackets; ** `combined_single`: Lua pattern for matching a spacing character followed by one or more single combining characters; ** `combined_double`: Lua pattern for matching a combination of two spacing characters separated by one or more double combining characters, possibly also with single combining characters; * `emoji_pattern`: A Lua character class pattern (including surrounding brackets) that matches emojis. Meant to be used with {mw.ustring.find()}. * `L2_list`: Ordered list of L2 headings on the page, with the extra key `n` that gives the length of the list. * `L2_sections`: Lookup table of L2 headings on the page, where the key is the section number assigned by the preprocessor, and the value is the L2 heading name. Once an invocation has got its actual section number from get_current_L2 in [[Module:pages]], it can use this table to determine its parent L2. TODO: We could expand this to include subsections, to check POS headings are correct etc. * `unsupported_titles`: Map from pagenames to canonical titles for unsupported-title pages. * `namespace`: Namespace of the pagename. * `ns`: Namespace table for the page from mw.site.namespaces (TODO: merge with `namespace` above). * `full_raw_pagename`: Full version of the '''RAW''' pagename (i.e. unsupported-title pages aren't canonicalized); including the namespace and the base (portion before the slash). * `pagename`: Canonicalized subpage portion of the pagename (unsupported-title pages are canonicalized). * `pagename_with_base`: Same as `pagename` in the main namespace; otherwise, the whole pagename without the namespace. * `decompose_pagename`: Equivalent of `pagename` in NFD decomposition. * `pagename_len`: Length of `pagename` in Unicode chars, where combinations of spacing character + decomposed diacritic are treated as single characters. * `explode_pagename`: Set of characters found in `pagename`. The keys are characters (where combinations of spacing character + decomposed diacritic are treated as single characters). * `encoded_pagename`: FIXME: Document me. * `pagename_defaultsort`: FIXME: Document me. * `raw_defaultsort`: FIXME: Document me. * `wikitext_topic_cat`: FIXME: Document me. * `wikitext_langname_cat`: FIXME: Document me. `no_fetch_content` says to not fetch and parse the content or set a DEFAULTSORT sort key, in order to save time on test and documentation pages that have lots of template invocations that set `|pagename=`. It turns out nearly all the time of this function is contained in the line `frame:callParserFunction("DEFAULTSORT", data.pagename_defaultsort)`, so we skip it on test and documentation pages where it accomplishes nothing in any case. ]==] function export.process_page(pagename, no_fetch_content) local data = { comb_chars = comb_chars, emoji_pattern = "[" .. emoji_chars .. "]", unsupported_titles = unsupported_titles or get_unsupported_titles() } local cats = {} data.cats = cats -- We cannot store `raw_title` in `data` because it contains a metatable. local raw_title local function bad_pagename() if not pagename then error("Internal error: Something wrong, `data.pagename` not specified but current title contains illegal characters") else error(format("Bad value for `data.pagename`: '%s', which must not contain illegal characters", pagename)) end end if pagename then -- for testing, doc pages, etc. raw_title = new_title(pagename) if not raw_title then bad_pagename() end else raw_title = mw.title.getCurrentTitle() end local nsText = raw_title.nsText local namespace_is_reconstruction = nsText == "Reconstruction" data.namespace = nsText data.ns = mw.site.namespaces[raw_title.namespace] local full_raw_pagename = raw_title.fullText data.full_raw_pagename = full_raw_pagename local frame = mw.getCurrentFrame() -- WARNING: `content` may be nil, e.g. if we're substing a template like {{ja-new}} on a not-yet-created page -- or if the module specifies the subpage as `data.pagename` (which many modules do) and we're in an Appendix -- or other non-mainspace page. We used to make the latter an error but there are too many modules that do it, -- and substing on a nonexistent page is totally legit, and we don't actually need to be able to access the -- content of the page. local content = not no_fetch_content and raw_title:getContent() or nil -- Get the pagename. pagename = physical_to_logical_pagename_if_mammoth(raw_title) pagename = gsub(pagename, "^Unsupported titles/(.+)", function(m) insert(cats, "Unsupported titles") local title = (unsupported_titles or get_unsupported_titles())[m] if title then return title end -- Substitute pairs of "`". Those not used for escaping should be escaped as "`grave`", but might not be, -- so if a pair don't form a match, the closing "`" should become the opening "`" of the next match attempt. -- This has to be done manually, instead of using gsub. local open_pos = find(m, "`") if not open_pos then return m end title = {sub(m, 1, open_pos - 1)} while true do local close_pos = find(m, "`", open_pos + 1) if not close_pos then -- Add "`" plus any remaining characters. insert(title, sub(m, open_pos)) break end local escape = sub(m, open_pos, close_pos) local ch = (unsupported_characters or get_unsupported_characters())[escape] -- Match found, so substitute the character and move to the first "`" after the match if found, or -- otherwise return. if ch then insert(title, ch) local nxt_pos = close_pos + 1 open_pos = find(m, "`", nxt_pos) -- Add any characters between the match and the next "`" or end. if open_pos then insert(title, sub(m, nxt_pos, open_pos - 1)) else insert(title, sub(m, nxt_pos)) break end -- Match not found, so make the closing "`" the opening "`" of the next attempt. else -- Add the failed match, except for the closing "`". insert(title, sub(m, open_pos, close_pos - 1)) open_pos = close_pos end end return concat(title) end) -- Save pagename, as the local variable will be destructively modified. data.pagename = pagename if nsText == "" then data.pagename_with_base = pagename else data.pagename_with_base = raw_title.text end -- Decompose the pagename in Unicode normalization form D. data.decompose_pagename = toNFD(pagename) -- Explode the current page name into a character table, taking decomposed combining characters into account. local explode_pagename = {} local pagename_len = 0 local function explode(char) explode_pagename[char] = true pagename_len = pagename_len + 1 return "" end pagename = ugsub(pagename, comb_chars.combined_double, explode) pagename = gsub(ugsub(pagename, comb_chars.combined_single, explode), ".[\128-\191]*", explode) data.explode_pagename = explode_pagename data.pagename_len = pagename_len -- Generate DEFAULTSORT. data.encoded_pagename = encode_entities(data.pagename) data.pagename_defaultsort = get_lang("mul"):makeSortKey(data.encoded_pagename) if not no_fetch_content then frame:callParserFunction("DEFAULTSORT", data.pagename_defaultsort) end data.raw_defaultsort = uupper(raw_title.text) -- Make `L2_list` and `L2_sections`, note raw wikitext use of {{DEFAULTSORT:}} and {{DISPLAYTITLE:}}, then add categories if any unwanted L1 headings are found, the L2 headings are in the wrong order, or they don't match a canonical language name. -- Note: HTML comments shouldn't be removed from `content` until after this step, as they can affect the result. do local L2_list, L2_list_len, L2_sections = {}, 0, {} local prev, rc local new_cats, L2_wrong_order = {} local function handle_heading(heading) local level = heading.level if level > 2 then return end local name = heading:get_name() -- heading:get_name() will return nil if there are any newline characters in the preprocessed heading name (e.g. from an expanded template). In such cases, the preprocessor section count still increments (since it's calculated pre-expansion), but the heading will fail, so the L2 count shouldn't be incremented. if name == nil then return end L2_list_len = L2_list_len + 1 L2_list[L2_list_len] = name L2_sections[heading.section] = name -- Also add any L1s, since they terminate the preceding L2, but add a maintenance category since it's probably a mistake. if level == 1 then new_cats["Pages with unwanted L1 headings"] = true end -- Check the heading is in the right order. -- FIXME: we need a more sophisticated sorting method which handles non-diacritic special characters (e.g. Magɨ). if prev and not ( L2_wrong_order or string_compare(export.get_L2_sort_key(prev), export.get_L2_sort_key(name)) ) then new_cats["Pages with language headings in the wrong order"] = true L2_wrong_order = true end -- Check it's a canonical language name. if not (langnames or get_langnames())[name] then new_cats["Pages with nonstandard language headings"] = true end prev = name end local function handle_template(template) -- Turn off redirect checking except in the Reconstruction namespace because the rc flag is only -- used in the Reconstruction namespace and the other names are parser functions, which AFAIK can't -- be redirected to. local name = template:get_name(nil, not namespace_is_reconstruction and "no_redirect" or nil) if name == "DEFAULTSORT:" then new_cats["Pages with DEFAULTSORT conflicts"] = true elseif name == "DISPLAYTITLE:" then new_cats["Pages with DISPLAYTITLE conflicts"] = true elseif name == "reconstructed" then rc = true end end if content then for node in parse(content):iterate_nodes() do local node_class = class_else_type(node) if node_class == "heading" then handle_heading(node) elseif node_class == "template" then handle_template(node) elseif node_class == "parameter" then new_cats["Pages with raw triple-brace template parameters"] = true end end end L2_list.n = L2_list_len data.L2_list = L2_list data.L2_sections = L2_sections insert(cats, get_category("Pages with entries")) insert(cats, get_category(format("Pages with %s entr%s", L2_list_len, L2_list_len == 1 and "y" or "ies"))) for cat in pairs(new_cats) do insert(cats, get_category(cat)) end if namespace_is_reconstruction and not rc then local langname = match(full_raw_pagename, "^Reconstruction:([^/]+)/.") if langname then insert(cats, get_category(langname .. " entries missing Template:reconstructed")) end end end ------ 4. Parse page for maintenance categories. ------ -- Use of tab characters. if content and find(content, "\t", 1, true) then insert(cats, get_category("Pages with tab characters")) end -- Unencoded character(s) in title. local IDS = list_to_set{"⿰", "⿱", "⿲", "⿳", "⿴", "⿵", "⿶", "⿷", "⿸", "⿹", "⿺", "⿻", "⿼", "⿽", "⿾", "⿿", "㇯"} for char in pairs(explode_pagename) do if IDS[char] and char ~= data.pagename then insert(cats, "Terms containing unencoded characters") break end end -- Raw wikitext use of a topic or langname category. Also check if any raw sortkeys have been used. do local wikitext_topic_cat = {} local wikitext_langname_cat = {} local raw_sortkey -- If a raw sortkey has been found, add it to the relevant table. -- If there's no table (or the index is just `true`), create one first. local function add_cat_table(t, lang, sortkey) local t_lang = t[lang] if not sortkey then if not t_lang then t[lang] = true end return elseif t_lang == true or not t_lang then t_lang = {} t[lang] = t_lang end t_lang[uupper(decode_entities(sortkey))] = true end local function process_category(content, cat, colon, nxt) local pipe = find(cat, "|", colon + 1, true) -- Categories cannot end "|]]". if pipe == #cat then return end local title = new_title(pipe and sub(cat, 1, pipe - 1) or cat) if not (title and title.namespace == 14) then return end -- Get the sortkey (if any), then canonicalize category title. local sortkey = pipe and sub(cat, pipe + 1) or nil cat = title.text if sortkey then raw_sortkey = true -- If the sortkey contains "[", the first "]" of a final "]]]" is treated as part of the sortkey. if find(sortkey, "[", 1, true) and sub(content, nxt, nxt) == "]" then sortkey = sortkey .. "]" end end local code = match(cat, "^([%w%-.]+):") if code then add_cat_table(wikitext_topic_cat, code, sortkey) return end -- Split by word. cat = split(cat, " ", true, true) -- Formerly we looked for the language name anywhere in the category. This is simply wrong -- because there are no categories like 'Alsatian French lemmas' (only L2 languages -- have langname categories), but doing it this way wrongly catches things like [[Category:Shapsug Adyghe]] -- in [[Category:Adyghe entries with language name categories using raw markup]]. local n = #cat - 1 if n <= 0 then return end -- Go from longest to shortest and stop once we've found a language name. Going from shortest -- to longest or not stopping after a match risks falsely matching (e.g.) German Low German -- categories as German. repeat local name = concat(cat, " ", 1, n) if (langnames or get_langnames())[name] then add_cat_table(wikitext_langname_cat, name, sortkey) return end n = n - 1 until n == 0 end if content then -- Remove comments, then iterate over category links. content = remove_comments(content, "BOTH") local head = find(content, "[[", 1, true) while head do local close = find(content, "]]", head + 2, true) if not close then break end -- Make sure there are no intervening "[[" between head and close. local open = find(content, "[[", head + 2, true) while open and open < close do head = open open = find(content, "[[", head + 2, true) end local cat = sub(content, head + 2, close - 1) -- Locate the colon, and weed out most unwanted links. "[ _\128-\244]*" catches valid whitespace, and ensures any category links using the colon trick are ignored. We match all non-ASCII characters, as there could be multibyte spaces, and mw.title.new will filter out any remaining false-positives; this is a lot faster than running mw.title.new on every link. local colon = match(cat, "^[ _\128-\244]*[Cc][Aa][Tt][EeGgOoRrYy _\128-\244]*():") if colon then process_category(content, cat, colon, close + 2) end head = open end end data.wikitext_topic_cat = wikitext_topic_cat data.wikitext_langname_cat = wikitext_langname_cat if raw_sortkey then insert(cats, get_category("Pages with raw sortkeys")) end end return data end return export 0esb0zcz2sjpy8aqh1uv1deoe3q625h 36504 36501 2026-08-09T17:21:11Z Ghybu 12 پووچەڵکردنەوەی پێداچوونەوەی [[Special:Diff/36501|36501]] لە لایەن [[Special:Contributions/Ghybu|Ghybu]] ([[User talk:Ghybu|لێدوان]]) 36504 Scribunto text/plain local export = {} local languages_module = "Module:languages" local maintenance_category_module = "Module:maintenance category" local string_compare_module = "Module:string/compare" local string_decode_entities_module = "Module:string/decodeEntities" local string_remove_comments_module = "Module:string/removeComments" local string_utilities_module = "Module:string utilities" local table_module = "Module:table" local template_parser_module = "Module:template parser" local mw = mw local string = string local table = table local ustring = mw.ustring local concat = table.concat local find = string.find local format = string.format local gsub = string.gsub local insert = table.insert local load_data = mw.loadData local match = string.match local new_title = mw.title.new local pairs = pairs local require = require local sub = string.sub local toNFC = ustring.toNFC local toNFD = ustring.toNFD local ugsub = ustring.gsub local function class_else_type(...) class_else_type = require(template_parser_module).class_else_type return class_else_type(...) end local function decode_entities(...) decode_entities = require(string_decode_entities_module) return decode_entities(...) end local function encode_entities(...) encode_entities = require(string_utilities_module).encode_entities return encode_entities(...) end local function get_category(...) get_category = require(maintenance_category_module).get_category return get_category(...) end local function get_lang(...) get_lang = require(languages_module).getByCode return get_lang(...) end local function list_to_set(...) list_to_set = require(table_module).listToSet return list_to_set(...) end local function parse(...) parse = require(template_parser_module).parse return parse(...) end local function remove_comments(...) remove_comments = require(string_remove_comments_module) return remove_comments(...) end local function split(...) split = require(string_utilities_module).split return split(...) end local function string_compare(...) string_compare = require(string_compare_module) return string_compare(...) end local function uupper(...) uupper = require(string_utilities_module).upper return uupper(...) 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 langnames local function get_langnames() langnames, get_langnames = load_data("Module:languages/canonical names"), nil return langnames end -- Combining character data used when categorising unusual characters. These resolve into two patterns, used to find -- single combining characters (i.e. character + diacritic(s)) or double combining characters (i.e. character + -- diacritic(s) + character). -- Charsets are in the format used by Unicode's UnicodeSet tool: https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp. -- Single combining characters. -- Charset: [[:M:]&[:^Canonical_Combining_Class=/^Double_/:]&[:^subhead=Grapheme joiner:]&[:^Variation_Selector=Yes:]] -- Note: concatenating hundreds of lines at once gives an error, so () are used every 150 lines to break it up into chunks. local comb_chars_single = ("\204\128-\205\142" .. -- U+0300-U+034E "\205\144-\205\155" .. -- U+0350-U+035B "\205\163-\205\175" .. -- U+0363-U+036F "\210\131-\210\137" .. -- U+0483-U+0489 "\214\145-\214\189" .. -- U+0591-U+05BD "\214\191" .. -- U+05BF "\215\129" .. -- U+05C1 "\215\130" .. -- U+05C2 "\215\132" .. -- U+05C4 "\215\133" .. -- U+05C5 "\215\135" .. -- U+05C7 "\216\144-\216\154" .. -- U+0610-U+061A "\217\139-\217\159" .. -- U+064B-U+065F "\217\176" .. -- U+0670 "\219\150-\219\156" .. -- U+06D6-U+06DC "\219\159-\219\164" .. -- U+06DF-U+06E4 "\219\167" .. -- U+06E7 "\219\168" .. -- U+06E8 "\219\170-\219\173" .. -- U+06EA-U+06ED "\220\145" .. -- U+0711 "\220\176-\221\138" .. -- U+0730-U+074A "\222\166-\222\176" .. -- U+07A6-U+07B0 "\223\171-\223\179" .. -- U+07EB-U+07F3 "\223\189" .. -- U+07FD "\224\160\150-\224\160\153" .. -- U+0816-U+0819 "\224\160\155-\224\160\163" .. -- U+081B-U+0823 "\224\160\165-\224\160\167" .. -- U+0825-U+0827 "\224\160\169-\224\160\173" .. -- U+0829-U+082D "\224\161\153-\224\161\155" .. -- U+0859-U+085B "\224\162\151-\224\162\159" .. -- U+0897-U+089F "\224\163\138-\224\163\161" .. -- U+08CA-U+08E1 "\224\163\163-\224\164\131" .. -- U+08E3-U+0903 "\224\164\186-\224\164\188" .. -- U+093A-U+093C "\224\164\190-\224\165\143" .. -- U+093E-U+094F "\224\165\145-\224\165\151" .. -- U+0951-U+0957 "\224\165\162" .. -- U+0962 "\224\165\163" .. -- U+0963 "\224\166\129-\224\166\131" .. -- U+0981-U+0983 "\224\166\188" .. -- U+09BC "\224\166\190-\224\167\132" .. -- U+09BE-U+09C4 "\224\167\135" .. -- U+09C7 "\224\167\136" .. -- U+09C8 "\224\167\139-\224\167\141" .. -- U+09CB-U+09CD "\224\167\151" .. -- U+09D7 "\224\167\162" .. -- U+09E2 "\224\167\163" .. -- U+09E3 "\224\167\190" .. -- U+09FE "\224\168\129-\224\168\131" .. -- U+0A01-U+0A03 "\224\168\188" .. -- U+0A3C "\224\168\190-\224\169\130" .. -- U+0A3E-U+0A42 "\224\169\135" .. -- U+0A47 "\224\169\136" .. -- U+0A48 "\224\169\139-\224\169\141" .. -- U+0A4B-U+0A4D "\224\169\145" .. -- U+0A51 "\224\169\176" .. -- U+0A70 "\224\169\177" .. -- U+0A71 "\224\169\181" .. -- U+0A75 "\224\170\129-\224\170\131" .. -- U+0A81-U+0A83 "\224\170\188" .. -- U+0ABC "\224\170\190-\224\171\133" .. -- U+0ABE-U+0AC5 "\224\171\135-\224\171\137" .. -- U+0AC7-U+0AC9 "\224\171\139-\224\171\141" .. -- U+0ACB-U+0ACD "\224\171\162" .. -- U+0AE2 "\224\171\163" .. -- U+0AE3 "\224\171\186-\224\171\191" .. -- U+0AFA-U+0AFF "\224\172\129-\224\172\131" .. -- U+0B01-U+0B03 "\224\172\188" .. -- U+0B3C "\224\172\190-\224\173\132" .. -- U+0B3E-U+0B44 "\224\173\135" .. -- U+0B47 "\224\173\136" .. -- U+0B48 "\224\173\139-\224\173\141" .. -- U+0B4B-U+0B4D "\224\173\149-\224\173\151" .. -- U+0B55-U+0B57 "\224\173\162" .. -- U+0B62 "\224\173\163" .. -- U+0B63 "\224\174\130" .. -- U+0B82 "\224\174\190-\224\175\130" .. -- U+0BBE-U+0BC2 "\224\175\134-\224\175\136" .. -- U+0BC6-U+0BC8 "\224\175\138-\224\175\141" .. -- U+0BCA-U+0BCD "\224\175\151" .. -- U+0BD7 "\224\176\128-\224\176\132" .. -- U+0C00-U+0C04 "\224\176\188" .. -- U+0C3C "\224\176\190-\224\177\132" .. -- U+0C3E-U+0C44 "\224\177\134-\224\177\136" .. -- U+0C46-U+0C48 "\224\177\138-\224\177\141" .. -- U+0C4A-U+0C4D "\224\177\149" .. -- U+0C55 "\224\177\150" .. -- U+0C56 "\224\177\162" .. -- U+0C62 "\224\177\163" .. -- U+0C63 "\224\178\129-\224\178\131" .. -- U+0C81-U+0C83 "\224\178\188" .. -- U+0CBC "\224\178\190-\224\179\132" .. -- U+0CBE-U+0CC4 "\224\179\134-\224\179\136" .. -- U+0CC6-U+0CC8 "\224\179\138-\224\179\141" .. -- U+0CCA-U+0CCD "\224\179\149" .. -- U+0CD5 "\224\179\150" .. -- U+0CD6 "\224\179\162" .. -- U+0CE2 "\224\179\163" .. -- U+0CE3 "\224\179\179" .. -- U+0CF3 "\224\180\128-\224\180\131" .. -- U+0D00-U+0D03 "\224\180\187" .. -- U+0D3B "\224\180\188" .. -- U+0D3C "\224\180\190-\224\181\132" .. -- U+0D3E-U+0D44 "\224\181\134-\224\181\136" .. -- U+0D46-U+0D48 "\224\181\138-\224\181\141" .. -- U+0D4A-U+0D4D "\224\181\151" .. -- U+0D57 "\224\181\162" .. -- U+0D62 "\224\181\163" .. -- U+0D63 "\224\182\129-\224\182\131" .. -- U+0D81-U+0D83 "\224\183\138" .. -- U+0DCA "\224\183\143-\224\183\148" .. -- U+0DCF-U+0DD4 "\224\183\150" .. -- U+0DD6 "\224\183\152-\224\183\159" .. -- U+0DD8-U+0DDF "\224\183\178" .. -- U+0DF2 "\224\183\179" .. -- U+0DF3 "\224\184\177" .. -- U+0E31 "\224\184\180-\224\184\186" .. -- U+0E34-U+0E3A "\224\185\135-\224\185\142" .. -- U+0E47-U+0E4E "\224\186\177" .. -- U+0EB1 "\224\186\180-\224\186\188" .. -- U+0EB4-U+0EBC "\224\187\136-\224\187\142" .. -- U+0EC8-U+0ECE "\224\188\152" .. -- U+0F18 "\224\188\153" .. -- U+0F19 "\224\188\181" .. -- U+0F35 "\224\188\183" .. -- U+0F37 "\224\188\185" .. -- U+0F39 "\224\188\190" .. -- U+0F3E "\224\188\191" .. -- U+0F3F "\224\189\177-\224\190\132" .. -- U+0F71-U+0F84 "\224\190\134" .. -- U+0F86 "\224\190\135" .. -- U+0F87 "\224\190\141-\224\190\151" .. -- U+0F8D-U+0F97 "\224\190\153-\224\190\188" .. -- U+0F99-U+0FBC "\224\191\134" .. -- U+0FC6 "\225\128\171-\225\128\190" .. -- U+102B-U+103E "\225\129\150-\225\129\153" .. -- U+1056-U+1059 "\225\129\158-\225\129\160" .. -- U+105E-U+1060 "\225\129\162-\225\129\164" .. -- U+1062-U+1064 "\225\129\167-\225\129\173" .. -- U+1067-U+106D "\225\129\177-\225\129\180" .. -- U+1071-U+1074 "\225\130\130-\225\130\141" .. -- U+1082-U+108D "\225\130\143" .. -- U+108F "\225\130\154-\225\130\157" .. -- U+109A-U+109D "\225\141\157-\225\141\159" .. -- U+135D-U+135F "\225\156\146-\225\156\149" .. -- U+1712-U+1715 "\225\156\178-\225\156\180" .. -- U+1732-U+1734 "\225\157\146" .. -- U+1752 "\225\157\147" .. -- U+1753 "\225\157\178" .. -- U+1772 "\225\157\179" .. -- U+1773 "\225\158\180-\225\159\147") .. -- U+17B4-U+17D3 ("\225\159\157" .. -- U+17DD "\225\162\133" .. -- U+1885 "\225\162\134" .. -- U+1886 "\225\162\169" .. -- U+18A9 "\225\164\160-\225\164\171" .. -- U+1920-U+192B "\225\164\176-\225\164\187" .. -- U+1930-U+193B "\225\168\151-\225\168\155" .. -- U+1A17-U+1A1B "\225\169\149-\225\169\158" .. -- U+1A55-U+1A5E "\225\169\160-\225\169\188" .. -- U+1A60-U+1A7C "\225\169\191" .. -- U+1A7F "\225\170\176-\225\171\142" .. -- U+1AB0-U+1ACE "\225\172\128-\225\172\132" .. -- U+1B00-U+1B04 "\225\172\180-\225\173\132" .. -- U+1B34-U+1B44 "\225\173\171-\225\173\179" .. -- U+1B6B-U+1B73 "\225\174\128-\225\174\130" .. -- U+1B80-U+1B82 "\225\174\161-\225\174\173" .. -- U+1BA1-U+1BAD "\225\175\166-\225\175\179" .. -- U+1BE6-U+1BF3 "\225\176\164-\225\176\183" .. -- U+1C24-U+1C37 "\225\179\144-\225\179\146" .. -- U+1CD0-U+1CD2 "\225\179\148-\225\179\168" .. -- U+1CD4-U+1CE8 "\225\179\173" .. -- U+1CED "\225\179\180" .. -- U+1CF4 "\225\179\183-\225\179\185" .. -- U+1CF7-U+1CF9 "\225\183\128-\225\183\140" .. -- U+1DC0-U+1DCC "\225\183\142-\225\183\187" .. -- U+1DCE-U+1DFB "\225\183\189-\225\183\191" .. -- U+1DFD-U+1DFF "\226\131\144-\226\131\176" .. -- U+20D0-U+20F0 "\226\179\175-\226\179\177" .. -- U+2CEF-U+2CF1 "\226\181\191" .. -- U+2D7F "\226\183\160-\226\183\191" .. -- U+2DE0-U+2DFF "\227\128\170-\227\128\175" .. -- U+302A-U+302F "\227\130\153" .. -- U+3099 "\227\130\154" .. -- U+309A "\234\153\175-\234\153\178" .. -- U+A66F-U+A672 "\234\153\180-\234\153\189" .. -- U+A674-U+A67D "\234\154\158" .. -- U+A69E "\234\154\159" .. -- U+A69F "\234\155\176" .. -- U+A6F0 "\234\155\177" .. -- U+A6F1 "\234\160\130" .. -- U+A802 "\234\160\134" .. -- U+A806 "\234\160\139" .. -- U+A80B "\234\160\163-\234\160\167" .. -- U+A823-U+A827 "\234\160\172" .. -- U+A82C "\234\162\128" .. -- U+A880 "\234\162\129" .. -- U+A881 "\234\162\180-\234\163\133" .. -- U+A8B4-U+A8C5 "\234\163\160-\234\163\177" .. -- U+A8E0-U+A8F1 "\234\163\191" .. -- U+A8FF "\234\164\166-\234\164\173" .. -- U+A926-U+A92D "\234\165\135-\234\165\147" .. -- U+A947-U+A953 "\234\166\128-\234\166\131" .. -- U+A980-U+A983 "\234\166\179-\234\167\128" .. -- U+A9B3-U+A9C0 "\234\167\165" .. -- U+A9E5 "\234\168\169-\234\168\182" .. -- U+AA29-U+AA36 "\234\169\131" .. -- U+AA43 "\234\169\140" .. -- U+AA4C "\234\169\141" .. -- U+AA4D "\234\169\187-\234\169\189" .. -- U+AA7B-U+AA7D "\234\170\176" .. -- U+AAB0 "\234\170\178-\234\170\180" .. -- U+AAB2-U+AAB4 "\234\170\183" .. -- U+AAB7 "\234\170\184" .. -- U+AAB8 "\234\170\190" .. -- U+AABE "\234\170\191" .. -- U+AABF "\234\171\129" .. -- U+AAC1 "\234\171\171-\234\171\175" .. -- U+AAEB-U+AAEF "\234\171\181" .. -- U+AAF5 "\234\171\182" .. -- U+AAF6 "\234\175\163-\234\175\170" .. -- U+ABE3-U+ABEA "\234\175\172" .. -- U+ABEC "\234\175\173" .. -- U+ABED "\239\172\158" .. -- U+FB1E "\239\184\160-\239\184\175" .. -- U+FE20-U+FE2F "\240\144\135\189" .. -- U+101FD "\240\144\139\160" .. -- U+102E0 "\240\144\141\182-\240\144\141\186" .. -- U+10376-U+1037A "\240\144\168\129-\240\144\168\131" .. -- U+10A01-U+10A03 "\240\144\168\133" .. -- U+10A05 "\240\144\168\134" .. -- U+10A06 "\240\144\168\140-\240\144\168\143" .. -- U+10A0C-U+10A0F "\240\144\168\184-\240\144\168\186" .. -- U+10A38-U+10A3A "\240\144\168\191" .. -- U+10A3F "\240\144\171\165" .. -- U+10AE5 "\240\144\171\166" .. -- U+10AE6 "\240\144\180\164-\240\144\180\167" .. -- U+10D24-U+10D27 "\240\144\181\169-\240\144\181\173" .. -- U+10D69-U+10D6D "\240\144\186\171" .. -- U+10EAB "\240\144\186\172" .. -- U+10EAC "\240\144\187\188-\240\144\187\191" .. -- U+10EFC-U+10EFF "\240\144\189\134-\240\144\189\144" .. -- U+10F46-U+10F50 "\240\144\190\130-\240\144\190\133" .. -- U+10F82-U+10F85 "\240\145\128\128-\240\145\128\130" .. -- U+11000-U+11002 "\240\145\128\184-\240\145\129\134" .. -- U+11038-U+11046 "\240\145\129\176" .. -- U+11070 "\240\145\129\179" .. -- U+11073 "\240\145\129\180" .. -- U+11074 "\240\145\129\191-\240\145\130\130" .. -- U+1107F-U+11082 "\240\145\130\176-\240\145\130\186" .. -- U+110B0-U+110BA "\240\145\131\130" .. -- U+110C2 "\240\145\132\128-\240\145\132\130" .. -- U+11100-U+11102 "\240\145\132\167-\240\145\132\180" .. -- U+11127-U+11134 "\240\145\133\133" .. -- U+11145 "\240\145\133\134" .. -- U+11146 "\240\145\133\179" .. -- U+11173 "\240\145\134\128-\240\145\134\130" .. -- U+11180-U+11182 "\240\145\134\179-\240\145\135\128" .. -- U+111B3-U+111C0 "\240\145\135\137-\240\145\135\140" .. -- U+111C9-U+111CC "\240\145\135\142" .. -- U+111CE "\240\145\135\143" .. -- U+111CF "\240\145\136\172-\240\145\136\183" .. -- U+1122C-U+11237 "\240\145\136\190" .. -- U+1123E "\240\145\137\129" .. -- U+11241 "\240\145\139\159-\240\145\139\170" .. -- U+112DF-U+112EA "\240\145\140\128-\240\145\140\131" .. -- U+11300-U+11303 "\240\145\140\187" .. -- U+1133B "\240\145\140\188" .. -- U+1133C "\240\145\140\190-\240\145\141\132" .. -- U+1133E-U+11344 "\240\145\141\135" .. -- U+11347 "\240\145\141\136" .. -- U+11348 "\240\145\141\139-\240\145\141\141" .. -- U+1134B-U+1134D "\240\145\141\151" .. -- U+11357 "\240\145\141\162" .. -- U+11362 "\240\145\141\163" .. -- U+11363 "\240\145\141\166-\240\145\141\172" .. -- U+11366-U+1136C "\240\145\141\176-\240\145\141\180" .. -- U+11370-U+11374 "\240\145\142\184-\240\145\143\128" .. -- U+113B8-U+113C0 "\240\145\143\130" .. -- U+113C2 "\240\145\143\133" .. -- U+113C5 "\240\145\143\135-\240\145\143\138" .. -- U+113C7-U+113CA "\240\145\143\140-\240\145\143\144" .. -- U+113CC-U+113D0 "\240\145\143\146" .. -- U+113D2 "\240\145\143\161" .. -- U+113E1 "\240\145\143\162" .. -- U+113E2 "\240\145\144\181-\240\145\145\134" .. -- U+11435-U+11446 "\240\145\145\158" .. -- U+1145E "\240\145\146\176-\240\145\147\131" .. -- U+114B0-U+114C3 "\240\145\150\175-\240\145\150\181" .. -- U+115AF-U+115B5 "\240\145\150\184-\240\145\151\128" .. -- U+115B8-U+115C0 "\240\145\151\156" .. -- U+115DC "\240\145\151\157" .. -- U+115DD "\240\145\152\176-\240\145\153\128" .. -- U+11630-U+11640 "\240\145\154\171-\240\145\154\183" .. -- U+116AB-U+116B7 "\240\145\156\157-\240\145\156\171" .. -- U+1171D-U+1172B "\240\145\160\172-\240\145\160\186" .. -- U+1182C-U+1183A "\240\145\164\176-\240\145\164\181" .. -- U+11930-U+11935 "\240\145\164\183" .. -- U+11937 "\240\145\164\184" .. -- U+11938 "\240\145\164\187-\240\145\164\190" .. -- U+1193B-U+1193E "\240\145\165\128") .. -- U+11940 ("\240\145\165\130" .. -- U+11942 "\240\145\165\131" .. -- U+11943 "\240\145\167\145-\240\145\167\151" .. -- U+119D1-U+119D7 "\240\145\167\154-\240\145\167\160" .. -- U+119DA-U+119E0 "\240\145\167\164" .. -- U+119E4 "\240\145\168\129-\240\145\168\138" .. -- U+11A01-U+11A0A "\240\145\168\179-\240\145\168\185" .. -- U+11A33-U+11A39 "\240\145\168\187-\240\145\168\190" .. -- U+11A3B-U+11A3E "\240\145\169\135" .. -- U+11A47 "\240\145\169\145-\240\145\169\155" .. -- U+11A51-U+11A5B "\240\145\170\138-\240\145\170\153" .. -- U+11A8A-U+11A99 "\240\145\176\175-\240\145\176\182" .. -- U+11C2F-U+11C36 "\240\145\176\184-\240\145\176\191" .. -- U+11C38-U+11C3F "\240\145\178\146-\240\145\178\167" .. -- U+11C92-U+11CA7 "\240\145\178\169-\240\145\178\182" .. -- U+11CA9-U+11CB6 "\240\145\180\177-\240\145\180\182" .. -- U+11D31-U+11D36 "\240\145\180\186" .. -- U+11D3A "\240\145\180\188" .. -- U+11D3C "\240\145\180\189" .. -- U+11D3D "\240\145\180\191-\240\145\181\133" .. -- U+11D3F-U+11D45 "\240\145\181\135" .. -- U+11D47 "\240\145\182\138-\240\145\182\142" .. -- U+11D8A-U+11D8E "\240\145\182\144" .. -- U+11D90 "\240\145\182\145" .. -- U+11D91 "\240\145\182\147-\240\145\182\151" .. -- U+11D93-U+11D97 "\240\145\187\179-\240\145\187\182" .. -- U+11EF3-U+11EF6 "\240\145\188\128" .. -- U+11F00 "\240\145\188\129" .. -- U+11F01 "\240\145\188\131" .. -- U+11F03 "\240\145\188\180-\240\145\188\186" .. -- U+11F34-U+11F3A "\240\145\188\190-\240\145\189\130" .. -- U+11F3E-U+11F42 "\240\145\189\154" .. -- U+11F5A "\240\147\145\128" .. -- U+13440 "\240\147\145\135-\240\147\145\149" .. -- U+13447-U+13455 "\240\150\132\158-\240\150\132\175" .. -- U+1611E-U+1612F "\240\150\171\176-\240\150\171\180" .. -- U+16AF0-U+16AF4 "\240\150\172\176-\240\150\172\182" .. -- U+16B30-U+16B36 "\240\150\189\143" .. -- U+16F4F "\240\150\189\145-\240\150\190\135" .. -- U+16F51-U+16F87 "\240\150\190\143-\240\150\190\146" .. -- U+16F8F-U+16F92 "\240\150\191\164" .. -- U+16FE4 "\240\150\191\176" .. -- U+16FF0 "\240\150\191\177" .. -- U+16FF1 "\240\155\178\157" .. -- U+1BC9D "\240\155\178\158" .. -- U+1BC9E "\240\156\188\128-\240\156\188\173" .. -- U+1CF00-U+1CF2D "\240\156\188\176-\240\156\189\134" .. -- U+1CF30-U+1CF46 "\240\157\133\165-\240\157\133\169" .. -- U+1D165-U+1D169 "\240\157\133\173-\240\157\133\178" .. -- U+1D16D-U+1D172 "\240\157\133\187-\240\157\134\130" .. -- U+1D17B-U+1D182 "\240\157\134\133-\240\157\134\139" .. -- U+1D185-U+1D18B "\240\157\134\170-\240\157\134\173" .. -- U+1D1AA-U+1D1AD "\240\157\137\130-\240\157\137\132" .. -- U+1D242-U+1D244 "\240\157\168\128-\240\157\168\182" .. -- U+1DA00-U+1DA36 "\240\157\168\187-\240\157\169\172" .. -- U+1DA3B-U+1DA6C "\240\157\169\181" .. -- U+1DA75 "\240\157\170\132" .. -- U+1DA84 "\240\157\170\155-\240\157\170\159" .. -- U+1DA9B-U+1DA9F "\240\157\170\161-\240\157\170\175" .. -- U+1DAA1-U+1DAAF "\240\158\128\128-\240\158\128\134" .. -- U+1E000-U+1E006 "\240\158\128\136-\240\158\128\152" .. -- U+1E008-U+1E018 "\240\158\128\155-\240\158\128\161" .. -- U+1E01B-U+1E021 "\240\158\128\163" .. -- U+1E023 "\240\158\128\164" .. -- U+1E024 "\240\158\128\166-\240\158\128\170" .. -- U+1E026-U+1E02A "\240\158\130\143" .. -- U+1E08F "\240\158\132\176-\240\158\132\182" .. -- U+1E130-U+1E136 "\240\158\138\174" .. -- U+1E2AE "\240\158\139\172-\240\158\139\175" .. -- U+1E2EC-U+1E2EF "\240\158\147\172-\240\158\147\175" .. -- U+1E4EC-U+1E4EF "\240\158\151\174" .. -- U+1E5EE "\240\158\151\175" .. -- U+1E5EF "\240\158\163\144-\240\158\163\150" .. -- U+1E8D0-U+1E8D6 "\240\158\165\132-\240\158\165\138") -- U+1E944-U+1E94A -- Double combining characters. -- Charset: [[:M:]&[:Canonical_Combining_Class=/^Double_/:]&[:^subhead=Grapheme joiner:]&[:^Variation_Selector=Yes:]] local comb_chars_double = "\205\156-\205\162" .. -- U+035C-U+0362 "\225\183\141" .. -- U+1DCD "\225\183\188" -- U+1DFC -- Variation selectors etc.; separated out so that we don't get categories for them. -- Charset: [[:M:]&[[:subhead=Grapheme joiner:][:Variation_Selector=Yes:]]]. local comb_chars_other = "\205\143" .. -- U+034F "\225\160\139-\225\160\141" .. -- U+180B-U+180D "\225\160\143" .. -- U+180F "\239\184\128-\239\184\143" .. -- U+FE00-U+FE0F "\243\160\132\128-\243\160\135\175" -- U+E0100-U+E01EF local comb_chars_all = comb_chars_single .. comb_chars_double .. comb_chars_other local comb_chars = { combined_single = "[^" .. comb_chars_all .. "][" .. comb_chars_single .. comb_chars_other .. "]+%f[^" .. comb_chars_all .. "]", combined_double = "[^" .. comb_chars_all .. "][" .. comb_chars_single .. comb_chars_other .. "]*[" .. comb_chars_double .. "]+[" .. comb_chars_all .. "]*.[" .. comb_chars_single .. comb_chars_other .. "]*", diacritics_single = "[" .. comb_chars_single .. "]", diacritics_double = "[" .. comb_chars_double .. "]", diacritics_all = "[" .. comb_chars_all .. "]" } -- Somewhat curated list from https://unicode.org/Public/emoji/16.0/emoji-sequences.txt. -- NOTE: There are lots more emoji sequences involving non-emoji Plane 0 symbols followed by 0xFE0F, which we don't -- (yet?) handle. local emoji_chars = "\226\140\154" .. -- U+231A (⌚) "\226\140\155" .. -- U+231B (⌛) "\226\140\168" .. -- U+2328 (⌨) "\226\143\143" .. -- U+23CF (⏏) "\226\143\169-\226\143\179" .. -- U+23E9-U+23F3 (⏩-⏳) "\226\143\184-\226\143\186" .. -- U+23F8-U+23FA (⏸-⏺) "\226\150\170" .. -- U+25AA (▪) "\226\150\171" .. -- U+25AB (▫) "\226\150\182" .. -- U+25B6 (▶) "\226\151\128" .. -- U+25C0 (◀) "\226\151\187-\226\151\190" .. -- U+25FB-U+25FE (◻-◾) "\226\152\128-\226\152\132" .. -- U+2600-U+2604 (☀-☄) "\226\152\142" .. -- U+260E (☎) "\226\152\145" .. -- U+2611 (☑) "\226\152\148" .. -- U+2614 (☔) "\226\152\149" .. -- U+2615 (☕) "\226\152\152" .. -- U+2618 (☘) "\226\152\157" .. -- U+261D (☝) "\226\152\160" .. -- U+2620 (☠) "\226\152\162" .. -- U+2622 (☢) "\226\152\163" .. -- U+2623 (☣) "\226\152\166" .. -- U+2626 (☦) "\226\152\170" .. -- U+262A (☪) "\226\152\174" .. -- U+262E (☮) "\226\152\175" .. -- U+262F (☯) "\226\152\184-\226\152\186" .. -- U+2638-U+263A (☸-☺) "\226\153\136-\226\153\147" .. -- U+2648-U+2653 (♈-♓) "\226\153\159" .. -- U+265F (♟) "\226\153\160" .. -- U+2660 (♠) "\226\153\163" .. -- U+2663 (♣) "\226\153\165" .. -- U+2665 (♥) "\226\153\166" .. -- U+2666 (♦) "\226\153\168" .. -- U+2668 (♨) "\226\153\187" .. -- U+267B (♻) "\226\153\190" .. -- U+267E (♾) "\226\153\191" .. -- U+267F (♿) "\226\154\146-\226\154\151" .. -- U+2692-U+2697 (⚒-⚗) "\226\154\153" .. -- U+2699 (⚙) "\226\154\155" .. -- U+269B (⚛) "\226\154\156" .. -- U+269C (⚜) "\226\154\160" .. -- U+26A0 (⚠) "\226\154\161" .. -- U+26A1 (⚡) "\226\154\170" .. -- U+26AA (⚪) "\226\154\171" .. -- U+26AB (⚫) "\226\154\176" .. -- U+26B0 (⚰) "\226\154\177" .. -- U+26B1 (⚱) "\226\154\189" .. -- U+26BD (⚽) "\226\154\190" .. -- U+26BE (⚾) "\226\155\132" .. -- U+26C4 (⛄) "\226\155\133" .. -- U+26C5 (⛅) "\226\155\136" .. -- U+26C8 (⛈) "\226\155\142" .. -- U+26CE (⛎) "\226\155\143" .. -- U+26CF (⛏) "\226\155\145" .. -- U+26D1 (⛑) "\226\155\147" .. -- U+26D3 (⛓) "\226\155\148" .. -- U+26D4 (⛔) "\226\155\169" .. -- U+26E9 (⛩) "\226\155\170" .. -- U+26EA (⛪) "\226\155\176-\226\155\181" .. -- U+26F0-U+26F5 (⛰-⛵) "\226\155\183-\226\155\186" .. -- U+26F7-U+26FA (⛷-⛺) "\226\155\189" .. -- U+26FD (⛽) "\226\156\130" .. -- U+2702 (✂) "\226\156\133" .. -- U+2705 (✅) "\226\156\136-\226\156\141" .. -- U+2708-U+270D (✈-✍) "\226\156\143" .. -- U+270F (✏) "\226\156\146" .. -- U+2712 (✒) "\226\156\148" .. -- U+2714 (✔) "\226\156\150" .. -- U+2716 (✖) "\226\156\157" .. -- U+271D (✝) "\226\156\161" .. -- U+2721 (✡) "\226\156\168" .. -- U+2728 (✨) "\226\156\179" .. -- U+2733 (✳) "\226\156\180" .. -- U+2734 (✴) "\226\157\132" .. -- U+2744 (❄) "\226\157\135" .. -- U+2747 (❇) "\226\157\140" .. -- U+274C (❌) "\226\157\142" .. -- U+274E (❎) "\226\157\147-\226\157\149" .. -- U+2753-U+2755 (❓-❕) "\226\157\151" .. -- U+2757 (❗) "\226\157\163" .. -- U+2763 (❣) "\226\157\164" .. -- U+2764 (❤) "\226\158\149-\226\158\151" .. -- U+2795-U+2797 (➕-➗) "\226\158\161" .. -- U+27A1 (➡) "\226\158\176" .. -- U+27B0 (➰) "\226\158\191" .. -- U+27BF (➿) "\226\164\180" .. -- U+2934 (⤴) "\226\164\181" .. -- U+2935 (⤵) "\226\172\133-\226\172\135" .. -- U+2B05-U+2B07 (⬅-⬇) "\226\172\155" .. -- U+2B1B (⬛) "\226\172\156" .. -- U+2B1C (⬜) "\226\173\144" .. -- U+2B50 (⭐) "\226\173\149" .. -- U+2B55 (⭕) "\227\128\176" .. -- U+3030 (〰) "\227\128\189" .. -- U+303D (〽) "\227\138\151" .. -- U+3297 (㊗) "\227\138\153" .. -- U+3299 (㊙) "\240\159\128\132" .. -- U+1F004 (🀄) "\240\159\131\143" .. -- U+1F0CF (🃏) "\240\159\133\176" .. -- U+1F170 (🅰) "\240\159\133\177" .. -- U+1F171 (🅱) "\240\159\133\190" .. -- U+1F17E (🅾) "\240\159\133\191" .. -- U+1F17F (🅿) "\240\159\134\142" .. -- U+1F18E (🆎) "\240\159\134\145-\240\159\134\154" .. -- U+1F191-U+1F19A (🆑-🆚) "\240\159\136\129" .. -- U+1F201 (🈁) "\240\159\136\130" .. -- U+1F202 (🈂) "\240\159\136\154" .. -- U+1F21A (🈚) "\240\159\136\175" .. -- U+1F22F (🈯) "\240\159\136\178-\240\159\136\186" .. -- U+1F232-U+1F23A (🈲-🈺) "\240\159\137\144" .. -- U+1F250 (🉐) "\240\159\137\145" .. -- U+1F251 (🉑) "\240\159\140\128-\240\159\153\143" .. -- U+1F300-U+1F64F (🌀-🙏) "\240\159\154\128-\240\159\155\151" .. -- U+1F680-U+1F6D7 (🚀-🛗) "\240\159\155\156-\240\159\155\172" .. -- U+1F6DC-U+1F6EC (🛜-🛬) "\240\159\155\176-\240\159\155\188" .. -- U+1F6F0-U+1F6FC (🛰-🛼) "\240\159\159\160-\240\159\159\171" .. -- U+1F7E0-U+1F7EB (🟠-🟫) "\240\159\159\176" .. -- U+1F7F0 (🟰) "\240\159\164\140-\240\159\169\147" .. -- U+1F90C-U+1FA53 (🤌-🩓) "\240\159\169\160-\240\159\169\173" .. -- U+1FA60-U+1FA6D (🩠-🩭) "\240\159\169\176-\240\159\169\188" .. -- U+1FA70-U+1FA7C (🩰-🩼) "\240\159\170\128-\240\159\170\137" .. -- U+1FA80-U+1FA89 (🪀-🪉) "\240\159\170\143-\240\159\171\134" .. -- U+1FA8F-U+1FAC6 (🪏-🫆) "\240\159\171\142-\240\159\171\156" .. -- U+1FACE-U+1FADC (🫎-🫜) "\240\159\171\159-\240\159\171\169" .. -- U+1FADF-U+1FAE9 (🫟-🫩) "\240\159\171\176-\240\159\171\184" -- U+1FAF0-U+1FAF8 (🫰-🫸) local unsupported_characters local function get_unsupported_characters() unsupported_characters, get_unsupported_characters = {}, nil for k, v in pairs(load_data("Module:links/data").unsupported_characters) do unsupported_characters[v] = k end return unsupported_characters end -- The list of unsupported titles and invert it (so the keys are pagenames and values are canonical titles). local unsupported_titles local function get_unsupported_titles() unsupported_titles, get_unsupported_titles = {}, nil for k, v in pairs(load_data("Module:links/data").unsupported_titles) do unsupported_titles[v] = k end return unsupported_titles end --[==[ Given a pagename (or {nil} for the current page), create and return a data structure describing the page. The returned object includes the following fields: * `comb_chars`: A table containing various Lua character class patterns for different types of combined characters (those that decompose into multiple characters in the NFD decomposition). The patterns are meant to be used with {mw.ustring.find()}. The keys are: ** `single`: Single combining characters (character + diacritic), without surrounding brackets; ** `double`: Double combining characters (character + diacritic + character), without surrounding brackets; ** `vs`: Variation selectors, without surrounding brackets; ** `all`: Concatenation of `single` + `double` + `vs`, without surrounding brackets; ** `diacritics_single`: Like `single` but with surrounding brackets; ** `diacritics_double`: Like `double` but with surrounding brackets; ** `diacritics_all`: Like `all` but with surrounding brackets; ** `combined_single`: Lua pattern for matching a spacing character followed by one or more single combining characters; ** `combined_double`: Lua pattern for matching a combination of two spacing characters separated by one or more double combining characters, possibly also with single combining characters; * `emoji_pattern`: A Lua character class pattern (including surrounding brackets) that matches emojis. Meant to be used with {mw.ustring.find()}. * `L2_list`: Ordered list of L2 headings on the page, with the extra key `n` that gives the length of the list. * `L2_sections`: Lookup table of L2 headings on the page, where the key is the section number assigned by the preprocessor, and the value is the L2 heading name. Once an invocation has got its actual section number from get_current_L2 in [[Module:pages]], it can use this table to determine its parent L2. TODO: We could expand this to include subsections, to check POS headings are correct etc. * `unsupported_titles`: Map from pagenames to canonical titles for unsupported-title pages. * `namespace`: Namespace of the pagename. * `ns`: Namespace table for the page from mw.site.namespaces (TODO: merge with `namespace` above). * `full_raw_pagename`: Full version of the '''RAW''' pagename (i.e. unsupported-title pages aren't canonicalized); including the namespace and the root (portion before the slash). * `pagename`: Canonicalized subpage portion of the pagename (unsupported-title pages are canonicalized). * `decompose_pagename`: Equivalent of `pagename` in NFD decomposition. * `pagename_len`: Length of `pagename` in Unicode chars, where combinations of spacing character + decomposed diacritic are treated as single characters. * `explode_pagename`: Set of characters found in `pagename`. The keys are characters (where combinations of spacing character + decomposed diacritic are treated as single characters). * `encoded_pagename`: FIXME: Document me. * `pagename_defaultsort`: FIXME: Document me. * `raw_defaultsort`: FIXME: Document me. * `wikitext_topic_cat`: FIXME: Document me. * `wikitext_langname_cat`: FIXME: Document me. ]==] function export.process_page(pagename) local data = { comb_chars = comb_chars, emoji_pattern = "[" .. emoji_chars .. "]", unsupported_titles = unsupported_titles or get_unsupported_titles() } local cats = {} data.cats = cats -- We cannot store `raw_title` in `data` because it contains a metatable. local raw_title local function bad_pagename() if not pagename then error("Internal error: Something wrong, `data.pagename` not specified but current title contains illegal characters") else error(format("Bad value for `data.pagename`: '%s', which must not contain illegal characters", pagename)) end end if pagename then -- for testing, doc pages, etc. raw_title = new_title(pagename) if not raw_title then bad_pagename() end else raw_title = mw.title.getCurrentTitle() end local nsText = raw_title.nsText data.namespace = nsText data.ns = mw.site.namespaces[raw_title.namespace] local full_raw_pagename = raw_title.fullText data.full_raw_pagename = full_raw_pagename local frame = mw.getCurrentFrame() -- WARNING: `content` may be nil, e.g. if we're substing a template like {{ja-new}} on a not-yet-created page -- or if the module specifies the subpage as `data.pagename` (which many modules do) and we're in an Appendix -- or other non-mainspace page. We used to make the latter an error but there are too many modules that do it, -- and substing on a nonexistent page is totally legit, and we don't actually need to be able to access the -- content of the page. local content = raw_title:getContent() -- Get the pagename. pagename = gsub(raw_title.subpageText, "^Unsupported titles/(.+)", function(m) insert(cats, "Unsupported titles") local title = (unsupported_titles or get_unsupported_titles())[m] if title then return title end -- Substitute pairs of "`". Those not used for escaping should be escaped as "`grave`", but might not be, -- so if a pair don't form a match, the closing "`" should become the opening "`" of the next match attempt. -- This has to be done manually, instead of using gsub. local open_pos = find(m, "`") if not open_pos then return m end title = {sub(m, 1, open_pos - 1)} while true do local close_pos = find(m, "`", open_pos + 1) if not close_pos then -- Add "`" plus any remaining characters. insert(title, sub(m, open_pos)) break end local escape = sub(m, open_pos, close_pos) local ch = (unsupported_characters or get_unsupported_characters())[escape] -- Match found, so substitute the character and move to the first "`" after the match if found, or -- otherwise return. if ch then insert(title, ch) local nxt_pos = close_pos + 1 open_pos = find(m, "`", nxt_pos) -- Add any characters between the match and the next "`" or end. if open_pos then insert(title, sub(m, nxt_pos, open_pos - 1)) else insert(title, sub(m, nxt_pos)) break end -- Match not found, so make the closing "`" the opening "`" of the next attempt. else -- Add the failed match, except for the closing "`". insert(title, sub(m, open_pos, close_pos - 1)) open_pos = close_pos end end return concat(title) end) -- Save pagename, as local variable will be destructively modified. data.pagename = pagename -- Decompose the pagename in Unicode normalization form D. data.decompose_pagename = toNFD(pagename) -- Explode the current page name into a character table, taking decomposed combining characters into account. local explode_pagename = {} local pagename_len = 0 local function explode(char) explode_pagename[char] = true pagename_len = pagename_len + 1 return "" end pagename = ugsub(pagename, comb_chars.combined_double, explode) pagename = gsub(ugsub(pagename, comb_chars.combined_single, explode), ".[\128-\191]*", explode) data.explode_pagename = explode_pagename data.pagename_len = pagename_len -- Generate DEFAULTSORT. data.encoded_pagename = encode_entities(data.pagename) data.pagename_defaultsort = get_lang("mul"):makeSortKey(data.encoded_pagename) frame:callParserFunction("DEFAULTSORT", data.pagename_defaultsort) data.raw_defaultsort = uupper(raw_title.text) -- Make `L2_list` and `L2_sections`, note raw wikitext use of {{DEFAULTSORT:}} and {{DISPLAYTITLE:}}, then add categories if any unwanted L1 headings are found, the L2 headings are in the wrong order, or they don't match a canonical language name. -- Note: HTML comments shouldn't be removed from `content` until after this step, as they can affect the result. do local L2_list, L2_list_len, L2_sections, sort_cache, prev, rc = {}, 0, {}, {} local new_cats, L2_wrong_order = {} local function get_weight(L2) if L2 == "Translingual" then return "\1" elseif L2 == "English" then return "\2" elseif match(L2, "^[%z\1-\b\14-!#-&(-,.-\127]+$") then return L2 end local weight = sort_cache[L2] if weight then return weight end weight = toNFC(ugsub(ugsub(toNFD(L2), "[" .. comb_chars_all .. "'\"ʻʼ]+", ""), "[%s%-]+", " ")) sort_cache[L2] = weight return weight end local function handle_heading(heading) local level = heading.level if level > 2 then return end local name = heading:get_name() -- heading:get_name() will return nil if there are any newline characters in the preprocessed heading name (e.g. from an expanded template). In such cases, the preprocessor section count still increments (since it's calculated pre-expansion), but the heading will fail, so the L2 count shouldn't be incremented. if name == nil then return end L2_list_len = L2_list_len + 1 L2_list[L2_list_len] = name L2_sections[heading.section] = name -- Also add any L1s, since they terminate the preceding L2, but add a maintenance category since it's probably a mistake. if level == 1 then new_cats["Pages with unwanted L1 headings"] = true end -- Check the heading is in the right order. -- FIXME: we need a more sophisticated sorting method which handles non-diacritic special characters (e.g. Magɨ). if prev and not ( L2_wrong_order or string_compare(get_weight(prev), get_weight(name)) ) then new_cats["Pages with language headings in the wrong order"] = true L2_wrong_order = true end -- Check it's a canonical language name. if not (langnames or get_langnames())[name] then new_cats["Pages with nonstandard language headings"] = true end prev = name end local function handle_template(template) local name = template:get_name() if name == "DEFAULTSORT:" then new_cats["Pages with DEFAULTSORT conflicts"] = true elseif name == "DISPLAYTITLE:" then new_cats["Pages with DISPLAYTITLE conflicts"] = true elseif name == "reconstructed" then rc = true end end if content then for node in parse(content):iterate_nodes() do local node_class = class_else_type(node) if node_class == "heading" then handle_heading(node) elseif node_class == "template" then handle_template(node) elseif node_class == "parameter" then new_cats["Pages with raw triple-brace template parameters"] = true end end end L2_list.n = L2_list_len data.L2_list = L2_list data.L2_sections = L2_sections insert(cats, get_category("Pages with entries")) insert(cats, get_category(format("Pages with %s entr%s", L2_list_len, L2_list_len == 1 and "y" or "ies"))) for cat in pairs(new_cats) do insert(cats, get_category(cat)) end if nsText == "Reconstruction" and not rc then local langname = match(full_raw_pagename, "^Reconstruction:([^/]+)/.") if langname then insert(cats, get_category(langname .. " entries missing Template:reconstructed")) end end end ------ 4. Parse page for maintenance categories. ------ -- Use of tab characters. if content and find(content, "\t", 1, true) then insert(cats, get_category("Pages with tab characters")) end -- Unencoded character(s) in title. local IDS = list_to_set{"⿰", "⿱", "⿲", "⿳", "⿴", "⿵", "⿶", "⿷", "⿸", "⿹", "⿺", "⿻", "⿼", "⿽", "⿾", "⿿", "㇯"} for char in pairs(explode_pagename) do if IDS[char] and char ~= data.pagename then insert(cats, "Terms containing unencoded characters") break end end -- Raw wikitext use of a topic or langname category. Also check if any raw sortkeys have been used. do local wikitext_topic_cat = {} local wikitext_langname_cat = {} local raw_sortkey -- If a raw sortkey has been found, add it to the relevant table. -- If there's no table (or the index is just `true`), create one first. local function add_cat_table(t, lang, sortkey) local t_lang = t[lang] if not sortkey then if not t_lang then t[lang] = true end return elseif t_lang == true or not t_lang then t_lang = {} t[lang] = t_lang end t_lang[uupper(decode_entities(sortkey))] = true end local function process_category(content, cat, colon, nxt) local pipe = find(cat, "|", colon + 1, true) -- Categories cannot end "|]]". if pipe == #cat then return end local title = new_title(pipe and sub(cat, 1, pipe - 1) or cat) if not (title and title.namespace == 14) then return end -- Get the sortkey (if any), then canonicalize category title. local sortkey = pipe and sub(cat, pipe + 1) or nil cat = title.text if sortkey then raw_sortkey = true -- If the sortkey contains "[", the first "]" of a final "]]]" is treated as part of the sortkey. if find(sortkey, "[", 1, true) and sub(content, nxt, nxt) == "]" then sortkey = sortkey .. "]" end end local code = match(cat, "^([%w%-.]+):") if code then add_cat_table(wikitext_topic_cat, code, sortkey) return end -- Split by word. cat = split(cat, " ", true, true) -- Formerly we looked for the language name anywhere in the category. This is simply wrong -- because there are no categories like 'Alsatian French lemmas' (only L2 languages -- have langname categories), but doing it this way wrongly catches things like [[Category:Shapsug Adyghe]] -- in [[Category:Adyghe entries with language name categories using raw markup]]. local n = #cat - 1 if n <= 0 then return end -- Go from longest to shortest and stop once we've found a language name. Going from shortest -- to longest or not stopping after a match risks falsely matching (e.g.) German Low German -- categories as German. repeat local name = concat(cat, " ", 1, n) if (langnames or get_langnames())[name] then add_cat_table(wikitext_langname_cat, name, sortkey) return end n = n - 1 until n == 0 end if content then -- Remove comments, then iterate over category links. content = remove_comments(content, "BOTH") local head = find(content, "[[", 1, true) while head do local close = find(content, "]]", head + 2, true) if not close then break end -- Make sure there are no intervening "[[" between head and close. local open = find(content, "[[", head + 2, true) while open and open < close do head = open open = find(content, "[[", head + 2, true) end local cat = sub(content, head + 2, close - 1) -- Locate the colon, and weed out most unwanted links. "[ _\128-\244]*" catches valid whitespace, and ensures any category links using the colon trick are ignored. We match all non-ASCII characters, as there could be multibyte spaces, and mw.title.new will filter out any remaining false-positives; this is a lot faster than running mw.title.new on every link. local colon = match(cat, "^[ _\128-\244]*[Cc][Aa][Tt][EeGgOoRrYy _\128-\244]*():") if colon then process_category(content, cat, colon, close + 2) end head = open end end data.wikitext_topic_cat = wikitext_topic_cat data.wikitext_langname_cat = wikitext_langname_cat if raw_sortkey then insert(cats, get_category("Pages with raw sortkeys")) end end return data end return export c380ic3kjsqy22c3blgwyio3y65tlzc میدیاویکی:Minerva.css 8 10778 36540 34306 2026-08-10T04:17:52Z Aram 9 NavToggle { float: right; > float: left; } بەپێی [[لێدوانی بەکارھێنەر:-Haryad#داڕێژە:trans-top|ئێرە]] 36540 css text/css /* Hiding this element must be done on this page because it is outside the ".mw-parser-output" class. Since TemplateStyles cannot be used to hide it, it must be added here. */ .page-Main_Page #firstHeading { display: none; /* NOT important */ } .NavContent table { margin-bottom: 0; } .tunk, .tneg, .tpos { vertical-align: 20%; font-size: 80%; } span.NavToggle { cursor: pointer; float: left; font-weight: normal; font-size: smaller; } .NavHead { cursor: pointer; min-height: 1.6em; font-weight: bold; line-height: 19px; padding: 2px; } .NavFrame { margin: 0px 0px -1px 0px; border: 1px solid #aaaaaa; padding: 1px; } .NavContent tbody { display: block; } #content ol dd { margin-left: 1em; } .mention, .mention-Latn, .use-with-mention, .ib-content { font-style: italic } li.senseid:target { background-color: #DEF } /* For RTL, unitalicized and vertical scripts: */ .Arab, .fa-Arab, .glk-Arab, .kk-Arab, .ks-Arab, .ku-Arab, .mzn-Arab, .ota-Arab, .pa-Arab, .ps-Arab, .sd-Arab, .tt-Arab, .ug-Arab, .ur-Arab, .ms-Arab, .Armi, .Avst, .Cprt, .Hebr, .Khar, .Phnx, .Samr, .Syrc { direction: rtl; unicode-bidi: embed; } .Arab, .Arab *, .fa-Arab, .fa-Arab *, .glk-Arab, .glk-Arab *, .kk-Arab, .kk-Arab *, .ks-Arab, .ks-Arab *, .ku-Arab, .ku-Arab *, .mzn-Arab, .mzn-Arab *, .ota-Arab, .ota-Arab *, .pa-Arab, .pa-Arab *, .ps-Arab, .ps-Arab *, .sd-Arab, .sd-Arab *, .tt-Arab, .tt-Arab *, .ug-Arab, .ug-Arab *, .ur-Arab, .ur-Arab *, .ms-Arab, .ms-Arab *, .Armi, .Armi *, .Armn, .Armn *, .Avst, .Avst *, .Bali, .Bali *, .Bamu, .Bamu *, .Batk, .Batk *, .Beng, .Beng *, .Bopo, .Bopo *, .Brah, .Brah *, .Brai, .Brai *, .Bugi, .Bugi *, .Buhd, .Buhd *, .Cakm, .Cakm *, .Cans, .Cans *, .Cari, .Cari *, .Cham, .Cham *, .Cher, .Cher *, .Copt, .Copt *, .Cprt, .Cprt *, .Cyrl, .Cyrl *, .Cyrs, .Cyrs *, i.Deva, .Dsrt, .Dsrt *, .Egyd, .Egyd *, .Egyp, .Egyp *, .Ethi, .Ethi *, .Geor, .Geor *, .Glag, .Glag *, .Goth, .Goth *, .Grek, .Grek *, .polytonic, .polytonic *, .Gujr, .Gujr *, .Guru, .Guru *, .Hani, .Hani * :not(:lang(vi)), .Hans, .Hans *, .Hant, .Hant *, .Hano, .Hano *, .Hebr, .Hebr *, .Hira, .Hira *, .Ital, .Ital *, .Java, .Java *, .Jpan, .Jpan *, .Kali, .Kali *, .Kana, .Kana *, .Olck, .Olck *, .Rjng, .Rjng *, .Khar, .Khar *, .Khmr, .Khmr *, .Knda, .Knda *, .Kore, .Kore *, .Hang, .Hang *, .CGK, .CGK *, .Kthi, .Kthi *, .Lana, .Lana *, .Laoo, .Laoo *, .Lepc, .Lepc *, .Limb, .Limb *, .Linb, .Linb *, .Lisu, .Lisu *, .Lyci, .Lyci *, .Lydi, .Lydi *, .Mand, .Mand *, .Mani, .Mani *, .Merc, .Merc *, .Mero, .Mero *, .Mlym, .Mlym *, .Mong, .Mong *, .Mtei, .Mtei *, .Mymr, .Mymr *, .Narb, .Narb *, .Nkoo, .Nkoo *, .Ogam, .Ogam *, .Orkh, .Orkh *, .Orya, .Orya *, .Osma, .Osma *, .Phag, .Phag *, .Phli, .Phli *, .Phlv, .Phlv *, .Phnx, .Phnx *, .Plrd, .Plrd *, .Prti, .Prti *, .Runr, .Runr *, .Samr, .Samr *, .Sarb, .Sarb *, .Saur, .Saur *, .Sgnw, .Sgnw *, .Shaw, .Shaw *, .Shrd, .Shrd *, .Sinh, .Sinh *, .Sora, .Sora *, .Sund, .Sund *, .Sylo, .Sylo *, .Syrc, .Syrc *, .Tagb, .Tagb *, .Tale, .Tale *, .Talu, .Talu *, .Taml, .Taml *, .Tang, .Tang *, .Tavt, .Tavt *, .Telu, .Telu *, .Tfng, .Tfng *, .Tglg, .Tglg *, .Thaa, .Thaa *, .Thai, .Thai *, .Tibt, .Tibt *, .xzh-Tibt, .xzh-Tibt *, .Ugar, .Ugar *, .Vaii, .Vaii *, .Xpeo, .Xpeo *, .Xsux, .Xsux *, .Yiii, .Yiii *, .Zmth, .Zmth *, .Zsym, .Zsym *, .IPA, .IPA *, .IPAchar, .IPAchar *, .musical, .musical * { font-style: normal; } .Mong, .Phag { -webkit-writing-mode: vertical-lr; -moz-writing-mode: vertical-lr; writing-mode: vertical-lr; layout-flow: vertical-ideographic; } .Tang { text-orientation: upright; -webkit-text-orientation: upright } /* hide redundant second interProject links */ .interProject { display: none; clear: both; border-top: 2px dotted #AAAAAA; margin-top: 2em; } .derivedterms, .term-list { -moz-column-count: 1 !important; -ms-column-count: 1 !important; -webkit-column-count: 1 !important; column-count: 1 !important; } /* allow inline hieroglyphs */ .mw-hiero-outer { display: inline-table; vertical-align: middle; font-size: 11px; } /* WikiHiero kludge */ .wikt-hierokludge { display: inline-block; } /* Support lists that automatically split into limited-width columns (for translations tables and the like): */ .multicolumn-list > ul { -webkit-column-width: 25em; -moz-column-width: 25em; -ms-column-width: 25em; column-width: 25em; column-gap: 20px; } .multicolumn-list-wide > ul { -webkit-column-width: 55em; -moz-column-width: 55em; -ms-column-width: 55em; column-width: 55em; column-gap: 20px; } .multicolumn-list-narrow > ul { -webkit-column-width: 22em; -moz-column-width: 22em; -ms-column-width: 22em; column-width: 22em; column-gap: 20px; } /* Horizontal rules */ body.ns-0 #mw-content-text .mw-parser-output > :not(.collapsible-block) + h2:not(:first-of-type), body.ns-0 #mw-content-text .mw-parser-output > .open-block + h2:not(:first-of-type) { border-top: 1px solid #a2a9b1; padding-top: calc(8px + 0.5em); margin-top: 8px; } body.ns-0 hr { display: none; } mx0093bfb9bbffqgi8c9w8wh77yx4ka لێدوانی بەکارھێنەر:-Haryad 3 11153 36488 36486 2026-08-09T15:45:16Z Ghybu 12 /* داڕێژە:trans-top */ وەڵامدانەوە 36488 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> ئەگەر بە دەستکاریکردنی ویکی ئاشنا نیت، ئەوا سەیرێکی [[:en:Help:How to edit a page|چۆنێتیی دەستکاریکردنی پەڕە]] بکە. پەڕەکە پێڕستێکی پوختە لەسەر ڕێنمایییە تەکنیکییەکانی ئەو شێوازی ویکییەی ئێمە لێرە بە کاری دەھێنین: بۆ نموونە، چۆن دەقێک تۆخ بکەیت یان بەستەرەکان دروست بکەیت. ئاسوودە بە لە ڕاھێنانکردن لە [[ویکیفەرھەنگ:خۆڵەپەتانێ|خۆڵەپەتانێ]]. ئەگەر حەزت لە پێشەکییەکی ھێواشترە، ئەوا [[ویکیفەرھەنگ:فێرکاری|فێرکارییەکی]] کورتمان ھەیە. لەوانەیە ئەم بەستەرانە وات لێ بکەن تا خۆت بە ویکیفەرھەنگ ئاشنا بکەیت: * [[ویکیفەرھەنگ:ڕێکخستنی سەروشە|ڕێکخستنی سەروشە]] سیاسەتێکی وردە لەسەر شێوازی ڕێکخستنی پەڕەی ویکیفەرھەنگ؛ فەرزە ھەموو سەروشەکان پەیڕەویی لێ بکەن. ئاسانترین ڕێگا بۆ ئەوەی دەست پێ بکەیت، ئەوەیە کە ناوەڕۆکی پەڕەیەکی سەروشەی تری ھەمان زمان لەبەر بگریتەوە و، ئینجا لەگەڵ ئەو پەڕەیەی کە دروستی دەکەیت بیگونجێنیت. * سەیری [[ویکیفەرھەنگ:ڕەچاوکردنی زمان|ڕەچاوکردنی زمان]] بکە تا زیاتر لەسەر چۆنێتیی دەستکاریکردنی زمانێکی دیاریکراو بزانیت. * [[ویکیفەرھەنگ:پێوەرەکانی قبووڵکردن|پێوەری قبووڵکردنەکەمان]] بەتەواوی باسی ئەو وشانە دەکات کە دەکرێت بۆ ویکیفەرھەنگ زیاد بکرێ؛ گرنگترین شت ئەوەیە کە ویکیفەرھەنگ تەنیا ئەو وشانە قبووڵ دەکات کە بە لای کەمەوە بۆ ماوەی ساڵێک تا ڕادەیەک بە شێوەیەکی بەرفراوان بە کار ھێنراون، و کە گومان ھەبوو داوای ئەو سەرچاوانە بکرێت کە بەکارھێنانیان دەسەلمێنێ. * ئەگەر پێشتر ئەزموونێکت لە دەستکاریکردنی پڕۆژەی [[w:دەستپێک|ویکیپیدیا]] ھەبووە، ئەوا لەوانەیە [[:en:Wiktionary:Wiktionary for Wikipedians|ڕێنمایی بۆ بەکارھێنەرانی ویکیپیدیا]] بە کەڵک بێت بۆت. * ئەگەر ھەر پرسیارێکت ھەیە، تکایە بیانبە [[ویکیفەرھەنگ:مێزی زانیاری]] یانیش لە پەڕەی وتووێژەکەم لە منیان بپرسە. * ھانت دەدەین تا [[: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) == [[داڕێژە:trans-top]] == {{Rtl}} Silav, Could you replace "right" with "left" here: [[MediaWiki:Common.css#L-569]]. Spas! {{Rtl/end}} [[بەکارھێنەر:Ghybu|Ghybu]] ([[لێدوانی بەکارھێنەر:Ghybu|لێدوان]]) ٠٠:٤٠، ٩ی ئابی ٢٠٢٦ (+03) :Silav Ghybu, bi xêr hatî :It is written as right because Sorani Kurdish writing is from RTL. If it is changed to left, the translation table will get messed up. [[User:-Haryad |<span style="color:black; font-size:16px;">'''ʜᴀʀʏᴀᴅ'''</span>]] <sub>[[User Talk: -Haryad |<span style="color:yellow; Background:black ">'''ᴛᴀʟᴋ'''</span>]]</sub> ٠٩:٣٨، ٩ی ئابی ٢٠٢٦ (+03) ::Look at the [[Template:trans-top]] table: currently, "[پیشان بدە ▼]" is written to the right of the table; this is incorrect—it should be placed at the far left of the table. Check the Sorani Wikipedia article [https://ckb.wikipedia.org/wiki/%D8%AF%D8%A7%DA%95%DB%8E%DA%98%DB%95:%D9%86%D9%88%D9%88%D8%B3%DB%95%D8%B1%D8%A7%D9%86_%D9%88_%D8%B4%D8%A7%D8%B9%DB%8C%D8%B1%D8%A7%D9%86%DB%8C_%D8%B3%DB%95%D8%AF%DB%95%DB%8C_%D8%A8%DB%8C%D8%B3%D8%AA%DB%95%D9%85%DB%8C_%D9%81%DB%95%DA%95%DB%95%D9%86%D8%B3%D8%A7]: it is located to the left of the title, not to the right. [[بەکارھێنەر:Ghybu|Ghybu]] ([[لێدوانی بەکارھێنەر:Ghybu|لێدوان]]) ١٨:٤٥، ٩ی ئابی ٢٠٢٦ (+03) g9uvyr78dpwij3epga79hwotrvy8l7n 36489 36488 2026-08-09T15:47:45Z Ghybu 12 /* داڕێژە:trans-top */ 36489 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> ئەگەر بە دەستکاریکردنی ویکی ئاشنا نیت، ئەوا سەیرێکی [[:en:Help:How to edit a page|چۆنێتیی دەستکاریکردنی پەڕە]] بکە. پەڕەکە پێڕستێکی پوختە لەسەر ڕێنمایییە تەکنیکییەکانی ئەو شێوازی ویکییەی ئێمە لێرە بە کاری دەھێنین: بۆ نموونە، چۆن دەقێک تۆخ بکەیت یان بەستەرەکان دروست بکەیت. ئاسوودە بە لە ڕاھێنانکردن لە [[ویکیفەرھەنگ:خۆڵەپەتانێ|خۆڵەپەتانێ]]. ئەگەر حەزت لە پێشەکییەکی ھێواشترە، ئەوا [[ویکیفەرھەنگ:فێرکاری|فێرکارییەکی]] کورتمان ھەیە. لەوانەیە ئەم بەستەرانە وات لێ بکەن تا خۆت بە ویکیفەرھەنگ ئاشنا بکەیت: * [[ویکیفەرھەنگ:ڕێکخستنی سەروشە|ڕێکخستنی سەروشە]] سیاسەتێکی وردە لەسەر شێوازی ڕێکخستنی پەڕەی ویکیفەرھەنگ؛ فەرزە ھەموو سەروشەکان پەیڕەویی لێ بکەن. ئاسانترین ڕێگا بۆ ئەوەی دەست پێ بکەیت، ئەوەیە کە ناوەڕۆکی پەڕەیەکی سەروشەی تری ھەمان زمان لەبەر بگریتەوە و، ئینجا لەگەڵ ئەو پەڕەیەی کە دروستی دەکەیت بیگونجێنیت. * سەیری [[ویکیفەرھەنگ:ڕەچاوکردنی زمان|ڕەچاوکردنی زمان]] بکە تا زیاتر لەسەر چۆنێتیی دەستکاریکردنی زمانێکی دیاریکراو بزانیت. * [[ویکیفەرھەنگ:پێوەرەکانی قبووڵکردن|پێوەری قبووڵکردنەکەمان]] بەتەواوی باسی ئەو وشانە دەکات کە دەکرێت بۆ ویکیفەرھەنگ زیاد بکرێ؛ گرنگترین شت ئەوەیە کە ویکیفەرھەنگ تەنیا ئەو وشانە قبووڵ دەکات کە بە لای کەمەوە بۆ ماوەی ساڵێک تا ڕادەیەک بە شێوەیەکی بەرفراوان بە کار ھێنراون، و کە گومان ھەبوو داوای ئەو سەرچاوانە بکرێت کە بەکارھێنانیان دەسەلمێنێ. * ئەگەر پێشتر ئەزموونێکت لە دەستکاریکردنی پڕۆژەی [[w:دەستپێک|ویکیپیدیا]] ھەبووە، ئەوا لەوانەیە [[:en:Wiktionary:Wiktionary for Wikipedians|ڕێنمایی بۆ بەکارھێنەرانی ویکیپیدیا]] بە کەڵک بێت بۆت. * ئەگەر ھەر پرسیارێکت ھەیە، تکایە بیانبە [[ویکیفەرھەنگ:مێزی زانیاری]] یانیش لە پەڕەی وتووێژەکەم لە منیان بپرسە. * ھانت دەدەین تا [[: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) == [[داڕێژە:trans-top]] == {{Rtl}} Silav, Could you replace "right" with "left" here: [[MediaWiki:Common.css#L-569]]. Spas! {{Rtl/end}} [[بەکارھێنەر:Ghybu|Ghybu]] ([[لێدوانی بەکارھێنەر:Ghybu|لێدوان]]) ٠٠:٤٠، ٩ی ئابی ٢٠٢٦ (+03) :Silav Ghybu, bi xêr hatî :It is written as right because Sorani Kurdish writing is from RTL. If it is changed to left, the translation table will get messed up. [[User:-Haryad |<span style="color:black; font-size:16px;">'''ʜᴀʀʏᴀᴅ'''</span>]] <sub>[[User Talk: -Haryad |<span style="color:yellow; Background:black ">'''ᴛᴀʟᴋ'''</span>]]</sub> ٠٩:٣٨، ٩ی ئابی ٢٠٢٦ (+03) {{Rtl}} ::Look at the [[Template:trans-top]] table: currently, "[پیشان بدە ▼]" is written to the right of the table; this is incorrect—it should be placed at the far left of the table. Check the Sorani Wikipedia article [https://ckb.wikipedia.org/wiki/%D8%AF%D8%A7%DA%95%DB%8E%DA%98%DB%95:%D9%86%D9%88%D9%88%D8%B3%DB%95%D8%B1%D8%A7%D9%86_%D9%88_%D8%B4%D8%A7%D8%B9%DB%8C%D8%B1%D8%A7%D9%86%DB%8C_%D8%B3%DB%95%D8%AF%DB%95%DB%8C_%D8%A8%DB%8C%D8%B3%D8%AA%DB%95%D9%85%DB%8C_%D9%81%DB%95%DA%95%DB%95%D9%86%D8%B3%D8%A7]: it is located to the left of the title, not to the right. [[بەکارھێنەر:Ghybu|Ghybu]] ([[لێدوانی بەکارھێنەر:Ghybu|لێدوان]]) ١٨:٤٥، ٩ی ئابی ٢٠٢٦ (+03) {{Rtl/end}} 30jbrl92o5rj5wxol5azxou7rgabi2q 36490 36489 2026-08-09T15:50:24Z Ghybu 12 /* داڕێژە:trans-top */ 36490 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> ئەگەر بە دەستکاریکردنی ویکی ئاشنا نیت، ئەوا سەیرێکی [[:en:Help:How to edit a page|چۆنێتیی دەستکاریکردنی پەڕە]] بکە. پەڕەکە پێڕستێکی پوختە لەسەر ڕێنمایییە تەکنیکییەکانی ئەو شێوازی ویکییەی ئێمە لێرە بە کاری دەھێنین: بۆ نموونە، چۆن دەقێک تۆخ بکەیت یان بەستەرەکان دروست بکەیت. ئاسوودە بە لە ڕاھێنانکردن لە [[ویکیفەرھەنگ:خۆڵەپەتانێ|خۆڵەپەتانێ]]. ئەگەر حەزت لە پێشەکییەکی ھێواشترە، ئەوا [[ویکیفەرھەنگ:فێرکاری|فێرکارییەکی]] کورتمان ھەیە. لەوانەیە ئەم بەستەرانە وات لێ بکەن تا خۆت بە ویکیفەرھەنگ ئاشنا بکەیت: * [[ویکیفەرھەنگ:ڕێکخستنی سەروشە|ڕێکخستنی سەروشە]] سیاسەتێکی وردە لەسەر شێوازی ڕێکخستنی پەڕەی ویکیفەرھەنگ؛ فەرزە ھەموو سەروشەکان پەیڕەویی لێ بکەن. ئاسانترین ڕێگا بۆ ئەوەی دەست پێ بکەیت، ئەوەیە کە ناوەڕۆکی پەڕەیەکی سەروشەی تری ھەمان زمان لەبەر بگریتەوە و، ئینجا لەگەڵ ئەو پەڕەیەی کە دروستی دەکەیت بیگونجێنیت. * سەیری [[ویکیفەرھەنگ:ڕەچاوکردنی زمان|ڕەچاوکردنی زمان]] بکە تا زیاتر لەسەر چۆنێتیی دەستکاریکردنی زمانێکی دیاریکراو بزانیت. * [[ویکیفەرھەنگ:پێوەرەکانی قبووڵکردن|پێوەری قبووڵکردنەکەمان]] بەتەواوی باسی ئەو وشانە دەکات کە دەکرێت بۆ ویکیفەرھەنگ زیاد بکرێ؛ گرنگترین شت ئەوەیە کە ویکیفەرھەنگ تەنیا ئەو وشانە قبووڵ دەکات کە بە لای کەمەوە بۆ ماوەی ساڵێک تا ڕادەیەک بە شێوەیەکی بەرفراوان بە کار ھێنراون، و کە گومان ھەبوو داوای ئەو سەرچاوانە بکرێت کە بەکارھێنانیان دەسەلمێنێ. * ئەگەر پێشتر ئەزموونێکت لە دەستکاریکردنی پڕۆژەی [[w:دەستپێک|ویکیپیدیا]] ھەبووە، ئەوا لەوانەیە [[:en:Wiktionary:Wiktionary for Wikipedians|ڕێنمایی بۆ بەکارھێنەرانی ویکیپیدیا]] بە کەڵک بێت بۆت. * ئەگەر ھەر پرسیارێکت ھەیە، تکایە بیانبە [[ویکیفەرھەنگ:مێزی زانیاری]] یانیش لە پەڕەی وتووێژەکەم لە منیان بپرسە. * ھانت دەدەین تا [[: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) == [[داڕێژە:trans-top]] == {{Rtl}} Silav, Could you replace "right" with "left" here: [[MediaWiki:Common.css#L-569]]. Spas! {{Rtl/end}} [[بەکارھێنەر:Ghybu|Ghybu]] ([[لێدوانی بەکارھێنەر:Ghybu|لێدوان]]) ٠٠:٤٠، ٩ی ئابی ٢٠٢٦ (+03) :Silav Ghybu, bi xêr hatî :It is written as right because Sorani Kurdish writing is from RTL. If it is changed to left, the translation table will get messed up. [[User:-Haryad |<span style="color:black; font-size:16px;">'''ʜᴀʀʏᴀᴅ'''</span>]] <sub>[[User Talk: -Haryad |<span style="color:yellow; Background:black ">'''ᴛᴀʟᴋ'''</span>]]</sub> ٠٩:٣٨، ٩ی ئابی ٢٠٢٦ (+03) {{Rtl}} ::Look at the [[Template:trans-top]] table: currently, "[بشارەوە ▲]/[پیشان بدە ▼]" is written to the right of the table; this is incorrect—it should be placed at the far left of the table. Check the Sorani Wikipedia article [https://ckb.wikipedia.org/wiki/%D8%AF%D8%A7%DA%95%DB%8E%DA%98%DB%95:%D9%86%D9%88%D9%88%D8%B3%DB%95%D8%B1%D8%A7%D9%86_%D9%88_%D8%B4%D8%A7%D8%B9%DB%8C%D8%B1%D8%A7%D9%86%DB%8C_%D8%B3%DB%95%D8%AF%DB%95%DB%8C_%D8%A8%DB%8C%D8%B3%D8%AA%DB%95%D9%85%DB%8C_%D9%81%DB%95%DA%95%DB%95%D9%86%D8%B3%D8%A7]: it is located to the left of the title, not to the right. [[بەکارھێنەر:Ghybu|Ghybu]] ([[لێدوانی بەکارھێنەر:Ghybu|لێدوان]]) ١٨:٤٥، ٩ی ئابی ٢٠٢٦ (+03) {{Rtl/end}} s32d5zm7xawr4xmfqcq6ikgjcmaq3f7 36491 36490 2026-08-09T16:03:41Z Ghybu 12 /* داڕێژە:trans-top */ 36491 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> ئەگەر بە دەستکاریکردنی ویکی ئاشنا نیت، ئەوا سەیرێکی [[:en:Help:How to edit a page|چۆنێتیی دەستکاریکردنی پەڕە]] بکە. پەڕەکە پێڕستێکی پوختە لەسەر ڕێنمایییە تەکنیکییەکانی ئەو شێوازی ویکییەی ئێمە لێرە بە کاری دەھێنین: بۆ نموونە، چۆن دەقێک تۆخ بکەیت یان بەستەرەکان دروست بکەیت. ئاسوودە بە لە ڕاھێنانکردن لە [[ویکیفەرھەنگ:خۆڵەپەتانێ|خۆڵەپەتانێ]]. ئەگەر حەزت لە پێشەکییەکی ھێواشترە، ئەوا [[ویکیفەرھەنگ:فێرکاری|فێرکارییەکی]] کورتمان ھەیە. لەوانەیە ئەم بەستەرانە وات لێ بکەن تا خۆت بە ویکیفەرھەنگ ئاشنا بکەیت: * [[ویکیفەرھەنگ:ڕێکخستنی سەروشە|ڕێکخستنی سەروشە]] سیاسەتێکی وردە لەسەر شێوازی ڕێکخستنی پەڕەی ویکیفەرھەنگ؛ فەرزە ھەموو سەروشەکان پەیڕەویی لێ بکەن. ئاسانترین ڕێگا بۆ ئەوەی دەست پێ بکەیت، ئەوەیە کە ناوەڕۆکی پەڕەیەکی سەروشەی تری ھەمان زمان لەبەر بگریتەوە و، ئینجا لەگەڵ ئەو پەڕەیەی کە دروستی دەکەیت بیگونجێنیت. * سەیری [[ویکیفەرھەنگ:ڕەچاوکردنی زمان|ڕەچاوکردنی زمان]] بکە تا زیاتر لەسەر چۆنێتیی دەستکاریکردنی زمانێکی دیاریکراو بزانیت. * [[ویکیفەرھەنگ:پێوەرەکانی قبووڵکردن|پێوەری قبووڵکردنەکەمان]] بەتەواوی باسی ئەو وشانە دەکات کە دەکرێت بۆ ویکیفەرھەنگ زیاد بکرێ؛ گرنگترین شت ئەوەیە کە ویکیفەرھەنگ تەنیا ئەو وشانە قبووڵ دەکات کە بە لای کەمەوە بۆ ماوەی ساڵێک تا ڕادەیەک بە شێوەیەکی بەرفراوان بە کار ھێنراون، و کە گومان ھەبوو داوای ئەو سەرچاوانە بکرێت کە بەکارھێنانیان دەسەلمێنێ. * ئەگەر پێشتر ئەزموونێکت لە دەستکاریکردنی پڕۆژەی [[w:دەستپێک|ویکیپیدیا]] ھەبووە، ئەوا لەوانەیە [[:en:Wiktionary:Wiktionary for Wikipedians|ڕێنمایی بۆ بەکارھێنەرانی ویکیپیدیا]] بە کەڵک بێت بۆت. * ئەگەر ھەر پرسیارێکت ھەیە، تکایە بیانبە [[ویکیفەرھەنگ:مێزی زانیاری]] یانیش لە پەڕەی وتووێژەکەم لە منیان بپرسە. * ھانت دەدەین تا [[: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) == [[داڕێژە:trans-top]] == {{Rtl}} Silav, Could you replace "right" with "left" here: [[MediaWiki:Common.css#L-569]]. Spas! {{Rtl/end}} [[بەکارھێنەر:Ghybu|Ghybu]] ([[لێدوانی بەکارھێنەر:Ghybu|لێدوان]]) ٠٠:٤٠، ٩ی ئابی ٢٠٢٦ (+03) :Silav Ghybu, bi xêr hatî :It is written as right because Sorani Kurdish writing is from RTL. If it is changed to left, the translation table will get messed up. [[User:-Haryad |<span style="color:black; font-size:16px;">'''ʜᴀʀʏᴀᴅ'''</span>]] <sub>[[User Talk: -Haryad |<span style="color:yellow; Background:black ">'''ᴛᴀʟᴋ'''</span>]]</sub> ٠٩:٣٨، ٩ی ئابی ٢٠٢٦ (+03) {{Rtl}} ::Look at the [[Template:trans-top]] table: currently, "[بشارەوە ▲]/[پیشان بدە ▼]" is written to the right of the table; this is incorrect—it should be placed at the far left of the table. Check the Sorani Wikipedia article [https://ckb.wikipedia.org/wiki/%D8%AF%D8%A7%DA%95%DB%8E%DA%98%DB%95:%D9%86%D9%88%D9%88%D8%B3%DB%95%D8%B1%D8%A7%D9%86_%D9%88_%D8%B4%D8%A7%D8%B9%DB%8C%D8%B1%D8%A7%D9%86%DB%8C_%D8%B3%DB%95%D8%AF%DB%95%DB%8C_%D8%A8%DB%8C%D8%B3%D8%AA%DB%95%D9%85%DB%8C_%D9%81%DB%95%DA%95%DB%95%D9%86%D8%B3%D8%A7]: it is located to the left of the title, not to the right. [[بەکارھێنەر:Ghybu|Ghybu]] ([[لێدوانی بەکارھێنەر:Ghybu|لێدوان]]) ١٨:٤٥، ٩ی ئابی ٢٠٢٦ (+03) :::PS: In the English Wiktionary (written left-to-right), it is also on the right: [https://en.wiktionary.org/wiki/MediaWiki:Gadget-Site.css#L-627] {{Rtl/end}} 2st6hmf8ye806gfl3nk0ye8rxj9oy7b 36526 36491 2026-08-09T20:03:22Z -Haryad 34 /* داڕێژە:trans-top */ وەڵام 36526 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> ئەگەر بە دەستکاریکردنی ویکی ئاشنا نیت، ئەوا سەیرێکی [[:en:Help:How to edit a page|چۆنێتیی دەستکاریکردنی پەڕە]] بکە. پەڕەکە پێڕستێکی پوختە لەسەر ڕێنمایییە تەکنیکییەکانی ئەو شێوازی ویکییەی ئێمە لێرە بە کاری دەھێنین: بۆ نموونە، چۆن دەقێک تۆخ بکەیت یان بەستەرەکان دروست بکەیت. ئاسوودە بە لە ڕاھێنانکردن لە [[ویکیفەرھەنگ:خۆڵەپەتانێ|خۆڵەپەتانێ]]. ئەگەر حەزت لە پێشەکییەکی ھێواشترە، ئەوا [[ویکیفەرھەنگ:فێرکاری|فێرکارییەکی]] کورتمان ھەیە. لەوانەیە ئەم بەستەرانە وات لێ بکەن تا خۆت بە ویکیفەرھەنگ ئاشنا بکەیت: * [[ویکیفەرھەنگ:ڕێکخستنی سەروشە|ڕێکخستنی سەروشە]] سیاسەتێکی وردە لەسەر شێوازی ڕێکخستنی پەڕەی ویکیفەرھەنگ؛ فەرزە ھەموو سەروشەکان پەیڕەویی لێ بکەن. ئاسانترین ڕێگا بۆ ئەوەی دەست پێ بکەیت، ئەوەیە کە ناوەڕۆکی پەڕەیەکی سەروشەی تری ھەمان زمان لەبەر بگریتەوە و، ئینجا لەگەڵ ئەو پەڕەیەی کە دروستی دەکەیت بیگونجێنیت. * سەیری [[ویکیفەرھەنگ:ڕەچاوکردنی زمان|ڕەچاوکردنی زمان]] بکە تا زیاتر لەسەر چۆنێتیی دەستکاریکردنی زمانێکی دیاریکراو بزانیت. * [[ویکیفەرھەنگ:پێوەرەکانی قبووڵکردن|پێوەری قبووڵکردنەکەمان]] بەتەواوی باسی ئەو وشانە دەکات کە دەکرێت بۆ ویکیفەرھەنگ زیاد بکرێ؛ گرنگترین شت ئەوەیە کە ویکیفەرھەنگ تەنیا ئەو وشانە قبووڵ دەکات کە بە لای کەمەوە بۆ ماوەی ساڵێک تا ڕادەیەک بە شێوەیەکی بەرفراوان بە کار ھێنراون، و کە گومان ھەبوو داوای ئەو سەرچاوانە بکرێت کە بەکارھێنانیان دەسەلمێنێ. * ئەگەر پێشتر ئەزموونێکت لە دەستکاریکردنی پڕۆژەی [[w:دەستپێک|ویکیپیدیا]] ھەبووە، ئەوا لەوانەیە [[:en:Wiktionary:Wiktionary for Wikipedians|ڕێنمایی بۆ بەکارھێنەرانی ویکیپیدیا]] بە کەڵک بێت بۆت. * ئەگەر ھەر پرسیارێکت ھەیە، تکایە بیانبە [[ویکیفەرھەنگ:مێزی زانیاری]] یانیش لە پەڕەی وتووێژەکەم لە منیان بپرسە. * ھانت دەدەین تا [[: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) == [[داڕێژە:trans-top]] == {{Rtl}} Silav, Could you replace "right" with "left" here: [[MediaWiki:Common.css#L-569]]. Spas! {{Rtl/end}} [[بەکارھێنەر:Ghybu|Ghybu]] ([[لێدوانی بەکارھێنەر:Ghybu|لێدوان]]) ٠٠:٤٠، ٩ی ئابی ٢٠٢٦ (+03) {{Rtl}} :Silav Ghybu, bi xêr hatî :It is written as right because Sorani Kurdish writing is from RTL. If it is changed to left, the translation table will get messed up. [[User:-Haryad |<span style="color:black; font-size:16px;">'''ʜᴀʀʏᴀᴅ'''</span>]] <sub>[[User Talk: -Haryad |<span style="color:yellow; Background:black ">'''ᴛᴀʟᴋ'''</span>]]</sub> ٠٩:٣٨، ٩ی ئابی ٢٠٢٦ (+03) {{Rtl/end}} {{Rtl}} ::Look at the [[Template:trans-top]] table: currently, "[بشارەوە ▲]/[پیشان بدە ▼]" is written to the right of the table; this is incorrect—it should be placed at the far left of the table. Check the Sorani Wikipedia article [https://ckb.wikipedia.org/wiki/%D8%AF%D8%A7%DA%95%DB%8E%DA%98%DB%95:%D9%86%D9%88%D9%88%D8%B3%DB%95%D8%B1%D8%A7%D9%86_%D9%88_%D8%B4%D8%A7%D8%B9%DB%8C%D8%B1%D8%A7%D9%86%DB%8C_%D8%B3%DB%95%D8%AF%DB%95%DB%8C_%D8%A8%DB%8C%D8%B3%D8%AA%DB%95%D9%85%DB%8C_%D9%81%DB%95%DA%95%DB%95%D9%86%D8%B3%D8%A7]: it is located to the left of the title, not to the right. [[بەکارھێنەر:Ghybu|Ghybu]] ([[لێدوانی بەکارھێنەر:Ghybu|لێدوان]]) ١٨:٤٥، ٩ی ئابی ٢٠٢٦ (+03) :::PS: In the English Wiktionary (written left-to-right), it is also on the right: [https://en.wiktionary.org/wiki/MediaWiki:Gadget-Site.css#L-627] {{Rtl/end}} {{Rtl}} ::::[[بەکارھێنەر:Aram|Aram]]: If you have time, check this out. [[User:-Haryad |<span style="color:black; font-size:16px;">'''ʜᴀʀʏᴀᴅ'''</span>]] <sub>[[User Talk: -Haryad |<span style="color:yellow; Background:black ">'''ᴛᴀʟᴋ'''</span>]]</sub> ٢٣:٠٣، ٩ی ئابی ٢٠٢٦ (+03) 8yz2gj4edq8uesqy1i00zjtc8xh0n8m 36542 36526 2026-08-10T04:26:27Z Aram 9 /* داڕێژە:trans-top */ reply 36542 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> ئەگەر بە دەستکاریکردنی ویکی ئاشنا نیت، ئەوا سەیرێکی [[:en:Help:How to edit a page|چۆنێتیی دەستکاریکردنی پەڕە]] بکە. پەڕەکە پێڕستێکی پوختە لەسەر ڕێنمایییە تەکنیکییەکانی ئەو شێوازی ویکییەی ئێمە لێرە بە کاری دەھێنین: بۆ نموونە، چۆن دەقێک تۆخ بکەیت یان بەستەرەکان دروست بکەیت. ئاسوودە بە لە ڕاھێنانکردن لە [[ویکیفەرھەنگ:خۆڵەپەتانێ|خۆڵەپەتانێ]]. ئەگەر حەزت لە پێشەکییەکی ھێواشترە، ئەوا [[ویکیفەرھەنگ:فێرکاری|فێرکارییەکی]] کورتمان ھەیە. لەوانەیە ئەم بەستەرانە وات لێ بکەن تا خۆت بە ویکیفەرھەنگ ئاشنا بکەیت: * [[ویکیفەرھەنگ:ڕێکخستنی سەروشە|ڕێکخستنی سەروشە]] سیاسەتێکی وردە لەسەر شێوازی ڕێکخستنی پەڕەی ویکیفەرھەنگ؛ فەرزە ھەموو سەروشەکان پەیڕەویی لێ بکەن. ئاسانترین ڕێگا بۆ ئەوەی دەست پێ بکەیت، ئەوەیە کە ناوەڕۆکی پەڕەیەکی سەروشەی تری ھەمان زمان لەبەر بگریتەوە و، ئینجا لەگەڵ ئەو پەڕەیەی کە دروستی دەکەیت بیگونجێنیت. * سەیری [[ویکیفەرھەنگ:ڕەچاوکردنی زمان|ڕەچاوکردنی زمان]] بکە تا زیاتر لەسەر چۆنێتیی دەستکاریکردنی زمانێکی دیاریکراو بزانیت. * [[ویکیفەرھەنگ:پێوەرەکانی قبووڵکردن|پێوەری قبووڵکردنەکەمان]] بەتەواوی باسی ئەو وشانە دەکات کە دەکرێت بۆ ویکیفەرھەنگ زیاد بکرێ؛ گرنگترین شت ئەوەیە کە ویکیفەرھەنگ تەنیا ئەو وشانە قبووڵ دەکات کە بە لای کەمەوە بۆ ماوەی ساڵێک تا ڕادەیەک بە شێوەیەکی بەرفراوان بە کار ھێنراون، و کە گومان ھەبوو داوای ئەو سەرچاوانە بکرێت کە بەکارھێنانیان دەسەلمێنێ. * ئەگەر پێشتر ئەزموونێکت لە دەستکاریکردنی پڕۆژەی [[w:دەستپێک|ویکیپیدیا]] ھەبووە، ئەوا لەوانەیە [[:en:Wiktionary:Wiktionary for Wikipedians|ڕێنمایی بۆ بەکارھێنەرانی ویکیپیدیا]] بە کەڵک بێت بۆت. * ئەگەر ھەر پرسیارێکت ھەیە، تکایە بیانبە [[ویکیفەرھەنگ:مێزی زانیاری]] یانیش لە پەڕەی وتووێژەکەم لە منیان بپرسە. * ھانت دەدەین تا [[: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) == [[داڕێژە:trans-top]] == {{Rtl}} Silav, Could you replace "right" with "left" here: [[MediaWiki:Common.css#L-569]]. Spas! [[بەکارھێنەر:Ghybu|Ghybu]] ([[لێدوانی بەکارھێنەر:Ghybu|لێدوان]]) ٠٠:٤٠، ٩ی ئابی ٢٠٢٦ (+03) :Silav Ghybu, bi xêr hatî :It is written as right because Sorani Kurdish writing is from RTL. If it is changed to left, the translation table will get messed up. [[User:-Haryad |<span style="color:black; font-size:16px;">'''ʜᴀʀʏᴀᴅ'''</span>]] <sub>[[User Talk: -Haryad |<span style="color:yellow; Background:black ">'''ᴛᴀʟᴋ'''</span>]]</sub> ٠٩:٣٨، ٩ی ئابی ٢٠٢٦ (+03) ::Look at the [[Template:trans-top]] table: currently, "[بشارەوە ▲]/[پیشان بدە ▼]" is written to the right of the table; this is incorrect—it should be placed at the far left of the table. Check the Sorani Wikipedia article [https://ckb.wikipedia.org/wiki/%D8%AF%D8%A7%DA%95%DB%8E%DA%98%DB%95:%D9%86%D9%88%D9%88%D8%B3%DB%95%D8%B1%D8%A7%D9%86_%D9%88_%D8%B4%D8%A7%D8%B9%DB%8C%D8%B1%D8%A7%D9%86%DB%8C_%D8%B3%DB%95%D8%AF%DB%95%DB%8C_%D8%A8%DB%8C%D8%B3%D8%AA%DB%95%D9%85%DB%8C_%D9%81%DB%95%DA%95%DB%95%D9%86%D8%B3%D8%A7]: it is located to the left of the title, not to the right. [[بەکارھێنەر:Ghybu|Ghybu]] ([[لێدوانی بەکارھێنەر:Ghybu|لێدوان]]) ١٨:٤٥، ٩ی ئابی ٢٠٢٦ (+03) :::PS: In the English Wiktionary (written left-to-right), it is also on the right: [https://en.wiktionary.org/wiki/MediaWiki:Gadget-Site.css#L-627] ::::[[بەکارھێنەر:Aram|Aram]]: If you have time, check this out. [[User:-Haryad |<span style="color:black; font-size:16px;">'''ʜᴀʀʏᴀᴅ'''</span>]] <sub>[[User Talk: -Haryad |<span style="color:yellow; Background:black ">'''ᴛᴀʟᴋ'''</span>]]</sub> ٢٣:٠٣، ٩ی ئابی ٢٠٢٦ (+03) :::::Hey -Haryad and {{وب|Ghybu}} {{کرا|Done!}} To see the changes, please [https://ckb.wiktionary.org/w/index.php?title=%D8%AA%D8%A7%DB%8C%D8%A8%DB%95%D8%AA%3A%D8%A8%DB%95%D8%B4%D8%AF%D8%A7%D8%B1%DB%8C%DB%8C%DB%95%DA%A9%D8%A7%D9%86&target=Aram&namespace=all&tagfilter=&start=2026-08-10&end=2026-08-10&limit=50 check the edits I've made today]. Thanks! [[بەکارھێنەر:Aram|Aram]] ([[لێدوانی بەکارھێنەر:Aram|لێدوان]]) ٠٧:٢٦، ١٠ی ئابی ٢٠٢٦ (+03) {{Rtl/end}} 9scseh0agv0fdj3yu1gnxbcpc2ka4vl کەوتن 0 11315 36529 36182 2026-08-09T20:13:58Z Ghybu 12 /* کوردیی ناوەندی */ 36529 wikitext text/x-wiki == کوردیی ناوەندی == === وشەڕەتناسی === #'''کەوتن''': کەفتن، کەڤتن، کەفتین، کەتین، کەتن، هەمان. هەورامی (کەوتەی)، زازاکی (کەوتش). ====چاوگ==== {{ckb-verb}} # داهاتن، وەشین # ڕوودان # بوون {| class="wikitable mw-collapsible mw-collapsed" |+گەردانکردن ! ! colspan="4" |-کەوت- ! colspan="4" |-کەو- |- !جێناو !ڕابردووی نزیک !ڕابردووی تەواو !ڕابردووی بەردەوام !ڕابردووی دوور !ڕانەبردوو !دانانی !داخوازی !نەرێنی |- |من |کەوتم |کەوتوومە |دەکەوتم |کەوتبووم |دەکەوم |بکەوم | | |- |تۆ |کەوتیت |کەوتوویتە |دەکەوتیت |کەوتبوویت |دەکەویت |بکەویت |بکەوە |مەکەوە |- |ئەو |کەوت |کەوتووە |دەکەوت |کەوتبوو |دەکەوێت |بکەوێت | | |- |ئێمە |کەوتین |کەوتووین |دەکەوتین |کەوتبووین |دەکەوین |بکەوین | | |- |ئێوە |کەوتن |کەوتوون |دەکەوتن |کەوتبوون |دەکەون |بکەون |بکەون |مەکەون |- |ئەوان |کەوتن |کەوتوون |دەکەوتن |کەوتبوون |دەکەون |بکەون | | |} {| class="wikitable mw-collapsible mw-collapsed" |+داڕشتن !ناوی بکەری !ناوی بەرکاری !ناوی چاوگی !ناوی شوێن !ناوی ئامێر !هاوەڵناوی بکەری !هاوەڵناوی بەرکاری !هاوەڵناوی لێهاتوویی |- |کەوەر |کەوتوو |کەوتن |کەوگە |کەوە |بکەو |کەوێنراو |کەوتنی |- |کەفتگ |کەوتە |کەوتار |کەوتگە |کەوک |کەوندە | |کەوەن |- | |کەفتگ |کەومان |کەوتمان |کەڤۆک |کەڤۆک | |کەوتەنی |- | |کەفتی |کەوشت |کەوتارگە |کەوەک |کەوەک | |کەوتەمەنی |- | | | |کەوتووگە |کەوەنگ |کەوتار | |کەواک |- | | | | | |کەوتەوار | | |} ===وەرگێڕان=== {{وەرگێڕان-سەر| داهاتن، بە زەویدا دران.}} * ئینگلیزی: {{t+|en|to fall}} {{وەرگێڕان-بن}} <!-- close {{multitrans}} --> iq3ct3qkfv4fic3b52528357dckkta9 36533 36529 2026-08-09T20:44:51Z Ghybu 12 /* کوردیی ناوەندی */ format 36533 wikitext text/x-wiki == کوردیی ناوەندی == === وشەڕەتناسی === #'''کەوتن''': کەفتن، کەڤتن، کەفتین، کەتین، کەتن، هەمان. هەورامی (کەوتەی)، زازاکی (کەوتش). ====چاوگ==== {{ckb-verb}} # داهاتن، وەشین # ڕوودان # بوون {| class="wikitable mw-collapsible mw-collapsed" |- | colspan="9" style="padding-left:1em"| گەردانکردن &nbsp;&nbsp; |- ! ! colspan="4" |-کەوت- ! colspan="4" |-کەو- |- !جێناو !ڕابردووی نزیک !ڕابردووی تەواو !ڕابردووی بەردەوام !ڕابردووی دوور !ڕانەبردوو !دانانی !داخوازی !نەرێنی |- |من |کەوتم |کەوتوومە |دەکەوتم |کەوتبووم |دەکەوم |بکەوم | | |- |تۆ |کەوتیت |کەوتوویتە |دەکەوتیت |کەوتبوویت |دەکەویت |بکەویت |بکەوە |مەکەوە |- |ئەو |کەوت |کەوتووە |دەکەوت |کەوتبوو |دەکەوێت |بکەوێت | | |- |ئێمە |کەوتین |کەوتووین |دەکەوتین |کەوتبووین |دەکەوین |بکەوین | | |- |ئێوە |کەوتن |کەوتوون |دەکەوتن |کەوتبوون |دەکەون |بکەون |بکەون |مەکەون |- |ئەوان |کەوتن |کەوتوون |دەکەوتن |کەوتبوون |دەکەون |بکەون | | |} {| class="wikitable mw-collapsible mw-collapsed" |- | colspan="9" style="padding-left:1em"| داڕشتن &nbsp;&nbsp; |- !ناوی بکەری !ناوی بەرکاری !ناوی چاوگی !ناوی شوێن !ناوی ئامێر !هاوەڵناوی بکەری !هاوەڵناوی بەرکاری !هاوەڵناوی لێهاتوویی |- |کەوەر |کەوتوو |کەوتن |کەوگە |کەوە |بکەو |کەوێنراو |کەوتنی |- |کەفتگ |کەوتە |کەوتار |کەوتگە |کەوک |کەوندە | |کەوەن |- | |کەفتگ |کەومان |کەوتمان |کەڤۆک |کەڤۆک | |کەوتەنی |- | |کەفتی |کەوشت |کەوتارگە |کەوەک |کەوەک | |کەوتەمەنی |- | | | |کەوتووگە |کەوەنگ |کەوتار | |کەواک |- | | | | | |کەوتەوار | | |} ===وەرگێڕان=== {{وەرگێڕان-سەر| داهاتن، بە زەویدا دران.}} * ئینگلیزی: {{t+|en|to fall}} {{وەرگێڕان-بن}} <!-- close {{multitrans}} --> ac5bkjkwcb3bnvhha244j94b51qvxkp سیب 0 11415 36508 2026-08-09T17:35:55Z Ghybu 12 پەڕەی دروست کرد بە «== فارسی == === وشەڕەتناسی === {{واڵا}} ===ناو=== {{head|ar|ناو|tr=sēb}} # [[سێو]]»ەوە 36508 wikitext text/x-wiki == فارسی == === وشەڕەتناسی === {{واڵا}} ===ناو=== {{head|ar|ناو|tr=sēb}} # [[سێو]] 1bjhvwgor2egz99ykc3lt31ujuve2ap 36509 36508 2026-08-09T17:36:10Z Ghybu 12 /* فارسی */ 36509 wikitext text/x-wiki == فارسی == === وشەڕەتناسی === {{واڵا}} ===ناو=== {{head|fa|ناو|tr=sēb}} # [[سێو]] kylqmv0ixa35bg3v1c2n015wy6z0asa 36519 36509 2026-08-09T18:30:27Z Ghybu 12 /* فارسی */ 36519 wikitext text/x-wiki == فارسی == ===ناو=== {{head|fa|ناو|tr=golâbi}} # [[ھەرمێ]] 7twatp17lg2ejddovhl0xvuyh8iy6yf 36520 36519 2026-08-09T18:31:28Z Ghybu 12 پووچەڵکردنەوەی پێداچوونەوەی [[Special:Diff/36519|36519]] لە لایەن [[Special:Contributions/Ghybu|Ghybu]] ([[User talk:Ghybu|لێدوان]]) 36520 wikitext text/x-wiki == فارسی == === وشەڕەتناسی === {{واڵا}} ===ناو=== {{head|fa|ناو|tr=sēb}} # [[سێو]] kylqmv0ixa35bg3v1c2n015wy6z0asa ھەرمێ 0 11416 36511 2026-08-09T18:13:49Z Ghybu 12 پەڕەی دروست کرد بە «== کوردیی ناوەندی == [[File:Bartlett pear.jpg|thumb|'''ھەرمێ''']] === وشەڕەتناسی === {{واڵا}} === ناو === #جۆرێکی [[میوە|میوەیە]]. ==== وەرگێڕانەکان ==== {{وەرگێڕان-سەر|میوەیە}} * ئینگلیزی: {{t+|en|pear}} * تورکی: {{t+|tr|armut}} * عەرەبی: {{t+|ar|كُمَّثْرَى}} * فارسی: {{t+|fa|گلابی|tr=golâbi}} {{وەرگێڕان-بن}}»ەوە 36511 wikitext text/x-wiki == کوردیی ناوەندی == [[File:Bartlett pear.jpg|thumb|'''ھەرمێ''']] === وشەڕەتناسی === {{واڵا}} === ناو === #جۆرێکی [[میوە|میوەیە]]. ==== وەرگێڕانەکان ==== {{وەرگێڕان-سەر|میوەیە}} * ئینگلیزی: {{t+|en|pear}} * تورکی: {{t+|tr|armut}} * عەرەبی: {{t+|ar|كُمَّثْرَى}} * فارسی: {{t+|fa|گلابی|tr=golâbi}} {{وەرگێڕان-بن}} h57ticwumzvi5egxwzyq6w5aroym2x4 36522 36511 2026-08-09T18:39:32Z Ghybu 12 /* کوردیی ناوەندی */ 36522 wikitext text/x-wiki == کوردیی ناوەندی == [[File:Bartlett pear.jpg|thumb|'''ھەرمێ''']] === وشەڕەتناسی === {{واڵا}} === ناو === {{ckb-noun}} #جۆرێکی [[میوە|میوەیە]]. ==== وەرگێڕانەکان ==== {{وەرگێڕان-سەر|میوەیە}} * ئینگلیزی: {{t+|en|pear}} * تورکی: {{t+|tr|armut}} * عەرەبی: {{t+|ar|كُمَّثْرَى}} * فارسی: {{t+|fa|گلابی|tr=golâbi}} {{وەرگێڕان-بن}} 9pi50wvhuwbklim6u92y2hrf6od8k6y كمثرى 0 11417 36518 2026-08-09T18:28:42Z Ghybu 12 پەڕەی دروست کرد بە «== عەرەبی == ===ناو=== {{head|ar|ناو|head=كُمَّثْرَى|g=f}} # [[ھەرمێ]]»ەوە 36518 wikitext text/x-wiki == عەرەبی == ===ناو=== {{head|ar|ناو|head=كُمَّثْرَى|g=f}} # [[ھەرمێ]] l7awuqn5r3prcbn00aqaro2vnxuni05 گلابی 0 11418 36521 2026-08-09T18:31:39Z Ghybu 12 پەڕەی دروست کرد بە «== فارسی == ===ناو=== {{head|fa|ناو|tr=golâbi}} # [[ھەرمێ]]»ەوە 36521 wikitext text/x-wiki == فارسی == ===ناو=== {{head|fa|ناو|tr=golâbi}} # [[ھەرمێ]] 7twatp17lg2ejddovhl0xvuyh8iy6yf ketin 0 11419 36531 2026-08-09T20:15:37Z Ghybu 12 پەڕەی دروست کرد بە «== کرمانجی == === چاوگ === {{kmr-verb}} # [[کەوتن]]»ەوە 36531 wikitext text/x-wiki == کرمانجی == === چاوگ === {{kmr-verb}} # [[کەوتن]] 3e5fxsvrb4j2lx1rsqn3ubyifk1yzxk بەکارھێنەر:Ghybu/common.css 2 11420 36534 2026-08-10T03:56:38Z Ghybu 12 پەڕەی دروست کرد بە «/* From ckb.wikipedia */ /* Default styling for HTML elements */ body { /* generally a sans-serif font should be here */ font-family: Tahoma, 'DejaVu Sans', 'Droid Arabic Naskh', sans-serif; } .mw-body h1, .mw-body h2, .mw-body #firstHeading, .mw-body .mw-editsection { /* generally a serif font should be here but Arial is not acceptable for Kurdish */ font-family: Tahoma, 'Droid Arabic Naskh', 'DejaVu Sans', serif; }»ەوە 36534 css text/css /* From ckb.wikipedia */ /* Default styling for HTML elements */ body { /* generally a sans-serif font should be here */ font-family: Tahoma, 'DejaVu Sans', 'Droid Arabic Naskh', sans-serif; } .mw-body h1, .mw-body h2, .mw-body #firstHeading, .mw-body .mw-editsection { /* generally a serif font should be here but Arial is not acceptable for Kurdish */ font-family: Tahoma, 'Droid Arabic Naskh', 'DejaVu Sans', serif; } 1yrhjxgs0j8zdwj6itcoviwdtak61dn