ویکیفەرھەنگ
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 .. " " .. 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 .. " " .. 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"| گەردانکردن
|-
!
! colspan="4" |-کرد-
! colspan="4" |-کە-
|-
!جێناو
!ڕابردووی نزیک
!ڕابردووی تەواو
!ڕابردووی بەردەوام
!ڕابردووی دوور
!ڕانەبردوو
!دانانی
!داخوازی
!نەرێنی
|-
|من
|کردم
|کردوومە
|دەمکرد
|کردبووم
|دەکەم
|بکەم
|
|
|-
|تۆ
|کردت
|کردووتە
|دەتکرد
|کردبووت
|دەکەیت
|بکەیت
|بکە
|مەکە
|-
|ئەو
|کردی
|کردوویە
|دەیکرد
|کردبووی
|دەکات
|بکات
|
|
|-
|ئێمە
|کردمان
|کردوومانە
|دەمانکرد
|کردبوومان
|دەکەین
|بکەین
|
|
|-
|ئێوە
|کردتان
|کردووتانە
|دەتانکرد
|کردبووتان
|دەکەن
|بکەن
|بکەن
|مەکەن
|-
|ئەوان
|کردیان
|کردوویانە
|دەیانکرد
|کردبوویان
|دەکەن
|بکەن
|
|
|}
{| class="wikitable sortable mw-collapsible"
|-
| colspan="9" style="padding-left:1em"| داڕشتن
|-
!ناوی بکەری
!ناوی بەرکاری
!ناوی چاوگی
!ناوی شوێن
!ناوی ئامێر
!هاوەڵناوی بکەری
!هاوەڵناوی بەرکاری
!هاوەڵناوی لێهاتوویی
|-
|بکەر
|کردوو
|کردن
|کردگە
|کەیەک
|بکەر
|کراو
|کردنی
|-
|
|کردە
|کردار
|کرگە
|کەیەنگ
|کەیندە
|
|کەیەن
|-
|
|کردگ
|کەیمان/کرمان
|کردمان
|پێکەر
|کەیۆک
|
|کردەنی
|-
|
|کردی
|کرشت/کەیشت
|کردارگە
|
|کەیەک
|
|کردەمەنی
|-
|
|
|
|کردووگە
|
|کردار
|
|
|-
|
|
|
|
|
|کەیناک
|
|
|-
|
|
|
|
|
|کردەوار
|
|
|}
{{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"| گەردانکردن
|-
!
! colspan="4" |-کەوت-
! colspan="4" |-کەو-
|-
!جێناو
!ڕابردووی نزیک
!ڕابردووی تەواو
!ڕابردووی بەردەوام
!ڕابردووی دوور
!ڕانەبردوو
!دانانی
!داخوازی
!نەرێنی
|-
|من
|کەوتم
|کەوتوومە
|دەکەوتم
|کەوتبووم
|دەکەوم
|بکەوم
|
|
|-
|تۆ
|کەوتیت
|کەوتوویتە
|دەکەوتیت
|کەوتبوویت
|دەکەویت
|بکەویت
|بکەوە
|مەکەوە
|-
|ئەو
|کەوت
|کەوتووە
|دەکەوت
|کەوتبوو
|دەکەوێت
|بکەوێت
|
|
|-
|ئێمە
|کەوتین
|کەوتووین
|دەکەوتین
|کەوتبووین
|دەکەوین
|بکەوین
|
|
|-
|ئێوە
|کەوتن
|کەوتوون
|دەکەوتن
|کەوتبوون
|دەکەون
|بکەون
|بکەون
|مەکەون
|-
|ئەوان
|کەوتن
|کەوتوون
|دەکەوتن
|کەوتبوون
|دەکەون
|بکەون
|
|
|}
{| class="wikitable mw-collapsible mw-collapsed"
|-
| colspan="9" style="padding-left:1em"| داڕشتن
|-
!ناوی بکەری
!ناوی بەرکاری
!ناوی چاوگی
!ناوی شوێن
!ناوی ئامێر
!هاوەڵناوی بکەری
!هاوەڵناوی بەرکاری
!هاوەڵناوی لێهاتوویی
|-
|کەوەر
|کەوتوو
|کەوتن
|کەوگە
|کەوە
|بکەو
|کەوێنراو
|کەوتنی
|-
|کەفتگ
|کەوتە
|کەوتار
|کەوتگە
|کەوک
|کەوندە
|
|کەوەن
|-
|
|کەفتگ
|کەومان
|کەوتمان
|کەڤۆک
|کەڤۆک
|
|کەوتەنی
|-
|
|کەفتی
|کەوشت
|کەوتارگە
|کەوەک
|کەوەک
|
|کەوتەمەنی
|-
|
|
|
|کەوتووگە
|کەوەنگ
|کەوتار
|
|کەواک
|-
|
|
|
|
|
|کەوتەوار
|
|
|}
===وەرگێڕان===
{{وەرگێڕان-سەر| داهاتن، بە زەویدا دران.}}
* ئینگلیزی: {{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