وحدة:ar-verb-form
تعرض هذه الوحدة صفحة صيغة من صيغ تصريف فعل عربي، مثل رُمِيَتْ واُكْتُبِي واِرْمِ. ولا تُستدعى مباشرة، بل من خلال قالب:صيغة فعل، وتوثيق وسائطها هناك.
كيف تعمل
[عدل]تتلقى الوحدة الجذر والوزن لا غير. فتُصرِّف الفعل بـوحدة:ar-verb، ثم تُقابل عنوان الصفحة بكل صيغة مولَّدة، فتعرف من تلقاء نفسها أي خانة يسمّيها العنوان.
ولذلك أثران:
- لا تنطق الصفحة بالنحو، فلا يمكن أن تنطق به خطأً. فلو صُحِّحت وحدة:ar-verb أُعيد عرض الصفحات كلها على الصواب.
- عنوانٌ لم يعد يطابق خانةً يقع في تصنيف:صفحات صيغ أفعال بلا مطابقة بدل أن يعرض نحوا قديما بصمت.
ما تُولِّده
[عدل]- صيغة فعل — البطاقة (الصيغة، والفعل الذي تعود إليه، والجذر والوزن ووزن الصيغة)، وتحتها سطر التعريف النحوي.
- النطق — من
{{أصد-عر}}، ولا يظهر عنوانه إن لم يكن للصفحة نطق. - التصريفات — جدول قالب:تصريف كاملا ومفتوحا، مظللةً فيه الخاناتُ التي تحمل الصفحة.
- صيغ تشارك الرسم — سائر الصيغ التي تشارك الصفحة رسمَها، ولا يظهر عنوانه إن لم تكن ثَمَّ صيغة أخرى.
ثم التصنيفات النحوية والصرفية وتصنيف الجذر.
المعاني والشواهد والمراجع تبقى في صفحة الفعل وحدها؛ صفحة الصيغة تحيل إليها ولا تكررها.
تسمية الوجه
[عدل]الجدول المبسط — وهو ما يعرضه قالب:تصريف افتراضا — لا يحمل غير الماضي والمضارع والأمر مبنيةً للمعلوم، ومضارعُه هو المرفوع. فصيغةٌ يجدها القارئ فيه لا تُقابَل بشيء، ويكون ذكر «المرفوع» فضلا؛ فـتُحْسِبَانِ هي «المضارع» لا غير.
ولا يُذكر المرفوع إلا حيث يُفيد: في المبني للمجهول، وهو ما لا يعرضه إلا الجدول الكامل (يُقَالُ في مقابل يُقَالَ ويُقَلْ)، وفي صيغةٍ تفترق أوجهها (يَكْتُبُوا منصوبٌ ومجزوم، في مقابل يَكْتُبُونَ). فإذا اتحدت الأوجه الثلاثة في لفظ واحد، كـ«يَكْتُبْنَ»، عاد الاسم «المضارع».
الصيانة
[عدل]- تصنيف:صفحات صيغ أفعال بلا مطابقة — صفحات لم يطابق عنوانُها خانةً، أو استُعملت فيها قيمةٌ غير معروفة في الوسيط
قسم.
انظر أيضا
[عدل]- قالب:صيغة فعل — وفيه توثيق الوسائط
- وحدة:ar-verb
- تصنيف:صيغ أفعال عربية
-- Module:ar-verb-form
-- By: Wael (User:ForzaGreen)
--
-- Renders a conjugated-form (non-lemma) page for an Arabic verb, e.g. أَكَلْتُ.
--
-- The page passes only the verb's root and morphological pattern. This module
-- re-conjugates the verb through Module:ar-verb and works out for itself which
-- slots the page title represents, so the wikitext never states the grammar and
-- therefore can never state it wrongly. When Module:ar-verb is corrected, every
-- form page re-renders; a title that no longer matches any slot lands in
-- [[تصنيف:صفحات صيغ أفعال بلا مطابقة]] instead of silently showing stale grammar.
--
-- {{صيغة فعل|جذر=أكل|وزن=فعَل يفعُل}}
--
-- A form belonging to several verbs repeats the pair: |جذر2=|وزن2=
--
-- Parameters:
-- جذر, وزن required
-- جذر2 … up to MAX_VERBS additional verbs
-- صفحة override the page title (documentation and tests)
-- قسم render one section instead of the whole page, so that a
-- contributor can split a page apart and write between the
-- sections: صيغة فعل | النطق | التصريفات | الرسم. Absent = الكل.
local p = {}
local ar_verb = require("Module:ar-verb")
local u = mw.ustring.char
local MAX_VERBS = 6
-------------------------------------------------------------------------------
-- Labels
-------------------------------------------------------------------------------
-- Wording follows the conjugation-table headers in Module:ar-verb (make_table)
-- and the pronoun labels of its simplified ar.wiktionary table (make_table_ar).
local TENSE = {
past = "الماضي",
ind = "المضارع المرفوع",
sub = "المضارع المنصوب",
juss = "المضارع المجزوم",
imp = "الأمر",
}
local VOICE = {
act = "المبني للمعلوم",
pass = "المبني للمجهول",
}
local PERSON = {["1"] = "المتكلم", ["2"] = "المخاطب", ["3"] = "الغائب"}
local NUMBER = {s = "المفرد", d = "المثنى", p = "الجمع"}
local GENDER = {m = "المذكر", f = "المؤنث"}
-- The same vocabulary without the article, for category names. ar.wiktionary
-- already names these categories this way — تصنيف:فعل مبني للمعلوم ماضي مفرد مذكر متكلم
-- has 133 pages — so the composed name lands on the existing category rather
-- than forking the taxonomy.
local PERSON_BARE = {["1"] = "متكلم", ["2"] = "مخاطب", ["3"] = "غائب"}
local NUMBER_BARE = {s = "مفرد", d = "مثنى", p = "جمع"}
local GENDER_BARE = {m = "مذكر", f = "مؤنث"}
local TENSE_BARE = {past = "ماضي", ind = "مضارع", sub = "مضارع", juss = "مضارع", imp = "أمر"}
local VOICE_BARE = {act = "مبني للمعلوم", pass = "مبني للمجهول"}
local PRONOUN = {
["1s"] = "أَنَا", ["1p"] = "نَحْنُ",
["2ms"] = "أَنْتَ", ["2fs"] = "أَنْتِ", ["2d"] = "أَنْتُمَا",
["2mp"] = "أَنْتُمْ", ["2fp"] = "أَنْتُنَّ",
["3ms"] = "هُوَ", ["3fs"] = "هِيَ",
["3md"] = "هُمَا", ["3fd"] = "هُمَا",
["3mp"] = "هُمْ", ["3fp"] = "هُنَّ",
}
-- Slots that carry no conjugated form of their own: the five *_linked duplicates
-- and the staging slots Module:ar-verb uses while picking a default.
local SKIP_SLOTS = {
ap1 = true, ap2 = true, ap3 = true, apcd = true, apan = true,
pp2 = true, vn2 = true,
}
-- Derived nominals. They are lemmas in their own right (اسم فاعل / اسم مفعول /
-- مصدر), not conjugated forms, so this module does not claim them.
local NOMINAL_SLOTS = {ap = true, pp = true, vn = true}
local QUADRILITERAL = {
["فعلل"] = true, ["تفعلل"] = true, ["افعنلل"] = true, ["افعللّ"] = true,
}
local MAINTENANCE_CATEGORY = "صفحات صيغ أفعال بلا مطابقة"
-- Ends the float started by the box, so the conjugation table below it starts on
-- a clean line rather than wrapping around it.
local CLEAR = '<div class="avf-clear"></div>'
-- |قسم= names one section to render instead of the whole page. The bot writes no
-- قسم at all on all ~949,000 pages and gets الكل; the named values exist for the
-- handful of pages a contributor wants to write inside, which they do by
-- replacing the single call with one call per section and typing in the gaps.
--
-- The values *read* like the headings but are not the headings. The module owns
-- the heading text — and «صيغ تشارك الرسم «رميت»» computes half of its own — so
-- keeping them apart means renaming a heading stays a one-line change here
-- instead of an edit to every page that was split.
local SECTIONS = {
["الكل"] = "all",
["صيغة فعل"] = "form",
["صيغة الفعل"] = "form",
["النطق"] = "pron",
["التصريفات"] = "conj",
["الرسم"] = "rasm",
["صيغ تشارك الرسم"] = "rasm",
}
-- Named in the order they appear on the page, for the error message.
local SECTION_NAMES = "الكل، صيغة فعل، النطق، التصريفات، الرسم"
-- The box floats, so whatever comes after it has to be told to start below it.
-- On the whole page that is the conjugation table. On a split page صيغة فعل has
-- to clear on its way out — otherwise the contributor's own section wraps up
-- beside the box — and التصريفات has to clear on its way in, because it can no
-- longer know what precedes it.
local CLEARS = {all = true, form = true, conj = true}
-------------------------------------------------------------------------------
-- Text helpers
-------------------------------------------------------------------------------
-- Tatweel and the invisible direction/joining marks. These survive copy-paste
-- and round-trips through external tools, and MediaWiki keeps them in page
-- titles, so both sides of every comparison are cleaned of them first.
local INVISIBLE = "[" .. u(0x0640) .. u(0x061C) .. u(0x200B) .. "-" .. u(0x200F)
.. u(0x202A) .. "-" .. u(0x202E) .. u(0xFEFF) .. "]"
-- The diacritics that are not typed when searching: the harakat proper, shadda,
-- sukun and the dagger alif. Hamza carriers (أ إ آ ؤ ئ) are distinct letters and
-- are deliberately left alone.
local DIACRITIC = "[" .. u(0x064B) .. "-" .. u(0x0652) .. u(0x0670) .. "]"
local function clean(s)
if not s or s == "" then return "" end
return (mw.ustring.gsub(s, INVISIBLE, ""))
end
-- The consonant skeleton (الرسم) a reader would type into the search box.
local function rasm(s)
return (mw.ustring.gsub(clean(s), DIACRITIC, ""))
end
local function link(target, display)
if not target or target == "" then return "" end
if display and display ~= target then
return "[[" .. target .. "|" .. display .. "]]"
end
return "[[" .. target .. "]]"
end
local function category(name)
return "[[تصنيف:" .. name .. "]]"
end
-- A *link to* a category needs the leading colon; without it the page silently
-- joins the category instead of pointing at it.
local function category_link(name, display)
return "[[:تصنيف:" .. name .. "|" .. (display or name) .. "]]"
end
-- Template calls returned from a module are not expanded — the preprocessor has
-- already run by the time Scribunto hands the text back — so they have to go
-- through the frame.
local function expand(title, args)
local frame = mw.getCurrentFrame()
if not frame then
return ""
end
local ok, result = pcall(frame.expandTemplate, frame, {title = title, args = args})
return ok and result or ""
end
-- "أكل" -> "أ ك ل". ar.wiktionary spells root categories and navboxes from the
-- letters as written (تصنيف:أ ك ل, 15 pages), not from the normalized radicals
-- Module:ar-verb reports, which would give ء ك ل and point at nothing.
local function space_root(root)
local letters = {}
for letter in mw.ustring.gmatch(clean(root), ".") do
if letter ~= " " and letter ~= "_" then letters[#letters + 1] = letter end
end
return table.concat(letters, " ")
end
-------------------------------------------------------------------------------
-- Slots
-------------------------------------------------------------------------------
-- "past_pass_3ms" -> tense "past", voice "pass", person/number "3ms"
-- "imp_2fs" -> tense "imp", voice "act", person/number "2fs"
local function parse_slot(slot)
local tense, rest = mw.ustring.match(slot, "^(%a+)_(.+)$")
if not tense or not TENSE[tense] then return nil end
local voice = "act"
local pn = rest
local stripped = mw.ustring.match(rest, "^pass_(.+)$")
if stripped then
voice = "pass"
pn = stripped
end
local person, gender, number = mw.ustring.match(pn, "^(%d)([mf]?)([sdp])$")
if not person then return nil end
return {
slot = slot, tense = tense, voice = voice, pn = pn,
person = person, gender = gender, number = number,
}
end
-- Every slot that can name a conjugated form, in the order Module:ar-verb
-- declares them.
local function each_form_slot(spec, fn)
for _, slot_accel in ipairs(spec.verb_slots) do
local slot = slot_accel[1]
if not SKIP_SLOTS[slot] and not NOMINAL_SLOTS[slot]
and not mw.ustring.find(slot, "_linked$") then
local forms = spec.forms[slot]
if forms then
for _, form in ipairs(forms) do
if form.form and form.form ~= "" and form.form ~= "?" then
fn(slot, form.form)
end
end
end
end
end
end
-- Name the mood only where the reader would otherwise be misled.
--
-- The simplified conjugation table — what {{تصريف}} shows by default — holds the
-- active الماضي، المضارع and الأمر and nothing else, and its المضارع column is the
-- indicative. A form the reader can find there has nothing on the table to
-- contrast with, so spelling out المرفوع is pedantry: تُحْسِبَانِ is simply المضارع.
--
-- المرفوع is worth saying in the two cases where it is not: the passive, which
-- only the full table (كامل=1) shows, and forms whose moods genuinely part ways —
-- يَكْتُبُوا is منصوب ومجزوم, against يَكْتُبُونَ for the indicative. Where all three moods
-- produce one string (يَكْتُبْنَ) they collapse back to a bare المضارع.
local function tense_phrase(tenses, voice)
if tenses.past then return TENSE.past end
if tenses.imp then return TENSE.imp end
if tenses.ind and (voice == "act" or (tenses.sub and tenses.juss)) then
return "المضارع"
end
local parts = {}
for _, t in ipairs({"ind", "sub", "juss"}) do
if tenses[t] then parts[#parts + 1] = TENSE[t] end
end
if #parts == 0 then return nil end
if #parts == 1 then return parts[1] end
-- "المضارع المنصوب والمجزوم" — say المضارع once, then the moods.
local moods = {}
for i, phrase in ipairs(parts) do
moods[i] = (mw.ustring.gsub(phrase, "^المضارع ", ""))
end
return "المضارع " .. table.concat(moods, " و")
end
-- Group the matched slots into one statement per (voice, person/number).
local function group_matches(matches)
local order, groups = {}, {}
for _, m in ipairs(matches) do
local key = m.voice .. "|" .. m.pn
if not groups[key] then
groups[key] = {voice = m.voice, pn = m.pn, person = m.person,
gender = m.gender, number = m.number, tenses = {}}
order[#order + 1] = key
end
groups[key].tenses[m.tense] = true
end
local out = {}
for _, key in ipairs(order) do out[#out + 1] = groups[key] end
return out
end
local function describe(group, lemma)
local phrase = tense_phrase(group.tenses, group.voice)
if not phrase then return nil end
local parts = {phrase}
-- The imperative is active by definition; saying so adds nothing.
if not group.tenses.imp then
parts[#parts + 1] = VOICE[group.voice]
end
-- ل + المتكلم contracts to للمتكلم, so the first word carries the merged
-- article and the rest keep theirs: "للغائب الجمع المذكر".
local who = {"لل" .. PERSON_BARE[group.person], NUMBER[group.number]}
if group.gender and group.gender ~= "" then
who[#who + 1] = GENDER[group.gender]
end
local pronoun = PRONOUN[group.pn]
local subject = table.concat(who, " ")
if pronoun then
subject = subject .. " (" .. pronoun .. ")"
end
return table.concat(parts, " ") .. "، " .. subject .. "، من الفعل " .. link(lemma) .. "."
end
-------------------------------------------------------------------------------
-- The pattern this form realises (وزن الصيغة)
-------------------------------------------------------------------------------
-- Conjugating ف-ع-ل (ف-ع-ل-ل for quadriliterals) through the same وزن and the
-- same slot names the shape of the form: past_1s of فعَل يفعُل is فَعَلْتُ. That is the
-- category ar.wiktionary already files these pages under, and it is a browse
-- axis nothing else offers — every verb's first-person past in one place.
local pattern_cache = {}
local function pattern_forms(wazn)
if pattern_cache[wazn] == nil then
local model = QUADRILITERAL[wazn] and "فعلل" or "فعل"
local ok, spec = pcall(ar_verb.do_generate_forms,
{["جذر"] = model, ["وزن"] = wazn}, "ar-conj")
pattern_cache[wazn] = ok and spec.forms or false
end
return pattern_cache[wazn] or nil
end
local function pattern_of(wazn, slot)
local forms = pattern_forms(wazn)
if not forms then return nil end
local entry = forms[slot]
if not entry or not entry[1] or entry[1].form == "?" then return nil end
return entry[1].form
end
-------------------------------------------------------------------------------
-- Blocks
-------------------------------------------------------------------------------
-- The box: the form, then the lemma it belongs to, then the morphological
-- coordinates. Laid out and floated like {{صندوق معلومات فعل}} on the lemma page,
-- so the two read as one family.
--
-- The form and the lemma are set at the same size. The form names the page, but
-- the lemma is where the reader is being sent, and a title twice the size of the
-- link under it says the opposite.
local function render_box(verb)
local box = mw.html.create("table"):addClass("ar-verb-form-box")
local head = box:tag("tr"):tag("th"):attr("colspan", 2):addClass("avf-head")
head:tag("div"):addClass("avf-form"):wikitext(verb.pagename)
head:tag("div"):addClass("avf-of"):wikitext("صيغة من الفعل")
head:tag("div"):addClass("avf-lemma"):wikitext(link(verb.lemma))
head:tag("div"):addClass("avf-note")
:wikitext("المعاني والشواهد والمراجع على صفحة الفعل")
local function row(label, value)
if not value or value == "" then return end
local tr = box:tag("tr")
tr:tag("th"):wikitext(label)
tr:tag("td"):wikitext(value)
end
-- A form can belong to the same verb under more than one root analysis
-- (آتَتْ is أ ت و and أ ت ي alike). Only the root differs, so only the root
-- row repeats; everything else would be identical.
local roots = {}
for i, root_display in ipairs(verb.root_displays) do
roots[i] = category_link(root_display)
end
row(#roots > 1 and "الجذور" or "الجذر", table.concat(roots, "، "))
-- The wazn alone. The classification that goes with it (فعل ثلاثي مزيد بحرف)
-- is stated in the conjugation table's own header further down the page.
row("الوزن", verb.wazn)
row("وزن الصيغة", verb.pattern_form and
category_link(verb.pattern_form) or nil)
return tostring(box)
end
-- Vocalizations of this same verb that share the page's consonant skeleton.
-- A reader searching أكلت lands on one of them and needs the others; this is
-- also why ar.wiktionary does not need a disambiguation page per skeleton.
local function collect_siblings(verbs, pagename)
local skeleton = rasm(pagename)
local seen, list = {[pagename] = true}, {}
for _, verb in ipairs(verbs) do
each_form_slot(verb.spec, function(_, form)
local cleaned = clean(form)
if not seen[cleaned] and rasm(cleaned) == skeleton then
seen[cleaned] = true
list[#list + 1] = cleaned
end
end)
end
table.sort(list)
return list, skeleton
end
local function render_siblings(siblings, skeleton)
if #siblings == 0 then return nil end
local links = {}
for i, form in ipairs(siblings) do links[i] = link(form) end
return "=== صيغ تشارك الرسم «" .. skeleton .. "» ===\n"
.. table.concat(links, " · ")
end
-- The dataset sometimes analyses one verb under two roots — آتَى is both أ ت و and
-- أ ت ي on وزن أفعل, and both produce آتَتْ. Left alone that renders two identical
-- boxes and two identical statements. Fold them into one entry whose only
-- plural field is the root; everything else was the same in both.
local function merge_equivalent(verbs)
local order, seen = {}, {}
for _, verb in ipairs(verbs) do
local key = table.concat({verb.lemma, verb.wazn,
table.concat(verb.sentences, "\1")}, "\2")
local first = seen[key]
if first then
for _, root in ipairs(verb.roots) do
first.roots[#first.roots + 1] = root
end
for _, display in ipairs(verb.root_displays) do
first.root_displays[#first.root_displays + 1] = display
end
else
seen[key] = verb
order[#order + 1] = verb
end
end
return order
end
-------------------------------------------------------------------------------
-- Core
-------------------------------------------------------------------------------
-- Work out what the page title is, without producing any wikitext. Kept apart
-- from p.render so the offline tests can assert on the grammar the module
-- derived rather than on the markup it happened to wrap it in.
--
-- args = {verbs = {{root=, wazn=}, ...}, pagename =}
-- returns {pagename=, verbs={...}, siblings={...}, skeleton=, categories={...}}
function p.analyze(args)
local pagename = clean(args.pagename)
local verbs = {}
for _, entry in ipairs(args.verbs or {}) do
local ok, spec = pcall(ar_verb.do_generate_forms,
{["جذر"] = entry.root, ["وزن"] = entry.wazn}, "ar-conj")
if ok and spec and spec.forms then
local matches = {}
each_form_slot(spec, function(slot, form)
if clean(form) == pagename then
local parsed = parse_slot(slot)
if parsed then matches[#matches + 1] = parsed end
end
end)
if #matches > 0 then
local lemma_entry = spec.forms.past_3ms
local groups = group_matches(matches)
local sentences = {}
local lemma = lemma_entry and lemma_entry[1] and lemma_entry[1].form
or entry.root
for i, group in ipairs(groups) do
sentences[i] = describe(group, lemma)
end
verbs[#verbs + 1] = {
root = entry.root,
roots = {entry.root},
wazn = entry.wazn,
lemma = lemma,
root_display = space_root(entry.root),
root_displays = {space_root(entry.root)},
pattern_form = pattern_of(entry.wazn, matches[1].slot),
spec = spec,
pagename = pagename,
slots = matches,
groups = groups,
sentences = sentences,
}
end
end
end
verbs = merge_equivalent(verbs)
local siblings, skeleton = collect_siblings(verbs, pagename)
-- Categories: the parent, then per statement the grammatical category in the
-- order ar.wiktionary already names them (فعل + بناء + زمن + عدد + جنس + شخص),
-- the pattern this form realises, and the root.
local categories, seen_category = {}, {}
local function add_category(name)
if name and name ~= "" and not seen_category[name] then
seen_category[name] = true
categories[#categories + 1] = name
end
end
if #verbs == 0 then
add_category(MAINTENANCE_CATEGORY)
else
add_category("صيغ أفعال عربية")
for _, verb in ipairs(verbs) do
for _, group in ipairs(verb.groups) do
local name = {"فعل"}
if group.tenses.imp then
-- The imperative categories carry neither voice nor person:
-- تصنيف:فعل أمر مفرد مذكر, as {{أ-م-ذ}} files them today.
name[#name + 1] = TENSE_BARE.imp
name[#name + 1] = NUMBER_BARE[group.number]
if group.gender and group.gender ~= "" then
name[#name + 1] = GENDER_BARE[group.gender]
end
else
local tense = group.tenses.past and "past" or "ind"
name[#name + 1] = VOICE_BARE[group.voice]
name[#name + 1] = TENSE_BARE[tense]
name[#name + 1] = NUMBER_BARE[group.number]
if group.gender and group.gender ~= "" then
name[#name + 1] = GENDER_BARE[group.gender]
end
name[#name + 1] = PERSON_BARE[group.person]
end
add_category(table.concat(name, " "))
end
add_category(verb.pattern_form)
for _, root_display in ipairs(verb.root_displays) do
add_category(root_display ~= "" and root_display or nil)
end
end
end
return {
pagename = pagename,
verbs = verbs,
siblings = siblings,
skeleton = skeleton,
categories = categories,
}
end
-- args = {verbs = {{root=, wazn=}, ...}, pagename =, section =}
--
-- `section` is the raw |قسم= value, so the resolution and its error live on the
-- one path both the wiki and the offline tests go through.
function p.render(args)
local requested = clean(mw.text.trim(args.section or ""))
local section = requested == "" and "all" or SECTIONS[requested]
-- A typo has to say so. At ~949,000 pages a |قسم=نطق that silently rendered
-- nothing would leave a blank page nobody would ever find.
if not section then
return '<span class="error">وسيط «قسم» غير معروف: «' .. requested
.. '». القيم المقبولة: ' .. SECTION_NAMES .. '.</span>'
.. category(MAINTENANCE_CATEGORY)
end
local data = p.analyze(args)
local pagename, verbs = data.pagename, data.verbs
if #verbs == 0 then
return "<span class=\"error\">لم تطابق هذه الصفحة أي صيغة من صيغ الفعل المذكور.</span>"
.. category(MAINTENANCE_CATEGORY)
end
local whole = section == "all"
local out = {}
if whole or section == "form" then
out[#out + 1] = "=== صيغة فعل ==="
for _, verb in ipairs(verbs) do
out[#out + 1] = render_box(verb)
for _, sentence in ipairs(verb.sentences) do
out[#out + 1] = "# " .. sentence
end
end
end
if whole or section == "pron" then
local ipa = expand("أصد-عر", {pagename})
if ipa ~= "" then
out[#out + 1] = "=== النطق ==="
out[#out + 1] = "* " .. ipa
end
end
-- Lemma pages write {{-}} at this point; same effect, one fewer template.
if CLEARS[section] then
out[#out + 1] = CLEAR
end
if whole or section == "conj" then
out[#out + 1] = "=== التصريفات ==="
for _, verb in ipairs(verbs) do
-- Full table, open, with the cells holding this page marked. The
-- full table is what makes the marking worth anything: it is the only
-- one that shows the passive and the moods, and a reader who arrived
-- on a passive form needs to see where it sits.
out[#out + 1] = expand("تصريف", {
["جذر"] = verb.root,
["وزن"] = verb.wazn,
["كامل"] = "1",
["موسع"] = "1",
["بؤرة"] = pagename,
})
end
end
if whole or section == "rasm" then
local siblings = render_siblings(data.siblings, data.skeleton)
if siblings then out[#out + 1] = siblings end
end
-- Categories describe the page, not a section of it, so only the two renders
-- that carry the form's identity emit them. Otherwise a stray
-- {{صيغة فعل|قسم=النطق}} on an unrelated page would file it under
-- تصنيف:فعل مبني للمجهول ماضي… on the strength of a pronunciation line.
local categories = {}
if whole or section == "form" then
for i, name in ipairs(data.categories) do categories[i] = category(name) end
end
return table.concat(out, "\n") .. "\n" .. table.concat(categories, "")
end
-------------------------------------------------------------------------------
-- Entry point
-------------------------------------------------------------------------------
function p.main(frame)
local args = frame:getParent() and frame:getParent().args or frame.args
local verbs = {}
for i = 1, MAX_VERBS do
local suffix = i == 1 and "" or tostring(i)
local root = args["جذر" .. suffix]
local wazn = args["وزن" .. suffix]
if root and root ~= "" and wazn and wazn ~= "" then
verbs[#verbs + 1] = {
root = mw.text.trim(root),
wazn = mw.text.trim(wazn),
}
end
end
local pagename = args["صفحة"]
if not pagename or pagename == "" then
pagename = mw.title.getCurrentTitle().text
end
return p.render({
verbs = verbs,
pagename = pagename,
section = args["قسم"],
})
end
return p